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.
npm install @modelcontextprotocol/sdk
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);
pip install mcp
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)
Gute Tool-Definitionen sind entscheidend — das LLM entscheidet basierend auf Name und Description, wann welches Tool genutzt wird:
| Eigenschaft | Beschreibung | Best Practice |
|---|---|---|
| name | Eindeutiger Bezeichner | Snake_case, beschreibend |
| description | Was das Tool tut | Klar, präzise, wann nutzen |
| inputSchema | JSON Schema für Parameter | Alle Felder dokumentieren |
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"
}]
})
);
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.`
}
}]
})
);
Kommunikation über Standard Input/Output. Ideal für lokale Server:
{
"mcpServers": {
"my-tools": {
"command": "node",
"args": ["./build/index.js"]
}
}
}
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.