Lässt es sich mit cremefarbenem Papier, einer Tönung, mehr Kontrast oder größerer Schrift besser lesen? Wählen Sie Ihre Farben und Ihre Schrift, einmalig für alle Seiten.

Kostenlose Lektion

Einen MCP-Server testen

Nach dieser Lektion: Ich kann einen Server mit Skript-Clients testen.

Lektion, 80 Min.12 BefehleLab für KI-Coding-Agenten: mit opencode Code erklären und reparieren lassen und die Arbeit prüfen

Einen MCP-Server mit Skript-Clients testen

Ein Skript-Client ist ein kurzes Programm, das eine feste Liste von Nachrichten an einen Server sendet und jede Antwort prüft. Er wird nie müde und lässt nie einen Schritt aus.

Das ist wichtig, weil sich ein Server leicht aus Versehen kaputtmachen lässt. Eine Änderung von einem Zeichen kann eine Antwort verändern, und niemand merkt es, bis ein echter Client scheitert.

Am Ende dieser Lektion

Notiz

Ich kann einen Server mit Skript-Clients testen.

Schlüsselwörter

BegriffWas er bedeutetBeispiel
Drehbucheine Liste von Nachrichten, jede mit der erwarteten Antwort
Erwartetdie Antwort, die der Test will
Erhaltendie Antwort, die der Server wirklich gesendet hat
Unterschieddie erste Stelle, an der die beiden nicht übereinstimmen
Platzhaltereine Marke, die zu jedem Wert passt<beliebig>

Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.

Ein Drehbuch ordnet jeder Nachricht ihre Antwort zu

Ein Drehbuch ist eine Liste von Schritten. Jeder Schritt hat eine Nachricht zum Senden und die erwartete Antwort. Ein Schritt mit einer Benachrichtigung erwartet keine Antwort.

Manche Werte ändern sich, etwa eine Versionsnummer. Der Test kennzeichnet sie mit einem Platzhalter, damit eine Änderung dort kein Fehler ist. Alles andere muss genau passen.

Beispiel: ein Drehbuch für ein Rechenwerkzeug

SchrittGesendetErwartete Antwort
1initializeDie verlangte Version, tools gemeldet, beliebige Serverversion
2Die Bereit-BenachrichtigungKeine Antwort
3tools/listEin Werkzeug namens addieren
4addieren 2 und 3Der Text 5, isError false
5addieren "2" und 3Ein Werkzeugfehler, isError true

Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.

Den ersten Unterschied melden, mit seiner Stelle

Ein Test, der nur "gescheitert" sagt, verschwendet Zeit. Ein guter sagt, wo. Der Pfad result.content[0].text zeigt auf einen Wert in der Antwort.

Der Prüfer geht die erwartete Antwort in fester Reihenfolge durch und hält beim ersten Unterschied an. Er sagt, was er erwartet hat und was er bekam.

Ein häufiger Fehler

Warnung

Viele lassen einen Test nur gegen einen Server laufen, der funktioniert. Ein Test, der nie gescheitert ist, hat nie gezeigt, dass er etwas fängt. Machen Sie den Server absichtlich kaputt und prüfen Sie, dass der Test scheitert. Machen Sie die Änderung dann rückgängig.

Modell-Helfer schreiben

Befehl
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

Befehl
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function senden(m) { process.stdout.write(JSON.stringify(Object.assign({ jsonrpc: \"2.0\" }, m)) + \"\n\"); }' 'const SCHEMA = { type: \"object\", properties: { a: { type: \"integer\" }, b: { type: \"integer\" } }, required: [\"a\", \"b\"] };' 'function worte(text, schlecht) { return { content: [{ type: \"text\", text: text }], isError: schlecht }; }' 'function verarbeiten(nachricht) {' '  if (nachricht.id === undefined) return;' '  const p = nachricht.params || {};' '  const argumente = p.arguments || {};' '  if (nachricht.method === \"initialize\") return senden({ id: nachricht.id, result: { protocolVersion: p.protocolVersion, capabilities: { tools: {} }, serverInfo: { name: \"rechner\", version: \"1.0\" } } });' > server6.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

Befehl
docker exec -w /home/node/project agent sh -c "printf '%s\n' '  if (nachricht.method === \"tools/list\") return senden({ id: nachricht.id, result: { tools: [{ name: \"addieren\", description: \"Zwei ganze Zahlen addieren\", inputSchema: SCHEMA }] } });' '  if (nachricht.method === \"tools/call\" && p.name === \"addieren\") {' '    if (Number.isInteger(argumente.a) && Number.isInteger(argumente.b)) return senden({ id: nachricht.id, result: worte(String(argumente.a + argumente.b), false) });' '    return senden({ id: nachricht.id, result: worte(\"a und b müssen ganze Zahlen sein\", true) });' '  }' '  if (nachricht.method === \"tools/call\") return senden({ id: nachricht.id, error: { code: -32602, message: \"Unbekanntes Werkzeug: \" + p.name } });' '  senden({ id: nachricht.id, error: { code: -32601, message: \"Method not found: \" + nachricht.method } });' '}' >> server6.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

Befehl
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'require(\"readline\").createInterface({ input: process.stdin }).on(\"line\", function (zeile) { verarbeiten(JSON.parse(zeile)); });' >> server6.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.

Prüfer schreiben, Teil 1

Befehl
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { fragen } = require(\"./model\");' 'const server = require(\"child_process\").spawn(\"node\", [process.argv[2] || \"server6.js\"]);' 'const BELIEBIG = \"<beliebig>\";' 'const eins = function (id, methode, params) { return { jsonrpc: \"2.0\", id: id, method: methode, params: params }; };' 'const addieren = function (id, argumente) { return eins(id, \"tools/call\", { name: \"addieren\", arguments: argumente }); };' 'const ok = function (id, ergebnis) { return { jsonrpc: \"2.0\", id: id, result: ergebnis }; };' 'const text = function (t, schlecht) { return { content: [{ type: \"text\", text: t }], isError: schlecht }; };' 'const schritte = [' '  [eins(1, \"initialize\", { protocolVersion: \"2025-03-26\" }), ok(1, { protocolVersion: \"2025-03-26\", capabilities: { tools: {} }, serverInfo: { name: \"rechner\", version: BELIEBIG } })],' > pruefen6.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.

Prüfer schreiben, Teil 2

Befehl
docker exec -w /home/node/project agent sh -c "printf '%s\n' '  [{ jsonrpc: \"2.0\", method: \"notifications/initialized\" }, null],' '  [eins(2, \"tools/list\"), ok(2, { tools: [{ name: \"addieren\", description: BELIEBIG, inputSchema: BELIEBIG }] })],' '  [addieren(3, { a: 2, b: 3 }), ok(3, text(\"5\", false))],' '  [addieren(4, { a: \"2\", b: 3 }), ok(4, text(\"a und b müssen ganze Zahlen sein\", true))],' '  [eins(5, \"tools/call\", { name: \"nichts\" }), { jsonrpc: \"2.0\", id: 5, error: { code: -32602, message: \"Unbekanntes Werkzeug: nichts\" } }],' '];' 'function istObjekt(x) { return Boolean(x) && typeof x === \"object\"; }' 'function pfadMit(pfad, k, liste) { return liste ? pfad + \"[\" + k + \"]\" : (pfad ? pfad + \".\" + k : k); }' 'function unterschied(erwartet, erhalten, pfad) {' '  if (erwartet === BELIEBIG) return null;' '  const beide = istObjekt(erwartet) && istObjekt(erhalten) && Array.isArray(erwartet) === Array.isArray(erhalten);' >> pruefen6.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.

Prüfer schreiben, Teil 3

Befehl
docker exec -w /home/node/project agent sh -c "printf '%s\n' '  const gesagt = \" erwartet \" + JSON.stringify(erwartet) + \", erhalten \" + JSON.stringify(erhalten);' '  if (beide === false) return JSON.stringify(erwartet) === JSON.stringify(erhalten) ? null : (pfad || \"(Wurzel)\") + \":\" + gesagt;' '  const liste = Array.isArray(erwartet);' '  if (liste && (erwartet.length === erhalten.length) === false) return (pfad || \"(Wurzel)\") + \": erwartet \" + erwartet.length + \" Elemente, erhalten \" + erhalten.length;' '  const schluessel = Object.keys(erwartet).sort();' '  for (const k of schluessel) {' '    if ((k in erhalten) === false) return pfadMit(pfad, k, liste) + \": fehlt\";' '    const d = unterschied(erwartet[k], erhalten[k], pfadMit(pfad, k, liste));' '    if (d) return d;' '  }' '  const zuviel = Object.keys(erhalten).sort().filter(function (k) { return schluessel.indexOf(k) < 0; });' >> pruefen6.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.

Prüfer schreiben, Teil 4

Befehl
docker exec -w /home/node/project agent sh -c "printf '%s\n' '  return zuviel.length > 0 ? pfadMit(pfad, zuviel[0], liste) + \": unerwartet\" : null;' '}' 'const zeilen = [];' 'const wartend = [];' 'require(\"readline\").createInterface({ input: server.stdout }).on(\"line\", function (l) {' '  if (wartend.length > 0) wartend.shift()(JSON.parse(l)); else zeilen.push(JSON.parse(l));' '});' 'function naechste() { return zeilen.length > 0 ? Promise.resolve(zeilen.shift()) : new Promise(function (r) { wartend.push(r); }); }' 'async function main() {' '  let bestanden = 0, gesamt = 0, erster = null;' '  for (let i = 0; i < schritte.length; i++) {' '    server.stdin.write(JSON.stringify(schritte[i][0]) + \"\n\");' '    if (schritte[i][1] === null) continue;' '    gesamt += 1;' '    const d = unterschied(schritte[i][1], await naechste(), \"\");' '    console.log((d ? \"FEHLER Schritt \" + (i + 1) + \" bei \" + d : \"BESTANDEN Schritt \" + (i + 1)));' >> pruefen6.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.

Prüfer schreiben, Teil 5

Befehl
docker exec -w /home/node/project agent sh -c "printf '%s\n' '    if (d === null) bestanden += 1; else if (erster === null) erster = d;' '  }' '  server.stdin.end();' '  console.log(\"bestanden \" + bestanden + \" von \" + gesamt);' '  if (erster === null) return;' '  const frage = \"Ein Test meines Servers ist mit diesem Unterschied gescheitert. Was ist die wahrscheinliche Ursache? Antworten Sie in einem Satz. \" + erster;' '  const antwort = await fragen([{ role: \"user\", content: frage }], \"Der Server hat einen anderen Text geliefert als der Test erwartet hat.\", 60);' '  console.log(\"Modell: \" + antwort.text);' '}' 'main();' >> pruefen6.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.

Prüfer auf dem guten Server ausführen

Befehl
docker exec -w /home/node/project agent node pruefen6.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.

Eine Kopie mit schlechter Änderung machen

Befehl
docker exec -w /home/node/project agent sh -c "sed 's/argumente.a + /argumente.a - /' server6.js > schlecht6.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.

Prüfer auf der schlechten Kopie ausführen

Befehl
docker exec -w /home/node/project agent node pruefen6.js schlecht6.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.

Die zwei Runs nebeneinander

RunGeprüfte SchritteErgebnis
Guter Server1, 3, 4, 5 und 6bestanden 5 von 5
Schlechte Kopie1, 3, 4, 5 und 6bestanden 4 von 5
Schlechte Kopie, Schritt 4addieren 2 und 3result.content[0].text: erwartet "5", erhalten "-1"

Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.

Über dieses Lab und seinen Quellcode: Lab für KI-Coding-Agenten: mit opencode Code erklären und reparieren lassen und die Arbeit prüfen

Sie unterrichten eine Gruppe? Weisen Sie dieses Modul Ihren Studierenden zu, mit Handbuch für Lehrende, automatisch geprüften Aufgaben und einem Lab für jede Person.