TypeScript-Skeleton, ein MCP-Server in 40 Zeilen
Der minimale Code. Nicht die beste Architektur, aber der klarste Einstiegspunkt.
Was Du installierst
mkdir mein-mcp-server && cd mein-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript tsx @types/node
@modelcontextprotocol/sdkist die offizielle Anthropic-Library.zodist für Input-Validation (Pflicht, nicht optional).tsxlässt Dich TypeScript direkt ausführen ohne Build-Step.
Der minimale Server
Datei src/index.ts:
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
const server = new Server(
{ name: "mein-mcp-server", version: "0.1.0" },
{ capabilities: { tools: {} } }
);
const EchoSchema = z.object({
message: z.string(),
});
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "echo",
description: "Gibt die uebergebene Nachricht zurück.",
inputSchema: {
type: "object",
properties: { message: { type: "string" } },
required: ["message"],
},
},
],
}));
server.setRequestHandler(CallToolRequestSchema, async (req) => {
if (req.params.name === "echo") {
const { message } = EchoSchema.parse(req.params.arguments);
return { content: [{ type: "text", text: `Echo: ${message}` }] };
}
throw new Error(`Unknown tool: ${req.params.name}`);
});
const transport = new StdioServerTransport();
await server.connect(transport);
Das war's. 30 Zeilen Code, ein voll-funktionsfähiger MCP-Server.
Spielen, im Editor anschauen
Lies den Code im Editor unten durch. Die zwei Request-Handler sind der Kern: ListToolsRequestSchema antwortet mit der Tool-Liste, CallToolRequestSchema führt einen Tool-Call aus. Klick Dich durch src/index.ts und package.json und fühl den Aufbau.
Editieren ist erlaubt (lokale Experimente). Ausführen geht hier nicht. MCP-Server brauchen Node + stdio, das läuft nicht im Browser. Wenn Du den Code laufen lassen willst, folge den Schritten weiter unten lokal.
Was der Code tut
- Importiert den SDK.
- Erstellt einen Server mit Name + Version.
- Meldet dem Client: "Ich habe ein Tool namens
echo." - Wenn der Client
echoaufruft, validiert den Input mit Zod und gibt ein Echo zurück. - Verbindet sich über stdin/stdout mit dem Client.
Das ist die Basis. Jeder MCP-Server folgt diesem Muster, egal wie komplex er wird.
Testen
In package.json diesen Script eintragen:
"scripts": { "dev": "tsx src/index.ts" }
Dann Claude Code konfigurieren:
claude mcp add mein-server -s user -- npx -y tsx /pfad/zum/projekt/src/index.ts
Claude Code neu starten, /mcp eingeben, Du solltest "mein-server" sehen. Tippen:
Benutze mein-server's echo-Tool mit der Nachricht "Hallo"
Claude ruft echo auf, gibt "Echo: Hallo" zurück.
Die zwei Fallen für Anfänger
1. console.log als Debug. Geht NICHT bei stdio-Servern. console.log landet auf stdout, das liest der Client als Protokoll-Nachricht, der Server crashes. Stattdessen: console.error(...) nutzen, das geht nach stderr und stört nicht.
2. Fehlende Input-Validation. Wenn der Client was falsches schickt und Du validierst nicht, crashed Dein Server. Der Agent sieht den Crash als "Tool failed", macht komische Sachen. Immer Zod-Schemas. Immer.
Was jetzt fehlt
Das ist das Skeleton. Für einen echten Server brauchst Du:
- Mehr Tools (statt
echowas Nuetzliches) - Externe API-Calls (mit Error-Handling, Retry, Rate-Limit)
- Auth falls nötig (API-Keys, OAuth)
- Tests
- Logging
- Deployment (npm publish oder Docker)
Das kommt in den nächsten Lektionen. Das Skeleton ist der Bauch, nicht der Sprint.
Nächste Lektion
Tool-Design. Wie Du gute Tools schreibst die vom Modell richtig benutzt werden. Das ist der Unterschied zwischen "mein Server existiert" und "mein Server wird genutzt".