Wie ein MCP-Server initialize beantwortet
Der Handshake ist der erste Austausch zwischen einem MCP-Client und einem Server. Der Client sendet initialize mit der Version, die er will. Der Server antwortet mit der Version, die er nutzt, und mit dem, was er kann.
Das ist wichtig, weil ein Client nur nutzt, was der Server als sein Angebot nennt. Ein Server, der zu viel behauptet, bringt den Client zum Scheitern.
Am Ende dieser Lektion
Notiz
Ich kann eine initialize-Anfrage beantworten und sagen, was mein Server kann.
Schlüsselwörter
| Begriff | Was er bedeutet | Beispiel |
|---|---|---|
| Handshake | der erste Austausch, der eine Sitzung aufbaut | |
| Protokollversion | das Datum, das eine Fassung der Regeln benennt | 2024-11-05 |
| Fähigkeit | eine Gruppe von Funktionen, die der Server zu haben angibt | tools |
| Zustand | was sich der Server merkt: neu, wartend oder bereit | |
| Aushandeln | sich auf eine Version einigen, die beide Seiten sprechen |
Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.
Der Server wählt eine Version, die er kennt
Der Client nennt eine Version. Der Server führt eine Liste der Versionen, die er spricht. Steht die verlangte auf der Liste, antwortet der Server mit ihr.
Wenn nicht, lehnt dieser Server mit dem Code -32602 ab. Er legt die verlangte Version und seine eigene Liste in den Fehler, damit der Client es noch einmal versuchen kann.
Beispiel: drei Clients fragen an
| Client verlangt | Server spricht | Server antwortet |
|---|---|---|
| 2024-11-05 | 2025-03-26, 2024-11-05 | Ein Ergebnis mit 2024-11-05 |
| 2025-03-26 | 2025-03-26, 2024-11-05 | Ein Ergebnis mit 2025-03-26 |
| 1999-01-01 | 2025-03-26, 2024-11-05 | Fehler -32602 mit beiden Listen |
Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.
Ein Server meldet nur, was er tut
Das Ergebnis von initialize enthält capabilities, die Fähigkeiten. Jede ist eine Gruppe von Methoden, die der Server hat: tools, resources, prompts oder logging.
Eine Fähigkeit kann Schalter tragen. listChanged sagt, dass der Server dem Client mitteilt, wenn sich seine Liste ändert. Dieser Server tut das nie, also steht dort false. Eine Methode aus einer nicht gemeldeten Gruppe wird mit -32601 beantwortet.
Ein häufiger Fehler
Warnung
Viele melden vorsichtshalber jede Fähigkeit. Der Client ruft dann Methoden auf, die der Server nicht hat. Melden Sie nur, was Sie geschrieben und getestet haben. Eine Angabe ist ein Versprechen.
Modell-Helfer schreiben
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const basis = process.env.AI_URL || \"http://ai:4000\";' 'async function fragen(nachrichten, probe, max) {' ' try {' ' const res = await fetch(basis + \"/v1/chat/completions\", { method: \"POST\", headers: { \"content-type\": \"application/json\", authorization: \"Bearer lab\" }, body: JSON.stringify({ model: \"lab\", messages: nachrichten, max_tokens: max || 80 }), signal: AbortSignal.timeout(30000) });' ' if (res.ok === false) throw new Error(\"Status \" + res.status);' ' const daten = await res.json();' ' return { text: daten.choices[0].message.content, aufgezeichnet: false };' ' } catch (fehler) {' ' console.log(\"Aufgezeichnete Probe: Kein Modell hat geantwortet (\" + fehler.message + \").\");' ' return { text: probe, aufgezeichnet: true };' ' }' '}' 'module.exports = { fragen };' > model.js"Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Server schreiben, Teil 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const UNTERSTUETZT = [\"2025-03-26\", \"2024-11-05\"];' 'let zustand = \"neu\";' 'function senden(m) { process.stdout.write(JSON.stringify(Object.assign({ jsonrpc: \"2.0\" }, m)) + \"\n\"); }' 'function antworten(id, ergebnis) { senden({ id: id, result: ergebnis }); }' 'function fehler(id, code, text, daten) { senden({ id: id, error: { code: code, message: text, data: daten } }); }' 'function initialisieren(nachricht) {' ' const gewuenscht = (nachricht.params || {}).protocolVersion;' ' if (UNTERSTUETZT.indexOf(gewuenscht) < 0) return fehler(nachricht.id, -32602, \"Protokollversion nicht unterstützt\", { requested: gewuenscht, supported: UNTERSTUETZT });' ' zustand = \"wartend\";' ' antworten(nachricht.id, { protocolVersion: gewuenscht, capabilities: { tools: { listChanged: false } }, serverInfo: { name: \"mensa\", version: \"0.2\" } });' '}' 'function verarbeiten(nachricht) {' > server2.js"Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Server schreiben, Teil 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' ' if (nachricht.id === undefined) {' ' if (nachricht.method === \"notifications/initialized\" && zustand === \"wartend\") zustand = \"bereit\";' ' return;' ' }' ' if (nachricht.method === \"ping\") return antworten(nachricht.id, {});' ' if (nachricht.method === \"initialize\") return zustand === \"neu\" ? initialisieren(nachricht) : fehler(nachricht.id, -32600, \"Schon initialisiert\");' ' if (zustand === \"neu\" || zustand === \"wartend\") return fehler(nachricht.id, -32600, \"Nicht initialisiert\");' ' if (nachricht.method === \"tools/list\") return antworten(nachricht.id, { tools: [] });' ' fehler(nachricht.id, -32601, \"Method not found: \" + nachricht.method);' '}' 'require(\"readline\").createInterface({ input: process.stdin }).on(\"line\", function (zeile) {' ' let nachricht;' >> server2.js"Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Server schreiben, Teil 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' ' try { nachricht = JSON.parse(zeile); } catch (problem) { return fehler(null, -32700, \"Parse error\"); }' ' verarbeiten(nachricht);' '});' >> server2.js"Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Skript-Client schreiben, Teil 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { fragen } = require(\"./model\");' 'const server = require(\"child_process\").spawn(\"node\", [\"server2.js\"]);' 'const start = function (id, version) { return { id: id, method: \"initialize\", params: { protocolVersion: version, capabilities: {}, clientInfo: { name: \"demo\", version: \"0.1\" } } }; };' 'const skript = [' ' { id: 1, method: \"tools/list\" },' ' start(2, \"1999-01-01\"),' ' start(3, \"2024-11-05\"),' ' { id: 4, method: \"tools/list\" },' ' { method: \"notifications/initialized\" },' ' { id: 5, method: \"tools/list\" },' ' { id: 6, method: \"resources/list\" },' '];' 'const gesehen = [];' 'require(\"readline\").createInterface({ input: server.stdout }).on(\"line\", function (zeile) {' ' console.log(\"<- \" + zeile);' ' gesehen.push(JSON.parse(zeile));' ' if (gesehen.length === 6) server.stdin.end();' '});' 'skript.forEach(function (m) {' > client2.js"Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Skript-Client schreiben, Teil 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' ' const ganz = Object.assign({ jsonrpc: \"2.0\" }, m);' ' console.log(\"-> \" + JSON.stringify(ganz));' ' server.stdin.write(JSON.stringify(ganz) + \"\n\");' '});' 'server.on(\"close\", async function () {' ' const faehig = JSON.stringify(gesehen[2].result.capabilities);' ' const frage = \"Sagen Sie in einem Satz, was ein Server mit diesen Fähigkeiten kann: \" + faehig;' ' const antwort = await fragen([{ role: \"user\", content: frage }], \"Er kann Werkzeuge auflisten und sagt nicht, dass sich seine Liste ändern wird.\", 60);' ' console.log(\"Modell: \" + antwort.text);' '});' >> client2.js"Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Client gegen den Server ausführen
docker exec -w /home/node/project agent node client2.jsHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Was das Skript sendet
Sieben Nachrichten gehen in fester Reihenfolge hinaus. Erst eine Liste zu früh und eine schlechte Version. Dann eine gute Version und eine zweite Liste zu früh.
Dann die Bereit-Benachrichtigung, eine Liste, die funktioniert, und eine Methode, die der Server nie gemeldet hat.
Ein kurzer Modellaufruf am Ende formuliert, was die Fähigkeiten bedeuten. Ohne Modell druckt er eine Aufgezeichnete Probe.
Die sechs Antworten
| Anfrage | Antwort |
|---|---|
| 1 tools/list | Fehler -32600, Nicht initialisiert |
| 2 initialize, 1999-01-01 | Fehler -32602, mit den zwei unterstützten Versionen |
| 3 initialize, 2024-11-05 | Ein Ergebnis mit gemeldeten tools und dem Servernamen |
| 4 tools/list | Fehler -32600, noch nicht bereit |
| 5 tools/list | Ein Ergebnis mit leerer Liste |
| 6 resources/list | Fehler -32601, nie gemeldet |
Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.