MCP · Lektion 02 · Der Wire

JSON-RPC & der initialize-Handshake

In Lektion 1 war Architektur ein Diagramm. Jetzt schauen wir auf die echten Bytes, die zwischen Client und Server fließen — damit du im Workshop sagen kannst „so sieht das auf dem Draht aus“ und nicht nur „irgendwie reden die“.

Dein Gewinn: Du kannst die drei Eröffnungs-Nachrichten jeder MCP-Verbindung benennen und zeichnen — initializeinitialize resultnotifications/initialized — und erklären, warum Capability Negotiation das Herz davon ist.

1. MCP ist JSON-RPC 2.0 — nicht mehr, nicht weniger

Jede MCP-Nachricht ist eine JSON-RPC-2.0-Message, UTF-8 kodiert.1 Drei Sorten, mehr gibt es nicht:

Das ist die ganze Grammatik. „MCP lernen“ heißt zu großen Teilen: lernen, welche method-Namen es gibt (tools/list, tools/call, resources/read …) und welche Parameter sie tragen.

2. Der Handshake: drei Nachrichten, immer zuerst

Die Initialisierung MUSS die allererste Interaktion sein.1 Der Client schickt initialize mit seiner Protokoll-Version, seinen Capabilities und clientInfo:

// Client → Server
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2025-06-18",
    "capabilities": { "roots": { "listChanged": true }, "sampling": {} },
    "clientInfo": { "name": "ExampleClient", "version": "1.0.0" }
  }
}

Der Server antwortet mit seinen Capabilities und serverInfo — hier deklariert er, dass er Tools/Resources/Prompts kann:

// Server → Client
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2025-06-18",
    "capabilities": {
      "tools":     { "listChanged": true },
      "resources": { "subscribe": true, "listChanged": true },
      "prompts":   { "listChanged": true }
    },
    "serverInfo": { "name": "ExampleServer", "version": "1.0.0" },
    "instructions": "Optional instructions for the client"
  }
}

Erst dann meldet der Client „bereit“ — als Notification (keine id):

// Client → Server
{ "jsonrpc": "2.0", "method": "notifications/initialized" }

Ab hier ist die Session offen und „operational“. Genau diese drei Nachrichten laufen gerade auch zwischen Claude Code und deinem forgejo-Server.1

3. Warum der ganze Tanz? Capability Negotiation

Der Sinn des Handshakes: beide Seiten sagen vorher, was sie können. Ein Server, der tools nicht deklariert, wird nie nach Tools gefragt. Ein Client, der sampling nicht kann, bekommt keine Sampling-Anfragen. So bleibt das Protokoll erweiterbar: neue Features werden über neue Capabilities eingeführt, ohne alte Implementierungen zu brechen.1

Workshop-Bild: Der Handshake ist wie zwei Geräte, die per USB-C aushandeln, ob sie Strom, Video und Daten können — bevor irgendwas fließt.

Auch die Versionsverhandlung steckt hier: Der Client schickt die neueste Version, die er kann; passt sie dem Server nicht, antwortet der Server mit einer Version, die er kann. Über HTTP wandert die ausgehandelte Version danach in jedem Request als MCP-Protocol-Version-Header mit.1

4. Mini-Check (aus dem Kopf!)

Welche der drei Eröffnungs-Nachrichten hat keine id?
Wozu dient die Capability Negotiation im Kern?
Ein Server bietet Tools an. Wo sagt er das?

5. Primärquelle

📖 MCP Specification — Lifecycle. Enthält genau diese JSON-Beispiele plus die MUST/SHOULD-Regeln. Lies Initialization und Capability Negotiation.

Tipp: Frag mich „zeig mir den echten Handshake mit forgejo“ — wir können den Server in einer Konsole mit dem MCP Inspector starten und die Roh-Nachrichten ansehen. Sag „weiter“ für Lektion 3 (die drei Primitive im Detail: wann Tool, wann Resource, wann Prompt).

1 — MCP Specification, Lifecycle & Transports (2025-06-18): …/basic/lifecycle