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

MCP-Werkzeuge mit Eingabeprüfung

Nach dieser Lektion: Ich kann ein Werkzeug anbieten, dessen Eingabe vor dem Lauf geprüft wird.

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

Wie ein MCP-Werkzeug seine Eingabe prüft

Ein MCP-Werkzeug hat einen Namen, eine Beschreibung und ein Eingabeschema. Das Schema sagt, wie seine Argumente aussehen müssen. Der Server prüft die Argumente dagegen, bevor irgendetwas läuft.

Das ist wichtig, weil ein Modell die Argumente füllt und manchmal falsch liegt. Eine gute Ablehnung sagt ihm, was es ändern soll.

Am Ende dieser Lektion

Notiz

Ich kann ein Werkzeug anbieten, dessen Eingabe vor dem Lauf geprüft wird.

Schlüsselwörter

BegriffWas er bedeutetBeispiel
Schemaeine Beschreibung, wie die Eingabe aussehen muss
Argumentein benannter Wert in einem Werkzeugaufruf
Prüfendie Eingabe kontrollieren, bevor man sie nutzt
Aufzählungeine feste Liste erlaubter Wertenudeln, wrap, salat
Werkzeugfehlerein Ergebnis mit isError true, das sagt, was schiefging

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

Ein Schema nennt die Regeln für jedes Argument

Jedes Argument hat einen Typ. Es kann auch eine Aufzählung, einen Zahlenbereich oder eine größte Länge haben. Das Schema nennt, welche Argumente nötig sind, und kann sagen, dass keine anderen erlaubt sind.

Der Server prüft jede Regel in fester Reihenfolge und sammelt alle Probleme, nicht nur das erste.

Beispiel: das Werkzeug essen_bestellen

ArgumentRegelBestehtScheitert
gerichtNötig. Eines von nudeln, wrap, salatwrapsuppe
mengeNötig. Eine ganze Zahl, 1 bis 529, oder "2" als Text
notizOptionaler Text, höchstens 20 Zeichenohne ZwiebelnEine Notiz mit 21 Zeichen
jeder andere NameNicht erlaubtKeinerfarbe

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

Eine Ablehnung sagt, was zu ändern ist

Ein Aufruf mit schlechten Argumenten ist keine kaputte Anfrage. Die Anfrage war in Ordnung. Der Server sendet also ein normales Ergebnis mit isError true und einem Satz, der die Probleme nennt.

Ein Aufruf für ein Werkzeug, das es nicht gibt, ist etwas anderes. Das ist ein Protokollfehler mit dem Code -32602.

Ein häufiger Fehler

Warnung

Viele glauben, das Modell halte sich immer an das Schema, der Server müsse also nicht prüfen. Ein Modell kann einen Namen weglassen, Text für eine Zahl senden oder einen erfundenen Namen hinzufügen. Auch der Client prüft vielleicht nicht. Der Server ist die letzte Stelle zum Prüfen, also prüft er immer.

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' 'const SCHEMA = { type: \"object\", properties: { gericht: { type: \"string\", enum: [\"nudeln\", \"wrap\", \"salat\"] }, menge: { type: \"integer\", minimum: 1, maximum: 5 }, notiz: { type: \"string\", maxLength: 20 } }, required: [\"gericht\", \"menge\"], additionalProperties: false };' 'function senden(m) { process.stdout.write(JSON.stringify(Object.assign({ jsonrpc: \"2.0\" }, m)) + \"\n\"); }' 'function typPasst(regel, wert) { return regel.type === \"string\" ? typeof wert === \"string\" : Number.isInteger(wert); }' 'function probleme(argumente) {' '  const aus = [];' '  SCHEMA.required.forEach(function (n) { if ((n in argumente) === false) aus.push(\"fehlt: \" + n); });' '  Object.keys(SCHEMA.properties).forEach(function (n) {' '    if ((n in argumente) === false) return;' '    const r = SCHEMA.properties[n], w = argumente[n];' > server3.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 (typPasst(r, w) === false) return aus.push(n + \": erwartet \" + r.type);' '    if (r.enum && r.enum.indexOf(w) < 0) aus.push(n + \": muss eines von \" + r.enum.join(\", \") + \" sein\");' '    if (typeof r.minimum === \"number\" && (w < r.minimum || w > r.maximum)) aus.push(n + \": muss zwischen \" + r.minimum + \" und \" + r.maximum + \" liegen\");' '    if (typeof r.maxLength === \"number\" && w.length > r.maxLength) aus.push(n + \": höchstens \" + r.maxLength + \" Zeichen\");' '  });' '  Object.keys(argumente).sort().forEach(function (n) { if ((n in SCHEMA.properties) === false) aus.push(\"zusätzlich: \" + n); });' '  return aus;' '}' 'function worte(text, schlecht) { return { content: [{ type: \"text\", text: text }], isError: schlecht }; }' 'function verarbeiten(nachricht) {' '  if (nachricht.id === undefined) return;' '  const p = nachricht.params || {};' >> server3.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' '  if (nachricht.method === \"initialize\") return senden({ id: nachricht.id, result: { protocolVersion: p.protocolVersion, capabilities: { tools: {} }, serverInfo: { name: \"mensa\", version: \"0.3\" } } });' '  if (nachricht.method === \"tools/list\") return senden({ id: nachricht.id, result: { tools: [{ name: \"essen_bestellen\", description: \"Essen in der Mensa bestellen\", inputSchema: SCHEMA }] } });' '  if (p.name === \"essen_bestellen\") {' '    const argumente = p.arguments || {};' '    const gefunden = probleme(argumente);' '    if (gefunden.length > 0) return senden({ id: nachricht.id, result: worte(\"Aufruf von essen_bestellen nicht möglich: \" + gefunden.join(\"; \"), true) });' '    return senden({ id: nachricht.id, result: worte(\"Bestellt: \" + argumente.menge + \" \" + argumente.gericht, false) });' '  }' >> server3.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 4

Befehl
docker exec -w /home/node/project agent sh -c "printf '%s\n' '  senden({ id: nachricht.id, error: { code: -32602, message: \"Unbekanntes Werkzeug: \" + p.name } });' '}' 'require(\"readline\").createInterface({ input: process.stdin }).on(\"line\", function (zeile) { verarbeiten(JSON.parse(zeile)); });' >> server3.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

Befehl
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { fragen } = require(\"./model\");' 'const server = require(\"child_process\").spawn(\"node\", [\"server3.js\"]);' 'const bestellen = function (id, argumente) { return { id: id, method: \"tools/call\", params: { name: \"essen_bestellen\", arguments: argumente } }; };' 'const skript = [' '  { id: 1, method: \"initialize\", params: { protocolVersion: \"2025-03-26\" } },' '  { method: \"notifications/initialized\" },' '  bestellen(2, { gericht: \"wrap\", menge: 2 }),' '  bestellen(3, { gericht: \"suppe\", menge: 9, farbe: \"rot\" }),' '  bestellen(4, {}),' '  bestellen(5, { gericht: \"nudeln\", menge: \"2\" }),' '  { id: 6, method: \"tools/call\", params: { name: \"speisekarte_loeschen\", arguments: {} } },' '];' 'const gesehen = [];' 'require(\"readline\").createInterface({ input: server.stdout }).on(\"line\", function (zeile) {' '  const m = JSON.parse(zeile);' > client3.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

Befehl
docker exec -w /home/node/project agent sh -c "printf '%s\n' '  console.log(\"<- \" + (m.result && m.result.content ? m.result.content[0].text + \" (isError \" + m.result.isError + \")\" : zeile));' '  gesehen.push(m);' '  if (gesehen.length === 6) server.stdin.end();' '});' 'skript.forEach(function (m) { server.stdin.write(JSON.stringify(Object.assign({ jsonrpc: \"2.0\" }, m)) + \"\n\"); });' 'server.on(\"close\", async function () {' '  const gezeigt = gesehen[2].result.content[0].text;' '  const frage = \"Ein Werkzeug hat einen Aufruf abgelehnt. Was soll der Aufrufer ändern? Antworten Sie in einem Satz. \" + gezeigt;' '  const antwort = await fragen([{ role: \"user\", content: frage }], \"Wählen Sie nudeln, wrap oder salat, eine Menge von 1 bis 5 und keine Farbe.\", 60);' '  console.log(\"Modell: \" + antwort.text);' '});' >> client3.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

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

Was das Skript aufruft

Das Skript startet eine Sitzung und ruft dann fünfmal essen_bestellen auf und einmal ein Werkzeug, das es nicht gibt. Ein Aufruf ist gut. Die anderen verletzen eine Regel, mehrere Regeln oder lassen alle nötigen Namen weg.

Ein kurzer Modellaufruf am Ende liest eine Ablehnung und sagt, was zu ändern ist. Ohne Modell druckt er eine Aufgezeichnete Probe.

Was jeder Aufruf zurückbekommt

AufrufAntwort
wrap, menge 2Bestellt: 2 wrap, isError false
suppe, menge 9, farbe rotDrei Probleme in einem Satz, isError true
keine Argumentefehlt: gericht; fehlt: menge
menge als Text "2"menge: erwartet integer
speisekarte_loeschenProtokollfehler -32602

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.