Start / Cheat Sheets

Cheat Sheet

MCP-Server und Protokoll — Cheat Sheet

Stand: · MCP & Agentic Software Engineering

MCPModel Context ProtocolTypeScriptOAuth

Der Spickzettel zum Seminar MCP & Agentic Software Engineering, erster Teil: das Protokoll und der eigene Server — Rollen, Primitives, Fehlercodes, Tool-Schnitt, Robustheit, Autorisierung, Betrieb. Wie man einen Coding Agent beauftragt, prüft und absichert, steht auf dem zweiten Blatt, Agenten kontrollieren. Maßgeblich ist und bleibt die Spezifikation auf modelcontextprotocol.io — sie bewegt sich schnell, dieses Blatt steht auf dem Stand 2026-07-28.

Rollen und Verantwortung

RolleKernaufgabeDazu gehört
HostkoordinierenZustimmung, Richtlinien, Kontext
Clientverbindeneine Verbindung, Grenze halten
ServerbereitstellenTools, Resources, Prompts

Die 1:1-Beziehung zwischen Client und Server ist die technische Form der Isolation: Ein Server sieht weder den Gesprächsverlauf noch, was andere Server liefern. Wer sie aufweicht, verliert die Nachvollziehbarkeit.

RolleVerantwortetTypischer Fehler
MenschAnforderung, Testorakeldelegiert das Urteil mit
HostZustimmung, Sichtbarkeitbestätigt im Stapel
ServerAutorisierung, Wirkungvertraut dem Aufrufer
ZielsystemKonsistenz, Auditkennt den Agenten nicht

Module: MCP-Architektur: Host, Client und Server · MCP als Verbindungsschicht einordnen

Abgrenzung zu den Nachbarn

KonzeptVerhältnis zu MCP
REST-APIbleibt die fachliche Schnittstelle darunter
Function CallingModellseite, die MCP-Werkzeuge nutzt
Agenten-Frameworksteuert Ablauf und Planung darüber
Hersteller-Plugindas, was MCP ablösen soll

MCP ersetzt keine dieser Schichten, sondern setzt sie in ein gemeinsames Format. Als Agenten-Framework eingeführt, enttäuscht es erwartungsgemäß.

Modul: MCP als Verbindungsschicht einordnen

Primitives: wer steuert was

PrimitiveGesteuert vonMethoden
Toolsdem Modelltools/list, tools/call
Resourcesder Anwendungresources/list, resources/read
Promptsdem Nutzerprompts/list, prompts/get

Die Zuordnung hängt daran, wer entscheidet, wann etwas benutzt wird — nicht an der Technik. Eine Aktion als Resource wird nie ausgeführt, ein Dokument als Tool verbrennt Kontextbudget bei jedem Aufruf.

Felder einer Resource:

FeldPflichtWofür
urijaeindeutige Adresse
namejatechnischer Bezeichner
titleneinAnzeige in der Oberfläche
mimeTypeneinAuswertung beim Client
annotationsneinZielgruppe, Priorität, Änderungsdatum

Eine nicht vorhandene Resource wird mit -32602 abgelehnt — nie mit einer leeren Liste, die bleibt mehrdeutig.

Module: Primitives, Protokollmechanik und Spezifikationsstand · Einen lokalen MCP-Server entwickeln

Protokollmechanik und Pflichtmetadaten

Es gibt keine Sitzung mehr: Fähigkeiten werden je Anfrage in _meta ausgehandelt, eine offene Verbindung ist kein Gespräch.

"_meta": {
  "io.modelcontextprotocol/protocolVersion": "2026-07-28",
  "io.modelcontextprotocol/clientInfo": {
    "name": "anliegen-client", "version": "1.0.0" },
  "io.modelcontextprotocol/clientCapabilities": { "elicitation": {} } }

protocolVersion und clientCapabilities sind Pflicht, clientInfo soll gesetzt sein. Listen tragen ttlMs und cacheScope und dürfen zwischengespeichert werden — cacheScope: private, sobald die Liste nutzerabhängig ist.

CodeNameBedeutet
-32602Invalid paramsPflichtfeld fehlt, Ressource unbekannt
-32020HeaderMismatchHeader und Rumpf widersprechen sich
-32021MissingRequiredClientCapabilityClient kann nicht, was nötig wäre
-32022UnsupportedProtocolVersionVersion wird nicht unterstützt

Der Bereich −32020 bis −32099 ist für die Spezifikation reserviert; eigene Codes gehören nicht hinein.

Rückfragen laufen als Multi Round-Trip Request — der Server sendet keine eigene Anfrage mehr:

{ "result": { "resultType": "input_required",
    "inputRequests": { "freigabe": { "method": "elicitation/create" } },
    "requestState": "eyJ..." } }

Die Wiederholung schickt inputResponses samt requestState — und eine neue JSON-RPC-ID, sonst wird sie abgelehnt.

Module: MCP-Architektur: Host, Client und Server · Primitives, Protokollmechanik und Spezifikationsstand

Abgekündigt mit 2026-07-28

FeatureEmpfohlener Weg
RootsVerzeichnisse über Parameter oder Konfiguration
Samplingdirekte Anbindung an die Modell-API
Logging über das Protokollstderr bei stdio, sonst OpenTelemetry
Dynamic Client RegistrationClient ID Metadata Documents
HTTP+SSE-TransportStreamable HTTP

Abgekündigt heißt: weiterhin Teil der Spezifikation, aber zum Entfall vorgesehen. Frühester Entfall für die ersten vier ist die erste Revision ab dem 28.07.2027. Neue Implementierungen nehmen es nicht mehr auf.

Modul: Primitives, Protokollmechanik und Spezifikationsstand

Server-Grundgerüst (TypeScript)

// npm install @modelcontextprotocol/server zod
import { McpServer } from "@modelcontextprotocol/server";
import { z } from "zod";
const server = new McpServer({ name: "anliegen-mcp", version: "1.0.0" });

server.registerTool("anforderung_suchen", {
  description: "Sucht in den Anforderungen des Anliegenbuchs.",
  inputSchema: z.object({
    suchbegriff: z.string().min(2).describe("Fachbegriff, z. B. Kostenstelle"),
    maxTreffer: z.number().int().max(50).default(10),
  }),
}, async ({ suchbegriff, maxTreffer }) => { /* ... */ });

Werkzeugnamen: 1 bis 128 Zeichen, nur Buchstaben, Ziffern, Unterstrich, Bindestrich und Punkt. Ohne Angabe gilt der JSON-Schema-Dialekt 2020-12; ein Tool ohne Parameter bekommt ein leeres Objektschema, nicht null.

Die Werkzeugfunktion ruft eine normale Funktion der Anwendung auf — sonst ist die Fachlogik ohne Protokoll nicht mehr testbar.

Modul: Einen lokalen MCP-Server entwickeln

Tool-Schnitt und Zusicherungen

Parameter führen besser als Prosa: Aufzählungen statt freier Zeichenketten, Muster statt Formathinweisen, Beispielwerte im describe.

inputSchema: z.object({
  status: z.enum(["offen", "geprueft", "geschlossen"])
    .describe("Zielstatus der Serviceanfrage"),
  anliegenId: z.string().regex(/^AN-\d{6}$/)
    .describe("Nummer im Format AN-000123"),
  grund: z.string().max(280).optional().describe("Pflicht bei geschlossen"),
})
ArtAussageWo sie steht
VorbedingungAnfrage existiert und ist offenBeschreibung
NachbedingungStatus gesetzt, Historie ergänztBeschreibung
Invariantegeschlossene Anfrage bleibt geschlossenBeschreibung

Was der Server zusichert, muss er auch prüfen — die Beschreibung allein setzt nichts durch. Zustand über mehrere Aufrufe läuft über ein explizites Handle, dessen Berechtigung bei jedem Aufruf neu geprüft wird; der Besitz eines Handles ist keine Berechtigung, und fortlaufende Nummern sind erratbar.

Lesend, vorbereitend und verändernd gehören getrennt — in eigene Werkzeuge und in eigene Berechtigungen, nicht nur in die Dokumentation.

Modul: Tools aus Sicht des Sprachmodells entwerfen

Robuste Operationen

Wiederholbarkeit entsteht über einen fachlichen Schlüssel, den der Aufrufer bei der Wiederholung gleich lässt:

inputSchema: z.object({
  anliegenId: z.string().regex(/^AN-\d{6}$/),
  status: z.enum(["offen", "geprueft", "geschlossen"]),
  vorgangsschluessel: z.string().uuid().describe("Gleicher Wert bei Wiederholung"),
})
OperationPaginationCaching
tools/listjaja
prompts/listjaja
resources/listjaja
resources/readneinja
tools/callneinnein

Cursor sind undurchsichtig und werden nicht ausgewertet; ein ungültiger Cursor wird mit -32602 abgelehnt. Eine Kürzung des Ergebnisses muss gemeldet werden, sonst hält das Modell sie für die Wahrheit. Bei Streamable HTTP gilt das Schließen des Streams als Abbruch.

Modul: Robuste Operationen, Fehler und Kontextbudget

Start, Diagnose und Fehlermeldungen

{ "mcpServers": {
    "anliegen": {
      "command": "node",
      "args": ["/opt/anliegen-mcp/build/index.js"],
      "env": { "ANLIEGEN_DB": "postgres://db/anliegen" } } } }

Immer absolute Pfade — das Arbeitsverzeichnis eines vom Client gestarteten Servers ist undefiniert, und über stdio gestartete Server erben nur einen begrenzten, plattformabhängigen Teil der Umgebungsvariablen.

SymptomUrsache
Prozess startet nichtrelativer Pfad, fehlende Rechte
Startet und beendet sofortSyntaxfehler, fehlende Umgebungsvariable
Verbindung bleibt leerAusgaben auf stdout statt stderr
npx @modelcontextprotocol/inspector node build/index.js
npx @modelcontextprotocol/inspector --cli node build/index.js \
  --method tools/call --tool-name anforderung_suchen \
  --tool-arg suchbegriff=Kostenstelle

Der Inspector verlangt Node 22.19.0 oder neuer; die CLI-Variante läuft in der Pipeline. Ein Ausführungsfehler ist kein Protokollfehler — er geht ans Modell weiter und muss deshalb den nächsten Schritt nennen:

{ "result": { "resultType": "complete", "isError": true,
    "content": [ { "type": "text",
      "text": "4711 unbekannt, siehe kostenstellen_listen." } ] } }

Drei Angaben machen den Unterschied: was war falsch (Feld beim Namen nennen), warum (Bereich, Format, Vorbedingung), was hilft weiter (der konkrete nächste Aufruf). Nicht hinein gehören interne Pfade, Zugangsdaten und Stacktraces.

Modul: Den Server testen und diagnostizieren

Lokal oder entfernt, und OAuth 2.1

MerkmalLokalEntfernt
Nutzerkreiseine Personviele gleichzeitig
Zugangsdatenaus der UmgebungOAuth 2.1
Betriebsaufwandgeringwie ein Dienst
Datenzugriffwas der Nutzer darfwas der Scope erlaubt

Sobald ein zweiter Mensch denselben Server braucht, ist die lokale Variante am Ende — und der Umstieg ist kürzer als gedacht: gleiche Semantik, gleiche Fachlogik, neu sind Autorisierung, Betrieb und Telemetrie.

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer
  resource_metadata="https://anliegen.example/.well-known/oauth-protected-resource",
  scope="anliegen:lesen"

Zwei Verschärfungen dieser Revision: Der Client prüft vor dem Einlösen des Codes den Issuer (RFC 9207), und Client ID Metadata Documents sind der empfohlene Weg zur Client-Identität. Zusammen entschärft das Mix-up- und Confused-Deputy-Angriffe.

POST /mcp HTTP/1.1
Authorization: Bearer eyJhbGciOi...
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: anliegen_status_setzen
Accept: application/json, text/event-stream

Die Autorisierung gehört in jede einzelne HTTP-Anfrage — es gibt keine Sitzung, die sie trägt. Der Server prüft nicht nur die Signatur, sondern den Empfänger des Tokens, und reicht es nie an ein Drittsystem durch.

Modul: Remote MCP und OAuth-Autorisierung

Eingaben, Isolation und Notbremse

inputSchema: z.object({
  anliegenId: z.string().regex(/^AN-\d{6}$/),
  feld: z.enum(["status", "frist", "kostenstelle"]),
  wert: z.string().max(120),
})
// Zusaetzlich im Server pruefen: existiert die Anfrage,
// darf der Aufrufer sie sehen, passt der Wert zum Feld

Das Schema begrenzt die Form, der Server entscheidet über Existenz und Berechtigung. Vier Injektionswege ins Zielsystem: Suchbegriffe in Abfragen, Pfadangaben aus dem erlaubten Verzeichnis heraus, Bezeichner als URL-Fragment, Schemareferenzen auf fremde Adressen.

MittelVerhindert
Sandbox oder ContainerZugriff jenseits des Arbeitsbereichs
NetzwerkregelnAbfluss an unbekannte Ziele
Rate Limits je Identitätkaskadierende Fehler und Kostenangriffe
Kill Switchdass ein Fehllauf bis zum Ende läuft

Lokale Server laufen über stdio oder beschränken den HTTP-Zugriff — an localhost gebunden, mit Token oder Unix-Socket, und mit Prüfung des Origin-Headers gegen DNS-Rebinding.

Modul: Least Privilege, Isolation und Freigaben

Betrieb, Resilienz und Versionierung

MittelFängt ab
Timeout je Operationhängende Aufrufe, die den Lauf blockieren
Begrenzte WiederholungSturm auf ein bereits überlastetes System
Circuit BreakerDauerlast auf ein ausgefallenes Zielsystem
Rate Limit je Identitätkaskadierende Fehler und Kostenangriffe

Wiederholungen setzen Idempotenz voraus. Agenten belasten anders als Menschen: Ein Fehlschlag löst sofort den nächsten Versuch aus, und eine Schleife erzeugt in Sekunden, wofür ein Team Stunden bräuchte.

ÄnderungWeg
Neues optionales Felddirekt möglich
Neues Pflichtfeldneues Werkzeug, altes abkündigen
Geänderte Semantikneuer Name, beide parallel
EntfernungFrist ankündigen, dann entfernen

Auch eine „verbesserte” Beschreibung ist eine Verhaltensänderung — sie steuert, was das Modell wählt.

Modul: Betrieb, Katalog und Team-Governance

Typische Fallen

  • console.log im Werkzeug macht einen stdio-Server unbrauchbar — jede Ausgabe auf stdout zerstört den Nachrichtenstrom. Logs gehören auf stderr.
  • Die Werkzeugliste wechselt je Verbindung. Das verbietet die Spezifikation; Listen sind dynamisch, aber nicht verbindungsabhängig.
  • Ein Zustandsbezeichner gilt als Nachweis der Berechtigung. Er ist ein Bezeichner, mehr nicht — geprüft wird bei jedem Aufruf.
  • cacheScope: public bei nutzerabhängigen Listen liefert die gefilterte Sicht eines Nutzers an alle anderen aus.
  • Der Server merkt sich den Nutzer aus der ersten Anfrage. Es gibt keine Sitzung mehr, auf die sich das stützen ließe.
  • Sensible Parameter werden mit x-mcp-header in HTTP-Header gespiegelt — und landen damit in Logs und Zwischenstellen.
  • Das Gateway entscheidet nach Header, der Server arbeitet nach Rumpf. Genau dafür gibt es -32020; der Server gleicht beides ab.
  • Der Teilerfolg wird als Gesamtfehler gemeldet und zwingt zum vollständigen Wiederholen, statt je Eintrag zu benennen, was gelang.
  • Jeder REST-Endpunkt wird eins zu eins zum Werkzeug. Der Katalog explodiert, und die Auswahl des Modells wird unsicher.
  • Der Inspector wird erst geholt, wenn nichts mehr geht — als Diagnosewerkzeug ist er am Anfang am billigsten.

Zum Seminar MCP & Agentic Software Engineering