Lektion 2 von 6·11 Min Lesezeit

MCP Server bauen

Ein MCP Server stellt Tools, Resources und Prompts über das standardisierte Protokoll bereit. In dieser Lektion bauen Sie einen eigenen MCP Server — mit TypeScript und Python.

TypeScript SDK

Installation

npm install @modelcontextprotocol/sdk

Minimaler MCP Server

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

const server = new McpServer({
  name: "my-tools",
  version: "1.0.0"
});

// Tool definieren
server.tool(
  "get_weather",
  "Aktuelles Wetter für eine Stadt abrufen",
  { city: z.string().describe("Name der Stadt") },
  async ({ city }) => {
    const data = await fetchWeather(city);
    return {
      content: [{ type: "text", text: JSON.stringify(data) }]
    };
  }
);

// Server starten
const transport = new StdioServerTransport();
await server.connect(transport);

Python SDK

Installation

pip install mcp

Python MCP Server

from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent

server = Server("my-tools")

@server.list_tools()
async def list_tools() -> list[Tool]:
    return [
        Tool(
            name="get_weather",
            description="Aktuelles Wetter für eine Stadt",
            inputSchema={
                "type": "object",
                "properties": {
                    "city": {"type": "string", "description": "Name der Stadt"}
                },
                "required": ["city"]
            }
        )
    ]

@server.call_tool()
async def call_tool(name: str, arguments: dict):
    if name == "get_weather":
        data = await fetch_weather(arguments["city"])
        return [TextContent(type="text", text=str(data))]

async def main():
    async with stdio_server() as (read, write):
        await server.run(read, write)

Tool-Definitionen

Gute Tool-Definitionen sind entscheidend — das LLM entscheidet basierend auf Name und Description, wann welches Tool genutzt wird:

EigenschaftBeschreibungBest Practice
nameEindeutiger BezeichnerSnake_case, beschreibend
descriptionWas das Tool tutKlar, präzise, wann nutzen
inputSchemaJSON Schema für ParameterAlle Felder dokumentieren

Resource Handling

Resources sind Daten, die das Modell lesen kann:

server.resource(
  "config://app",
  "Aktuelle Anwendungskonfiguration",
  async () => ({
    contents: [{
      uri: "config://app",
      text: JSON.stringify(appConfig),
      mimeType: "application/json"
    }]
  })
);

Prompt Templates

Vordefinierte Prompts, die der Client anfordern kann:

server.prompt(
  "analyze_data",
  "Daten analysieren und Insights generieren",
  { dataset: z.string().describe("Name des Datasets") },
  async ({ dataset }) => ({
    messages: [{
      role: "user",
      content: {
        type: "text",
        text: `Analysiere das Dataset "${dataset}" und identifiziere die wichtigsten Trends.`
      }
    }]
  })
);

Transport Layers

stdio (Standard)

Kommunikation über Standard Input/Output. Ideal für lokale Server:

{
  "mcpServers": {
    "my-tools": {
      "command": "node",
      "args": ["./build/index.js"]
    }
  }
}

SSE (Server-Sent Events)

Für Remote-Server über HTTP:

import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
// HTTP-Server mit SSE-Endpoint

Praxis-Tipp: Starten Sie mit stdio — es ist der einfachste Transport. Wechseln Sie zu SSE, wenn Ihr Server remote läuft. Testen Sie Ihren Server mit dem MCP Inspector, bevor Sie ihn in einer Anwendung nutzen.