MCP anhand der Spec erklärt: Was ein Server wirklich ist
Eine JSON-Zeile in einen Subprozess, dreizehn Tool-Definitionen zurück – gelesen gegen Revision 2026-07-28 ohne Handshake.
Auf dieser Seite
Installiere einen veröffentlichten MCP Server, sende ihm eine Zeile JSON und lies, was zurückkommt.
npm i @modelcontextprotocol/server-everything@2026.8.31
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \
| npx @modelcontextprotocol/server-everything stdio{"result":{"tools":[{"name":"echo","title":"Echo Tool","description":"Echoes
back the input string","inputSchema":{"$schema":"http://json-schema.org/draft-07/
schema#","type":"object","properties":{"message":{"type":"string","description":
"Message to echo"}},"required":["message"]},"annotations":{"readOnlyHint":true,
… … 7,663 bytes on one line …
"jsonrpc":"2.0","id":1}Dreizehn Tool-Definitionen, in einer einzigen Zeile, aus einem Prozess, der eine Zeile von seiner Standardeingabe gelesen hat. Du hast jetzt das Model Context Protocol gesprochen, ohne SDK, ohne Client-Bibliothek und ohne Framework. Das ist alles: ein Transport, ein Nachrichtenformat und eine kleine Menge benannter Methoden.
Kapitel 18 definierte ein Tool als zwei Dinge — ein JSON Schema, das das Modell sieht, und einen Endpoint in deinem Code, den das Modell nie sieht. Kapitel 23 baute ein harness, das einen Katalog davon hält. Keines beantwortete die Frage, die entscheidet, ob irgendetwas davon wiederverwendbar ist: Wer schreibt das Schema, und wie gelangt es von der Person, die es geschrieben hat, in deinen prompt? MCP ist eine Antwort auf diese Frage, und es lohnt sich, sie im Original zu lesen, weil fast alles, was darüber geschrieben wurde, eine Revision beschreibt, die nicht mehr existiert.
Drei Dinge an dem Befehl, den du gerade ausgeführt hast, sind falsch, und jedes davon ist ein Abschnitt dieses Kapitels. Er trug keine Protokollversion, also hätte ein konformer Server ihn abgelehnt. Er bekam trotzdem eine Antwort, aus einem Grund, den die Spezifikation als Gefahr und nicht als Feature bezeichnet. Und er fragte nach einem von drei Primitiven, ohne je herauszufinden, dass die anderen beiden existieren.
Das Problem, das es löst, und die Analogie, die die Spec selbst zieht
Link zum Abschnitt: Das Problem, das es löst, und die Analogie, die die Spec selbst ziehtVor dem Wire kommt die Arithmetik. Du hast AI-Anwendungen und Dinge, die sie erreichen können sollen — einen Kalender, einen Ticket-Tracker, eine Warehouse-Datenbank, ein Design-Tool. Ohne gemeinsamen Vertrag schreibt jemand Integrationen, und jede einzelne ist ein Schema plus ein Endpoint plus eine Authentifizierungsstory plus Wartungsaufwand. Mit einem schreibt der Tool-Anbieter einen Server, der Anwendungsanbieter einen Client, und die Summe ist .
Das ist keine neue Beobachtung, und die Spezifikation sagt, wessen Idee es war:
MCP takes some inspiration from the Language Server Protocol, which standardizes how to add support for programming languages across a whole ecosystem of development tools. In a similar way, MCP standardizes how to integrate additional context and tools into the ecosystem of AI applications.1
Nimm diesen Vergleich wörtlich, nicht als Kompliment. Vor diesem Protokoll bedeutete die Unterstützung einer Sprache in einem Editor ein Plugin pro Editor; danach lieferte ein Sprachteam einen Server aus und jeder Editor bekam sie. Das Maß für Erfolg war nicht Eleganz, sondern dass die Anzahl der Integrationen aufhörte, sich zu multiplizieren. Dasselbe folgt hier: Der Wert liegt in der Anzahl der Implementierungen, nicht im Design. Ein Protokoll, das zwei Produkte sprechen, ist ein Datenformat mit zusätzlicher Zeremonie.
Was tatsächlich auf dem Wire liegt
Link zum Abschnitt: Was tatsächlich auf dem Wire liegtMCP-Nachrichten sind JSON-RPC 2.0. Eine Request ist ein Objekt mit jsonrpc, einer id, einer method und optionalen params; eine Response trägt dieselbe id und entweder result oder error; eine Notification ist eine Request ohne id und erhält keine Antwort. Die Spezifikation legt drei zusätzliche Constraints darüber: Die id muss ein String oder eine Zahl sein und darf nicht null sein, sie darf nicht mit einer anderen noch laufenden Request kollidieren, und jedes Result muss ein resultType-Feld tragen.2
Beim stdio-Transport — dem, den der obige Befehl verwendet hat — lautet die Framing-Regel: eine Zeile pro Nachricht:
Messages are delimited by newlines, and MUST NOT contain embedded newlines. […] The server MUST NOT write anything to its
stdoutthat is not a valid MCP message.3
Diese letzte Klausel ist die häufigste Art, wie ein selbstgebauter Server kaputtgeht, und er geht still kaputt: ein verirrtes console.log, ein Fortschrittsbalken, eine Deprecation-Warnung einer Dependency, und der Line-Parser des Clients trifft auf etwas, das kein JSON ist. Der Notausgang steht im selben Abschnitt — der Server darf alles, was er möchte, nach stderr schreiben, und der Client sollte das nicht als Fehler behandeln. Der Referenzserver oben druckt bei jedem Start Starting default (STDIO) server... auf stderr, weshalb die Pipe trotzdem funktionierte.
Der andere Standardtransport ist Streamable HTTP: Jede Nachricht ist ein POST an einen einzelnen Endpoint, und die Antwort ist entweder ein JSON-Objekt oder ein Request-scoped Stream von Server-Sent Events — das Wire-Format, das Kapitel 14 von Hand geparst hat. Die Semantik ist bei beiden identisch, weil ein Transport ein Binding ist: Er definiert Framing und Zustellung, nicht Bedeutung.4
Das Erste, was falsch war: Es gab keine Version
Link zum Abschnitt: Das Erste, was falsch war: Es gab keine VersionDer obige Befehl sendete tools/list und sonst nichts. Unter der aktuellen Revision ist diese Request malformed, und ein konformer Server muss sie ablehnen.
Seit 2026-07-28 ist MCP ein zustandsloses Protokoll, und die Spezifikation sagt das ohne Einschränkung:
The Model Context Protocol (MCP) is a stateless protocol: all the information needed to process a request is contained in the request itself. A server processes each request independently; no state should be inferred from previous requests, even those on the same connection or stream.2
Also trägt jede Request ihre eigene Protokollversion und ihre eigenen Client-Capabilities in einem reservierten _meta-Objekt innerhalb von params. Zwei dieser Felder sind bei jeder einzelnen Request erforderlich; fehlt eines davon, ist die Request malformed und der Server muss mit -32602 antworten:2
_meta key | required | was es ist |
|---|---|---|
io.modelcontextprotocol/protocolVersion | yes | die Revision, die diese Request spricht, z. B. "2026-07-28" |
io.modelcontextprotocol/clientCapabilities | yes | was der Client für den Server bei dieser Request tun kann |
io.modelcontextprotocol/clientInfo | no (aber should) | Client-Name und -Version, nur für Anzeige und Logs |
io.modelcontextprotocol/logLevel | no | das Mindest-Log-Level, das der Server für diese Request ausgeben sollte |
Ausgeschrieben sieht ein korrektes tools/list so aus — und es ist das letzte Mal, dass dieses Kapitel die Metadaten vollständig zeigt, weil sie ab hier auf jeder Request stehen:
{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{"_meta":{
"io.modelcontextprotocol/protocolVersion":"2026-07-28",
"io.modelcontextprotocol/clientCapabilities":{"elicitation":{"form":{}}},
"io.modelcontextprotocol/clientInfo":{"name":"bare-hands","version":"0.0.1"}}}}Das Capability-Objekt ist die Negotiation. Es gibt keinen separaten Negotiation-Schritt mehr: Der Client erklärt bei jeder Request, was er kann, der Server erklärt im Result, was er kann, und keine Seite darf ein Feature verwenden, das die andere nicht beansprucht hat. Ein Server, der eine Capability braucht, die der Client nicht deklariert hat, muss mit -32021 antworten und die fehlende Capability in data.requiredCapabilities benennen. Ein Server, der die angeforderte Version nicht spricht, muss mit -32022 antworten und die Versionen auflisten, die er spricht.2
Clients, die die Antwort vorab möchten, können danach fragen: server/discover ist ein verpflichtender RPC, der unterstützte Versionen, Capabilities, Identität und einen optionalen Block von instructions in einem Roundtrip zurückgibt.5 Ihn aufzurufen ist optional. Ihn zu implementieren nicht.
Das Zweite, was falsch war: Der Server war legacy
Link zum Abschnitt: Das Zweite, was falsch war: Der Server war legacyDer Befehl funktionierte. Unter der aktuellen Revision hätte er das nicht tun sollen, und der Grund dafür ist eher eine Messung wert als einen Absatz, denn er ist der Zustand des gesamten Ökosystems in einer Zeile.
Probe den Referenzserver so, wie die Spezifikation es einem modernen Client vorschreibt:
echo '{"jsonrpc":"2.0","id":1,"method":"server/discover","params":{"_meta":{
"io.modelcontextprotocol/protocolVersion":"2026-07-28",
"io.modelcontextprotocol/clientCapabilities":{}}}}' \
| npx @modelcontextprotocol/server-everything stdio{"jsonrpc":"2.0","id":1,"error":{"code":-32601,"message":"Method not found"}}Das ist der dritte Zweig der Kompatibilitätsregel: Ein DiscoverResult bedeutet modern, ein erkannter moderner Fehler bedeutet modern-aber-falsche-Version, und alles andere — einschließlich -32601 — bedeutet legacy, falle auf den initialize-Handshake zurück.3 Also tue das, mit der aktuellen Revision:
→ {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2026-07-28",
"capabilities":{},"clientInfo":{"name":"bare-hands","version":"0.0.1"}}}
← {"result":{"protocolVersion":"2025-11-25","capabilities":{"tools":{"listChanged":true},
"prompts":{"listChanged":true},"resources":{"subscribe":true,"listChanged":true},
"logging":{},"tasks":{…},"completions":{}},"serverInfo":{"name":"mcp-servers/everything",
"title":"Everything Reference Server","version":"2.0.0"},"instructions":"…"}}Der Client fragte nach 2026-07-28 und der Server antwortete 2025-11-25. Am 7. September 2026 implementiert der offizielle Referenzserver — npm-Package @modelcontextprotocol/server-everything, Version 2026.8.31, veröffentlicht am 31. August 2026 — die aktuelle Revision nicht. Ebenso wenig, den Daten nach, das TypeScript SDK, auf dem er basiert: Release 1.30.0 erschien am 27. Juli 2026, einen Tag vor der Revision.
Lies die Konsequenz, nicht den Klatsch. Fast alles, was über MCP geschrieben wurde, beschreibt ein Protokoll mit einem initialize-Handshake, einer Session, einer roots/list-Request, die der Server an den Client sendet, und einem HTTP+SSE-Transport. Alle vier sind weg oder gehen gerade weg. Wenn du etwas über MCP liest, einschließlich dieser Seite, ist das Erste, wonach du suchen solltest, eine Revisionsnummer.
Und der Grund, weshalb der allererste Befehl funktionierte, steht in der Spezifikation als Gefahr, nicht als Feature:
some legacy servers do not validate that a request arrives after
initializeand would process an era-ambiguous method (such astools/call) under legacy semantics. Probing yields a deterministic failure instead.3
Gemessen: tools/list ohne jeden Handshake an diesen Server zu senden, liefert den vollständigen Katalog zurück. Eine Methode, die hätte abgelehnt werden sollen, wurde bedient, und genau deshalb sagt die Spezifikation, zuerst mit server/discover zu proben, selbst wenn du nur moderne Versionen unterstützt.
Drei Rollen und der Satz, den man aus dem ganzen Dokument zitieren sollte
Link zum Abschnitt: Drei Rollen und der Satz, den man aus dem ganzen Dokument zitieren sollteMCP hat drei Parteien, und die Unterscheidung zwischen den ersten beiden ist die, die Leute zusammenfallen lassen:
Host. Die Anwendung: das Chat-Produkt, der Editor, der agent. Er besitzt die Konversation, das Modell, die Credentials und die Zustimmung des Nutzers. Er erstellt Clients und erzwingt die Sicherheitsgrenze zwischen ihnen.
Client. Ein Connector innerhalb des Hosts. Jeder Client spricht mit genau einem Server — eine strikte 1:1-Beziehung — und hängt die Protokollversion und Capabilities an jede Request, die er routet.
Server. Ein Prozess oder Service, der Ressourcen, Tools und prompts exposes. Er kann lokal oder remote sein, arbeitet unabhängig, und seine ganze Aufgabe ist ein fokussierter Bereich.6
Diese „genau ein Server“-Regel ist keine Buchhaltung. Sie macht das folgende Designprinzip implementierbar, und dies ist der Satz, den du aus der Spezifikation mitnehmen solltest, wenn du nur einen mitnimmst:
Servers should not be able to read the whole conversation, nor "see into" other servers. Servers receive only necessary contextual information. Full conversation history stays with the host. Each server maintains isolation. Cross-server interactions are controlled by the host.6
Das kippt das mentale Modell, mit dem die meisten Menschen ankommen. Ein Wetterserver, den du mit deinem Assistant verbindest, sieht nicht, was du gefragt hast. Er sieht einen tools/call mit den Argumenten, die das Modell gewählt hat, und nichts anderes — nicht die vorherigen Turns, nicht deinen system prompt, nicht die Ergebnisse, die der Kalenderserver einen Moment zuvor zurückgegeben hat. Wenn zwei Server zusammenarbeiten müssen, trägt der Host einen Wert absichtlich von einem zum anderen, weil das Modell ihn darum gebeten hat. Deshalb ist Isolation die Sicherheitseigenschaft, auf der Kapitel 30 aufbaut: Ein kompromittierter Server hat einen kleinen, definierten Blast Radius, und ihn zu vergrößern erfordert die Kooperation des Hosts.
Das Dritte: drei Primitive, sortiert danach, wer das Sagen hat
Link zum Abschnitt: Das Dritte: drei Primitive, sortiert danach, wer das Sagen hatDer erste Befehl fragte diesen Server nach Tools und bekam dreizehn. Stell ihm die anderen zwei Fragen, und er beantwortet sie ebenfalls: resources/list gibt sieben zurück, prompts/list gibt vier zurück. Keines davon erschien, weil nichts danach fragte. Damit kommen wir zum pädagogischen Rückgrat von MCP, das in der Spezifikation als Tabelle steht und das fast niemand zitiert:
| Primitive | Control | Beschreibung | Beispiel |
|---|---|---|---|
| Prompts | User-controlled | Interaktive Templates, die durch Nutzerwahl aufgerufen werden | Slash-Commands, Menüoptionen |
| Resources | Application-controlled | Kontextdaten, die vom Client angehängt und verwaltet werden | Dateiinhalte, Git-Historie |
| Tools | Model-controlled | Funktionen, die dem LLM offengelegt werden, um Aktionen auszuführen | API-POST-Requests, Dateien schreiben |
Nicht „drei Wege, eine Capability offenzulegen“. Drei Antworten auf wer entscheidet, dass dies passiert. Das Modell entscheidet, ein Tool aufzurufen. Die Anwendung entscheidet, eine Ressource anzuhängen. Die Person entscheidet, einen prompt auszuführen. Verstehst du das falsch, funktioniert das Feature trotzdem, aber es funktioniert im falschen Moment und aus dem falschen Grund.
Am klarsten spürst du es an einem Kalender. Hier ist ein Server, der denselben Kalender dreimal exposed, einmal als jedes Primitive, in hundert Zeilen schlichtem Node ohne Dependencies:
const TOOL = {
name: "create_event",
description: "Create a calendar event. Writes to the calendar.",
inputSchema: {
type: "object",
properties: {
title: { type: "string", description: "Event title." },
startsAt: { type: "string", format: "date-time", description: "Start, ISO 8601 UTC." },
},
required: ["title"],
},
};
switch (method) {
case "resources/read":
return ok(id, { contents: [{ uri: "calendar://week",
mimeType: "application/json", text: JSON.stringify(EVENTS) }],
ttlMs: 60000, cacheScope: "private" });
case "prompts/get":
return ok(id, { description: PROMPT.description, messages: [{ role: "user",
content: { type: "text", text: `Read calendar://week and draft a plan. ` +
`Focus: ${params.arguments?.focus ?? "balance"}.` } }] });
case "tools/list":
return ok(id, { tools: [TOOL], ttlMs: 300000, cacheScope: "public" });
}Führe ihn aus und frage ihn auf alle drei Arten. Echte Ausgabe, eine Nachricht pro Zeile auf dem Wire, hier für die Seite umbrochen, mit der Request _meta und dem Identitätsblock des Servers ausgelassen:
→ resources/read {"uri":"calendar://week"}
← {"resultType":"complete","contents":[{"uri":"calendar://week",
"mimeType":"application/json","text":"[{\"id\":\"e1\",\"title\":\"Standup\",
\"startsAt\":\"2026-09-07T09:00:00Z\"},{\"id\":\"e2\",\"title\":\"Design review\",
\"startsAt\":\"2026-09-09T15:00:00Z\"}]"}],"ttlMs":60000,"cacheScope":"private"}
→ prompts/get {"name":"prepare_week","arguments":{"focus":"deep work"}}
← {"resultType":"complete","description":"Read the week and draft a plan.",
"messages":[{"role":"user","content":{"type":"text",
"text":"Read calendar://week and draft a plan. Focus: deep work."}}]}
→ tools/call {"name":"create_event","arguments":{"title":"Dentist",
"startsAt":"2026-09-10T08:30:00Z"}}
← {"resultType":"complete","content":[{"type":"text",
"text":"Created e3: Dentist at 2026-09-10T08:30:00Z"}],
"structuredContent":{"id":"e3","title":"Dentist","startsAt":"2026-09-10T08:30:00Z"},
"isError":false}Drei Methoden, drei Formen, ein Kalender. Jetzt der Punkt:
Die Woche zu lesen ist eine Ressource
Link zum Abschnitt: Die Woche zu lesen ist eine RessourceSie wird über eine URI adressiert, ist inert, und die Anwendung entscheidet, ob sie an die Konversation angehängt wird. Nichts im Protokoll lässt das Modell von selbst danach greifen. Das Result trägt ttlMs und cacheScope, neu in dieser Revision, damit der Client die Woche eine Minute cachen kann, statt zu pollen.
Ein Event zu erstellen ist ein Tool
Link zum Abschnitt: Ein Event zu erstellen ist ein ToolEs hat ein Schema, es hat Side Effects, und das Modell entscheidet, wann es aufgerufen wird. Sein Result trägt isError, das Feld, für das Kapitel 18 argumentiert hat: Ein Validierungsfehler kommt als Tool-Result zurück, das das Modell lesen und korrigieren kann, nicht als Protokollfehler.
„Bereite meine Woche vor“ ist ein prompt
Link zum Abschnitt: „Bereite meine Woche vor“ ist ein promptEs ist ein benanntes Template mit Argumenten, das die Person aufruft — der Slash-Command im Menü. Es gibt Nachrichten zurück, keine Antwort. Es ist eine Möglichkeit für einen Server-Autor, die Formulierung auszuliefern, die mit seinen eigenen Tools funktioniert, und genau dieses Wissen hat der Server-Autor und der Nutzer nicht.
Fast alle machen aus allen dreien Tools. Das Ergebnis ist ein Katalog, in dem ein Read, den die Anwendung still hätte anhängen sollen, um die Aufmerksamkeit des Modells mit einem Write konkurriert, der Approval braucht, und in dem das eine Ding, für das eine Person einen Button wollte, in einem Schema begraben liegt. Es kostet nichts, es richtig zu machen, und es wird entschieden, bevor du eine Zeile schreibst.
Der Server kann dich nicht aufrufen
Link zum Abschnitt: Der Server kann dich nicht aufrufenDas Kalender-Tool hat ein erforderliches Argument, title, und ein optionales startsAt. Bitte es, ein Event ohne Datum zu erstellen, und etwas Interessantes kommt zurück:
→ tools/call {"name":"create_event","arguments":{"title":"Dentist"}}
← {"resultType":"input_required",
"inputRequests":{"when":{"method":"elicitation/create","params":{"mode":"form",
"message":"When should \"Dentist\" start?",
"requestedSchema":{"type":"object",
"properties":{"startsAt":{"type":"string","format":"date-time"}},
"required":["startsAt"]}}}},
"requestState":"eyJ0aXRsZSI6IkRlbnRpc3QifQ=="}Der Server hat keine Request gesendet. Er hat diejenige beantwortet, die ihm gegeben wurde, mit resultType: "input_required" und einer Beschreibung dessen, was er noch braucht. Der Client sammelt die Antwort von der Person und sendet dann den ursprünglichen Call erneut — mit einer neuen id, die inputResponses trägt und das opaque requestState zurückspiegelt:
→ tools/call {"name":"create_event","arguments":{"title":"Dentist"},
"inputResponses":{"when":{"action":"accept",
"content":{"startsAt":"2026-09-10T08:30:00Z"}}},
"requestState":"eyJ0aXRsZSI6IkRlbnRpc3QifQ=="}
← {"resultType":"complete","content":[{"type":"text",
"text":"Created e3: Dentist at 2026-09-10T08:30:00Z"}],"isError":false}Das sind Multi Round-Trip Requests, eingeführt in der aktuellen Revision, und sie ersetzten das ältere Design, bei dem Server JSON-RPC-Requests zurück an Clients sendeten. Die Transport-Spezifikation sagt die Regel jetzt glatt heraus: „servers do not initiate JSON-RPC requests and clients do not send JSON-RPC responses“.4 Es gibt eine Richtung der Initiative, und sie gehört dem Host.
Zwei clientseitige Features reiten auf diesem Mechanismus, und eines davon hat einen Namen, über den du stolpern wirst.
Elicitation ist der Server, der die Person nach etwas fragt: ein Formular mit einem bewusst eingeschränkten JSON Schema — flache Objekte, primitive Properties, keine Verschachtelung — damit jeder Client es ohne Layout-Engine rendern kann. Es trägt eine harte Regel: Server dürfen den Formularmodus nicht verwenden, um nach „passwords, API keys, access tokens, or payment credentials“ zu fragen, und müssen dafür den URL-Modus verwenden, der den Nutzer auf eine Seite schickt, die der Client nie liest.7
Sampling ist der Server, der das Modell des Hosts um eine Generierung bittet, damit ein Server intelligent sein kann, ohne einen API key zu halten. Und hier ist die Vokabelwarnung, weil dieses Wort in diesem Kurs bereits etwas anderes bedeutet: Das ist nicht das Sampling aus Kapitel 17. Hier geht es nicht um Temperature, top-p oder die Form einer Wahrscheinlichkeitsverteilung. Es ist ein verschachtelter Modell-Call, der rückwärts durch ein Protokoll reist.
Es gibt einen zweiten Grund, nicht danach zu greifen: Ab dieser Revision ist sampling deprecated, zusammen mit Roots und Logging, unter SEP-2577, mit einer stumpfen vorgeschlagenen Migration — „integrate directly with LLM provider APIs instead of Sampling“.8 Die Idee ist nicht technisch gescheitert; sie hat ihre Surface Area nicht gerechtfertigt, und ein Protokoll, das Dinge entfernen kann, ist gesünder als eines, das es nicht kann.
Mach es absichtlich kaputt: Verbindungen sind keine Sessions
Link zum Abschnitt: Mach es absichtlich kaputt: Verbindungen sind keine SessionsZustandslosigkeit klingt wie ein Wire-Format-Detail, bis du sie testest. Nimm den Drei-Nachrichten-Austausch oben und führe jede Nachricht in einem separaten Prozess aus — ein frisches node calendar.mjs, kein gemeinsamer Speicher, nichts wird mitgetragen:
process A tools/call (no date) → resultType: input_required
requestState: eyJ0aXRsZSI6IkRlbnRpc3QifQ==
process B tools/call (with the answer, same requestState)
→ resultType: complete
"Created e3: Dentist at 2026-09-10T08:30:00Z"
process C resources/read calendar://week
→ events: 2 (Standup, Design review)Prozess B, der die Frage nie gesehen hat, schloss einen Multi-Round-Trip-Call ab, den Prozess A begonnen hatte. Das ist der Sinn von requestState: Die Continuation reist in der Nachricht, also hängt nichts davon ab, dass es derselbe Prozess ist.
Prozess C ist der Fehler. Das Event wurde erstellt und ist nicht da — weil der Toy-Server EVENTS in einem Array auf Modulebene hält, und ein Array auf Modulebene Connection State ist. Die Note der Spezifikation benennt den Fehler präzise:
an open connection, such as a STDIO process, is not a conversation or session: clients may interleave unrelated requests on the same transport, and a server must not treat connection or process identity as a proxy for conversation or session continuity.2
Der vorgeschriebene Fix ist keine Session. Es ist ein explizites Handle: Ein Creation-Tool gibt einen opaque Identifier zurück, und jeder spätere Call nimmt ihn als gewöhnliches Argument. Das Protokoll hat davon überhaupt keinen Begriff — „from the wire's perspective a handle is an ordinary string in a tool result and an ordinary argument to subsequent tool calls“.9 Damit ist das Modell dafür zuständig, ihn zu tragen, und der Server dafür, bei jedem einzelnen Call zu validieren, dass dieser Caller ihn verwenden darf, denn ein Handle ist ein Name und keine Berechtigung.
Was ein Server kostet, bevor er irgendetwas tut
Link zum Abschnitt: Was ein Server kostet, bevor er irgendetwas tutJedes Tool, das ein Server exposed, ist ein Schema, das bei jeder Request in deinen prompt geht, und Kapitel 24 hat gemessen, was das mit einer Window macht. MCP fügt einen zweiten Posten hinzu, der leicht zu übersehen ist, also lohnt es sich, beide beim Referenzserver oben zu zählen.
13 tool definitions (name + description + inputSchema): 1,307 tokens
cheapest tool, get-tiny-image 52
costliest tool, gzip-file-as-resource 235
server `instructions`, returned by discovery: 312 tokens
------
one server, connected, before it is used: 1,619 tokensZwei Beobachtungen. Die erste ist Arithmetik: Verbinde fünf Server dieser Größe, und ungefähr achttausend token deiner Window sind bei jedem Turn für immer belegt, egal ob das Modell irgendeinen davon nutzt — das ist der Mechanismus hinter der Reduktion von 150.000 auf 2.000, die Kapitel 24 zitierte, und der Grund, warum Just-in-time-Tool-Discovery existiert.
Die zweite ist eine Sicherheitsnotiz im Kostüm der Buchhaltung. instructions ist Natural-Language-Text, geschrieben vom Server-Autor, der im prompt des Hosts landet, und die Tool-Beschreibungen daneben sind dasselbe. Die Spezifikation sagt in ihren eigenen Sicherheitsprinzipien, was damit zu tun ist: Tool-Annotations und -Beschreibungen „should be considered untrusted, unless obtained from a trusted server“, und Hosts „must obtain explicit user consent before invoking any tool“.1 Einen MCP Server zu verbinden, ist nicht das Hinzufügen einer Dependency. Es bedeutet, einem Fremden 1.619 token deines system prompt und das Recht, aufgerufen zu werden, zu gewähren. Kapitel 30 zeigt, was passiert, wenn dieser Fremde feindlich ist.
Datierter Abschnitt: die Revision 2026-07-28 und was sie bricht
Link zum Abschnitt: Datierter Abschnitt: die Revision 2026-07-28 und was sie brichtAlles in diesem Abschnitt gilt für Protokollrevision 2026-07-28, die aktuelle, gelesen am 7. September 2026. Revisionen sind als YYYY-MM-DD datiert, und das Datum ist der letzte Zeitpunkt, zu dem eine rückwärtsinkompatible Änderung vorgenommen wurde.10 Das normative Dokument ist eine TypeScript-Datei, schema/2026-07-28/schema.ts; das JSON Schema daneben wird daraus generiert, weshalb die Spezifikation hier in TypeScript gelesen wird und weshalb MCP aus irgendetwas anderem zu lehren heißt, eine Übersetzung zu lehren.
| Was sich geändert hat | War | Ist jetzt | Bricht |
|---|---|---|---|
| Der Handshake | initialize + notifications/initialized, einmal pro Verbindung | entfernt; jede Request trägt _meta-Version und Capabilities | jeden Client, der vor dieser Revision geschrieben wurde |
| Sessions | Mcp-Session-Id-Header, connection-scoped State | entfernt; State reist in expliziten, vom Server geprägten Handles | List-Endpoints, die pro Verbindung variierten |
| Discovery | abgeleitet aus dem initialize-Result | server/discover, das Server implementieren müssen | nichts, aber die Implementierung ist jetzt verpflichtend |
| Server-zu-Client-Calls | Server sendete roots/list, sampling/createMessage, elicitation/create | InputRequiredResult und ein Client-Retry | jeden Server, der eine Request an einen Client gepusht hat |
| Result-Form | beliebiges Objekt | erforderliches resultType: "complete" oder "input_required" | nichts: ein fehlendes Feld muss als "complete" gelesen werden |
| Subscriptions | HTTP-GET-Stream, resources/subscribe | ein subscriptions/listen-Stream mit Opt-in-Typen | der GET-Endpoint ist weg |
| Stream Resumption | Last-Event-ID-Replay auf Streamable HTTP | entfernt; ein gebrochener Stream verliert die Request, erneut mit einer neuen id ausgeben | Clients, die sich auf Redelivery verlassen haben |
| Roots | ein Client-Feature, nach dem Server fragen konnten | deprecated (SEP-2577); Pfade als Tool-Argumente oder Resource-URIs übergeben | noch nichts — Zwölf-Monats-Fenster |
| Sampling und Logging | Client-Features | deprecated (SEP-2577) | noch nichts — Zwölf-Monats-Fenster |
| HTTP+SSE-Transport | deprecated seit 2025-03-26 | Deprecated unter der Lifecycle Policy (SEP-2596) | zu Streamable HTTP migrieren |
| Client Registration | OAuth 2.0 Dynamic Client Registration, RFC 7591 | deprecated zugunsten von Client ID Metadata Documents | für Authorization Server ohne sie beibehalten |
| Error Codes | -32002 für Resource not found | -32602; -32020–-32099 für die Spec reserviert | neue Codes -32020, -32021, -32022 |
Die Governance-Änderung unter dieser Tabelle ist wichtiger als jede einzelne Zeile. Diese Revision führte eine Feature-Lifecycle- und Deprecation-Policy ein: Features sind Active, Deprecated oder Removed, ein deprecated Feature dokumentiert seinen Migrationspfad und bleibt mindestens zwölf Monate in der Spezifikation, bevor es für Removal infrage kommt, und es gibt eine Registry, die alles auflistet, was aktuell im Deprecated-Zustand ist.8 Vor dieser Policy bedeutete „deprecated“ in einem AI-Protokoll, was auch immer der letzte Blogpost sagte. Jetzt bedeutet es ein Datum.
Details anzeigen
Extensions, der Teil, über den noch niemand geschrieben hat.
Über den Core hinaus definiert MCP optionale Extensions — „always opt-in and require explicit support from both client and server“, deklariert über ein extensions-Feld in den Capabilities von Client und Server.1 Drei solltest du beim Namen kennen:
- Tasks (
io.modelcontextprotocol/tasks), in dieser Revision aus dem Core-Protokoll in eine offizielle Extension verschoben: asynchrone Ausführung lang laufender Operationen, mit Polling übertasks/get, Mid-flight-Input übertasks/updateund durable Handles. Es ist die Antwort auf ein Tool, das zwanzig Minuten braucht, was Kapitel 23 mit einem Progress Event und einem Signal behandelte, das das Tool erreicht. - Skills over MCP, eine Working Group, die agent skills — das Thema von Kapitel 28 — über das Protokoll discoverable und consumable macht.
- MCP Apps, interaktive UI, die inline in der Konversation gerendert wird: Charts, Formulare, Videoplayer.
Und beachte, was „negotiated“ jetzt bedeutet: Es gibt keine Initialisierung mehr, bei der man negotiaten könnte, also wird eine Extension wie alles andere pro Request deklariert.
Wo MCP sitzt, gegenüber allem, womit es verwechselt wird
Link zum Abschnitt: Wo MCP sitzt, gegenüber allem, womit es verwechselt wirdDas ist das Vokabular des ganzen Blocks an einer Stelle.
| Was es ist | Wer mit wem spricht | Wann es die Antwort ist | |
|---|---|---|---|
| Eine plain API | Eine Schnittstelle für ein Programm | dein Code ↔ ein Service | Du schreibst den Caller. Du kontrollierst Schema, Auth und Error Handling, und es gibt kein Discovery-Problem zu lösen. |
| MCP | Ein Protokoll, um Tools, Daten und Templates für eine AI-Anwendung offenzulegen | Host ↔ Server, je ein Client | Jemand anders hat die Capability geschrieben, und viele Hosts sollen sie ohne bespoke Integration nutzen können. |
| RAG | Eine Technik, um Text zu finden und in den prompt zu legen | dein Code ↔ dein Index | Das Modell muss etwas wissen. Kapitel 19. MCP ist ein Weg, einen Retriever zu liefern; es ist kein Retriever. |
| Agent skills | Ein Ordner mit einem SKILL.md, das das Modell liest | Modell ↔ ein Dokument | Das Wissen ist prozedural — wie wir das machen — und es ist Prosa, keine Funktion. Kapitel 28. |
| A2A | Ein Protokoll, damit agents als Peers zusammenarbeiten | agent ↔ agent | Die andere Seite denkt, plant und hält State über eine lange Aufgabe hinweg, statt einen Call zu beantworten. |
| ACP | War ein separates agent-communication protocol | — | Es ist kein lebender Vergleich mehr. Siehe unten. |
Zwei davon verdienen je einen Satz, weil dort die Verwirrung tatsächlich lebt.
MCP gegenüber A2A ist keine Rivalität, und beide Spezifikationen sagen das. Die A2A-Dokumentation zieht die Linie danach, was am anderen Ende ist: MCP „defines how an AI agent interacts with and utilizes individual tools and resources, such as a database or an API“, wobei ein Tool „specific, often stateless, functions“ ausführt; A2A adressiert agents, „more autonomous systems“, die „reason, plan, use multiple tools, maintain state over longer interactions, and engage in complex, often multi-turn dialogues“. Die eigene Zusammenfassung ist der Satz, den man sich merken sollte: „A2A is about agents partnering on tasks, while MCP is more about agents using capabilities.“11 Die beiden verschachteln sich — eine Anwendung nutzt A2A, um andere agents zu erreichen, und jeder agent nutzt MCP, um seine eigenen Tools zu erreichen. Kapitel 25 zog diese Linie innerhalb eines Prozesses, zwischen dem Fragen eines sub-agent und dem Übergeben der Konversation; A2A zieht sie zwischen Organisationen.
MCP gegenüber ACP ist ein Vergleich mit einer veralteten Prämisse, und genau deshalb lohnt es sich, ihn zu beantworten. Das Agent Communication Protocol war ein separater offener Standard für agent-to-agent Messaging. Seine eigene Dokumentation beginnt inzwischen mit dem Hinweis: „ACP is now part of A2A under the Linux Foundation!“12 Die ehrliche Antwort auf „MCP oder ACP?“ im September 2026 ist, dass die Frage eine Option weniger hat, als die Seiten, die dafür ranken, suggerieren.
Und der Vergleich, nach dem die meisten fragen, mcp vs api, hat die am wenigsten interessante Antwort: MCP ist eine API. Was es hinzufügt, ist nicht Power, sondern Konventionen — eine feste Menge von Methodennamen, ein Discovery-Call, eine Kontrollhierarchie über die Primitive und ein Isolationsmodell. Du gibst die Freiheit auf, dein eigenes Interface zu designen, und bekommst jeden Host, der das Protokoll spricht, was der Trade ist, den jedes Protokoll je angeboten hat.
Wohin es als Nächstes geht
Link zum Abschnitt: Wohin es als Nächstes gehtDu kannst die Spezifikation jetzt ohne Übersetzer lesen, eine Ressource von einem Tool von einem prompt danach unterscheiden, wer darüber entscheidet, eine Request von Hand tippen, wenn eine Client-Bibliothek dich belügt, und jeden MCP-Artikel, den du liest, danach datieren, welche deprecated Features er noch als aktuell lehrt.
Was du noch nicht getan hast, ist, einen auszuliefern. Kapitel 27 schreibt denselben Server zweimal — TypeScript und Python, nebeneinander, weil MCP das eine wirklich zweisprachige Gebiet in diesem Kurs ist und die Zahlen das in beide Richtungen sagen. Es behandelt die beiden Live-Transporte richtig, den Inspector, Packaging und die Hälfte des Protokolls, die dieses Kapitel absichtlich ausgelassen hat: Authorization. Denn sobald dein Server remote ist und kein Subprozess auf deinem eigenen Laptop, wird der Client eines Fremden einen token präsentieren, und die Regel der Spezifikation dazu, was du damit tun darfst, ist ungewöhnlich strikt.
Das wirft die Frage auf, die das nächste Kapitel beantworten muss, und sie ist keine freundliche: Wenn ein token bei deinem Server ankommt und für die Audience von jemand anderem ausgestellt wurde, was genau hält dich davon ab, ihn weiterzuleiten?
Quellen und Methode
Link zum Abschnitt: Quellen und MethodeJedes Zitat, jeder Methodenname, jeder Error Code und jede Regel in diesem Kapitel wurde aus der Model Context Protocol Specification, Revision 2026-07-28, am 7. September 2026 gelesen. Jeder Trace wurde lokal auf Node 22 erzeugt: Der Toy-Kalenderserver hat 101 Zeilen ohne Dependencies, und der Referenzserver ist das unten genannte veröffentlichte npm-Package. Keine bezahlte API wurde aufgerufen, um dieses Kapitel zu schreiben — nichts hier braucht ein Modell, was selbst der Punkt ist.
Die Messungen: @modelcontextprotocol/server-everything@2026.8.31, veröffentlicht am 31. August 2026, gebaut auf @modelcontextprotocol/sdk@1.30.0, veröffentlicht am 27. Juli 2026 — einen Tag vor der Revision, die dieses Kapitel beschreibt. Es beantwortet server/discover mit -32601, negotiates 2025-11-25, wenn nach 2026-07-28 gefragt wird, und bedient tools/list ganz ohne Handshake. Sein Katalog umfasst 13 Tools in 7.663 Bytes; token Counts sind o200k_base via tiktoken, über name, description und inputSchema jeder Definition, also das, was ein Provider in deinen prompt rendert, nicht das Gewicht des JSON-RPC-Frames.
Anthropic, Code execution with MCP: building more efficient agents, 4. November 2025, ist die Quelle der 150.000-auf-2.000-Zahl, die in Kapitel 24 zitiert und verwendet wird und hier nur referenziert wird.
Referenzen
Link zum Abschnitt: Referenzen-
Specification,
modelcontextprotocol.io/specification/latest(redirecting to/2026-07-28), gelesen am 7. September 2026. Quelle des Vergleichs mit dem Language Server Protocol; der Aussage, dass die Spezifikation „based on the TypeScript schema inschema.ts“ ist; der Base-Protocol-Zusammenfassung („Stateless, self-contained requests“, „Per-request capability negotiation“); der Extension-Liste (Tasks, Skills over MCP, MCP Apps) und der Aussage, dass Extensions „are always opt-in and require explicit support from both client and server“; sowie der Security- und Trust-&-Safety-Principles, einschließlich „Hosts must obtain explicit user consent before invoking any tool“ und der Behandlung von Tool-Annotations als untrusted. ↩ ↩2 ↩3 -
Base Protocol,
modelcontextprotocol.io/specification/2026-07-28/basic. Quelle der JSON-RPC-Constraints (non-null id, keine id-Wiederverwendung, erforderlichesresultType); des Statelessness-Abschnitts und seiner Note, dass ein offener stdio-Prozess keine Session ist; der_meta-Reserved-Key-Tabelle und des Required/Optional-Status jedes Per-Request-Feldes; der-32602-Regel für ein fehlendes Pflichtfeld; derMissingRequiredClientCapability-Regel (-32021); und der Error-Code-Allocation-Policy. ↩ ↩2 ↩3 ↩4 ↩5 -
stdio transport,
modelcontextprotocol.io/specification/2026-07-28/basic/transports/stdio. Quelle der newline-delimited Framing-Regeln, derstdout-Purity-Anforderung, derstderr-Allowance und des Backward-Compatibility-Probes mit drei Outcomes — einschließlich der Warnung, dass manche legacy Server era-ambiguous Methods ohne Handshake verarbeiten, was die Messung in diesem Kapitel reproduziert. ↩ ↩2 ↩3 -
Transports overview,
modelcontextprotocol.io/specification/2026-07-28/basic/transports. Quelle des „a transport is a binding“-Framings und der Aussage, dass Server keine JSON-RPC-Requests initiieren und Clients keine JSON-RPC-Responses senden. ↩ ↩2 -
Discovery,
modelcontextprotocol.io/specification/2026-07-28/server/discover. Quelle des verpflichtenden Status vonserver/discover, der Form vonDiscoverResultund desinstructions-Feldes, beschrieben als „optional natural-language guidance for LLMs on how to use this server effectively“. ↩ -
Architecture,
modelcontextprotocol.io/specification/2026-07-28/architecture. Quelle der Host/Client/Server-Definitionen, der 1:1-Client-zu-Server-Regel, der vier Designprinzipien, von denen das Isolationsprinzip hier ohne seinen fünften Bullet „Host process enforces security boundaries“ zitiert wird, und des Capability-Negotiation-Abschnitts. ↩ ↩2 -
Elicitation,
.../client/elicitation, und Sampling,.../client/sampling. Quelle der zwei Elicitation-Modi und ihres eingeschränkten Schemas; des Verbots, Credentials über den Formularmodus anzufordern; der Sampling-Definition, ihrer human-in-the-loop-Anforderung und der daran hängenden Deprecation-Warnung. ↩ -
Key Changes,
modelcontextprotocol.io/specification/2026-07-28/changelog, und Feature lifecycle and deprecation policy,.../community/feature-lifecycle. Quelle jeder Zeile der Änderungstabelle: Entfernung von Sessions und desMcp-Session-Id-Headers (SEP-2567); Statelessness und Entfernung voninitialize(SEP-2575);server/discover(SEP-2575);subscriptions/listen(SEP-2575); Multi Round-Trip Requests undresultType(SEP-2322); Entfernung der Stream Resumability (SEP-2575); Deprecation von Roots, Sampling und Logging (SEP-2577); Reclassification von HTTP+SSE (SEP-2596); Deprecation von Dynamic Client Registration zugunsten von Client ID Metadata Documents; Error-Code-Renumbering; und das Zwölf-Monats-Deprecation-Fenster. ↩ ↩2 -
Tools,
modelcontextprotocol.io/specification/2026-07-28/server/tools, und Server Features,.../server. Quelle der oben reproduzierten Control-Hierarchy-Tabelle; dertools/list- undtools/call-Formen; derisError-Unterscheidung zwischen Protocol Errors und Tool Execution Errors; der Tool-Name-Regeln und der Namespace-Note, die „prefixing tool names with a server identifier“ empfiehlt; sowie der nicht-normativen „Stateful Tools“-Guidance zu expliziten Handles. ↩ -
Versioning,
modelcontextprotocol.io/specification/versioning. Quelle desYYYY-MM-DD-Schemas, der Draft/Current/Final-Revision-States, der Bestätigung, dass 2026-07-28 current ist, und der Per-Request-Negotiation-Regeln. Die SDK-Tier-Tabelle untermodelcontextprotocol.io/docs/sdklistet TypeScript, Python, C#, Go und Rust als Tier 1, Java und Ruby als Tier 2 und Swift, PHP und Kotlin als Tier 3. ↩ -
A2A Protocol, Version 1.0.0,
a2a-protocol.org— die Spezifikation und die Seite A2A and MCP: Relationship and Distinction, gelesen am 7. September 2026. Quelle der Tools-gegen-agents-Unterscheidung, der Aussage, dass die beiden Protokolle „address distinct but highly complementary needs“, und der Partnering/Using-Formulierung. ↩ -
Agent Communication Protocol,
agentcommunicationprotocol.dev, gelesen am 7. September 2026: „ACP is now part of A2A under the Linux Foundation!“, ein Banner, das über einer Spezifikation hinzugefügt wurde, die weiterhin vollständig ausgeliefert wird — Architecture, agent manifest, agent discovery, Message Structure, Stateful Agents, Run Lifecycle und die REST-Endpoint-Liste antworten alle weiterhin 200. Die Spezifikation ist nicht verschwunden; das Projekt ist es. ↩