Zustandsloses MCP: Was Server mit der Revision 2026-07-28 ändern müssen

Makroaufnahme einer Festplatte mit Magnetscheibe und Schreib-Lesearm, metallische Reflexe
Deine Reaktion:

„The 2026-07-28 revision of the Model Context Protocol took out the two things most servers were built around: the initialize handshake and the session.“ So beginnt ein Praxisbericht der Redaktion von ImportStatic über die aktuelle Fassung des Model Context Protocol, die im Juli als größtes Update des Protokolls angekündigt wurde. Wer in den vergangenen zwei Jahren einen MCP-Server gebaut hat, baute um zwei Dinge herum: den initialize-Handshake am Anfang und eine Session-ID, die an jeder weiteren Anfrage hing. Die Revision vom 28. Juli 2026 streicht beides. Übrig bleibt eine Spezifikation, in der jede HTTP-Anfrage alles mitbringen muss, was zu ihrer Beantwortung nötig ist.

Ein Amtsschalter, an dem niemand dich kennt, kommt der Sache näher. Früher bekamst du eine Vorgangsnummer, und jeder spätere Besuch klappte nur, wenn du an den Schalter zurückkamst, an dem deine Akte lag. Jetzt legst du bei jedem Besuch den vollständigen Vorgang auf die Theke, und es ist gleichgültig, wer Dienst hat. Daraus folgt fast jede Änderung für Server. Der Bericht ist mit Go 1.27.1 und go-sdk v1.8.0 entstanden, die Beispiele stammen aus zwei lokalen Handlern. ImportStatic entwirft seine Artikel nach eigener Angabe mit KI-Unterstützung, prüft sie gegen die Primärquellen und führt jedes Codebeispiel vor der Veröffentlichung aus.

Zustandsloses MCP: jede Anfrage trägt ihren eigenen Kontext

Bis zur Protokollversion 2025-11-25 begann jede Verbindung mit einem Handshake. Der Server antwortete auf ein initialize-POST mit Version, Fähigkeiten und einer Session-ID. Der Client schickte danach ein notifications/initialized hinterher und hängte die Session-ID an jede weitere Anfrage. Folge: Jede dieser Anfragen musste eine Instanz erreichen, die die Sitzung kannte. Das erzwang Sticky Sessions oder einen geteilten Sitzungsspeicher.

Die Revision 2026-07-28 entfernt den Handshake und die Session-ID. Protokollversion, Client-Identität und Client-Fähigkeiten reisen stattdessen in einem _meta-Block, den jede Anfrage mitführt. Die erste HTTP-Anfrage eines Clients darf damit direkt ein tools/call sein, und jede Instanz hinter einem Load Balancer kann sie beantworten. Für Serverautoren zerfällt die Arbeit in drei Teile: nicht mehr auf eine Sitzung bauen, die neuen Pflichtfelder senden, und jedes Tool umschreiben, das den Client mitten im Aufruf etwas fragt.

Auf dem Rückweg sind zwei Felder neu. Der Server identifiziert sich in _meta jedes einzelnen Ergebnisses, weil es keinen Handshake mehr gibt, in dem er das tun könnte. Und jedes Ergebnis trägt ein Pflichtfeld resultType, das im Normalfall complete lautet. Der zweite mögliche Wert kommt weiter unten vor, wenn ein Tool nachfragen muss.

MCP ohne Session: was nicht mehr in einer Sitzung liegen darf

Ob dein Server betroffen ist, entscheidet eine Frage: Was liegt bei mir zwischen zwei Aufrufen im Speicher? Ein Warenkorb, ein Dateihandle, ein Fortschrittszähler, ein angemeldeter Nutzer — all das hatte bisher seinen Platz in der Sitzung. Der fällt weg, damit auch die Annahme, dass der nächste Aufruf auf demselben Prozess landet. Die Spezifikation antwortet knapp: Solche Zustände werden zu expliziten Handles, die der Server ausstellt und die als Tool-Argumente durch den Dialog wandern.

Eine cart_id, die ein Tool zurückgibt und das nächste als Parameter verlangt, funktioniert auf jeder Instanz. Sie ist außerdem für das Modell sichtbar und damit Teil des Kontexts. Für alles, was nur für die Dauer eines Aufrufs gebraucht wird, gibt es den zweiten Ablageort requestState, dazu später mehr bei den Rückfragen. Wer Bestandsaufnahme machen will, fängt beim Code an, der heute Dinge in eine Session schreibt, nicht beim Protokollcode.

Mcp-Method und Mcp-Name: Header, die gegen den Body geprüft werden

Die Anfrage wiederholt Methode und Namen des Ziels in den HTTP-Headern Mcp-Method und Mcp-Name. Die Streamable-HTTP-Seite der Spezifikation macht sie verpflichtend, damit Gateways und Rate Limiter auf Basis der Header routen können, ohne den JSON-Body zu parsen. Wichtiger ist die zweite Regel: Der Server muss eine Anfrage ablehnen, bei der Header und Body sich widersprechen. Sonst könnte ein Proxy ein harmloses Tool autorisieren, während der Server ein anderes ausführt.

In den Tests von ImportStatic erzwingt das Go SDK diese Prüfung. Ein Request mit Mcp-Name: deploy und einem Body, der add aufruft, wurde mit dem Fehler -32020 abgewiesen, ein Request ohne Mcp-Method ebenso. Wer einen handgeschriebenen Client pflegt, sollte das zuerst nachrüsten: Eine Anfrage nach 2026-07-28 wird ohne diese Header abgelehnt, so korrekt ihr Body auch sein mag. Mcp-Name ist für tools/call, resources/read und prompts/get verpflichtend und trägt den Wert von params.name beziehungsweise params.uri.

Dieselbe Seite der Spezifikation streicht den eigenständigen GET-Stream und die fortsetzbaren Streams. Ein GET auf den stateless Handler im Test antwortete mit 405 Method Not Allowed. Eine abgerissene Antwortverbindung bedeutet jetzt, dass die laufende Anfrage verloren ist und der Client sie mit einer neuen Request-ID erneut senden muss. Änderungsbenachrichtigungen, die früher über den GET-Stream eintrafen, kommen nun als Antwort auf eine subscriptions/listen-Anfrage, die der Client öffnet, wenn er sie braucht.

Versionfehler -32022, server/discover und die neuen Cache-Felder

Es gibt keinen Aushandlungs-Handshake mehr. Jede Anfrage trägt ihre Protokollversion, und der Server akzeptiert oder verwirft sie einzeln. Ein Server, der die gewünschte Version nicht implementiert, antwortet mit dem Fehler -32022 und der Liste der unterstützten Versionen. Ein Client, der das vorab wissen will, ruft server/discover auf; für Server ist der Aufruf Pflicht, für Clients optional.

Neu und verpflichtend sind ttlMs und cacheScope in den Ergebnissen von tools/list, prompts/list, resources/list, resources/templates/list und resources/read. Sie sagen dem Client, wie lange er die Antwort wiederverwenden darf und ob ein gemeinsamer Zwischenspeicher das ebenfalls darf. Der Standardwert des SDK ist 0, also sofort veraltet; ein Client zieht daraus keinen Nutzen. Die Paketdokumentation beschreibt einen SetCacheable-Hook in den ServerOptions, über den sich echte Werte setzen lassen. Eine Toolliste, die sich nur bei einem Deployment ändert, verträgt eine lange TTL. Außerdem verlangt der Changelog, Tools in einer festen Reihenfolge auszuliefern, damit Clients und die Prompt-Caches der Sprachmodelle die Liste wiederverwenden können.

MCP Server auf stateless umstellen: eine Option und der Preis für alte Clients

Im Go-SDK hängt alles an einer Option. Wer der MCP-Server-Anleitung von ImportStatic gefolgt ist, hat seinen HTTP-Handler mit nil-Optionen erzeugt, und ein solcher Handler hält weiterhin Sitzungen. In v1.8.0 spricht ein sitzungshaltender Handler kein 2026-07-28: Ein moderner tools/call-Aufruf kam als Fehler zurück. SDK-Clients bemerken das kaum, weil sie auf den Handshake zurückfallen, aber man bleibt auf der alten Protokollversion stehen.

Entscheidend ist Stateless: true in den Streamable-HTTP-Optionen. Mit dieser Option handelte der SDK-Client 2026-07-28 aus, ohne sie 2025-11-25 — bei identischem Tool-Code. Der stateless Handler sperrt alte Clients nicht aus. Er beantwortete ein klassisches initialize weiterhin mit der Version 2025-11-25 und ohne Mcp-Session-Id, und er beantwortete auch ein 2025-11-25-tools/call, dem kein Handshake vorausging. Die Kompatibilitätsmatrix der Spezifikation ist bei der Alternative eindeutig: Ein alter Client gegen einen rein modernen Server scheitert, und alte Clients haben keine Möglichkeit, sich nach vorn zu hangeln.

input_required, signierter requestState und die Reihenfolge der Umstellung

Der unangenehmste Teil betrifft Tools, die den Client mitten im Aufruf etwas fragen. Elicitation, Sampling und Roots waren Anfragen des Servers an den Client, während ein Tool-Aufruf offen war. Das setzt einen offen gehaltenen Stream und eine Instanz voraus, die den Aufruf erinnert — beides gibt es nicht mehr. Mehrfach-Roundtrips drehen die Richtung um: Der Server beendet den Aufruf mit einem Ergebnis, das mitteilt, was er braucht, und der Client wiederholt den Aufruf mit den Antworten. Erlaubt ist das nur für tools/call, prompts/get und resources/read.

Konkret liefert der Handler beim ersten Aufruf InputRequests, das Ergebnis trägt resultType: input_required und eine Map mit den offenen Punkten. Der zweite Aufruf ist ein neues tools/call mit neuer ID, denselben Argumenten und zwei zusätzlichen Parametern: inputResponses und, falls der Server welchen mitgeschickt hat, der undurchsichtige requestState. Dort liegt das Gedächtnis des Servers, und weil es über den Client zurückkommt, gilt es laut Spezifikation als angreiferkontrolliert. Beeinflusst es Autorisierung oder Geschäftslogik, muss es integritätsgeschützt sein; empfohlen sind eine Bindung an den Aufrufer, eine kurze Gültigkeit und die Herkunftsanfrage. Das Beispiel signiert mit HMAC-SHA256 über Tool, Dienstnamen und Ablaufzeit, mit einem Schlüssel, den alle Instanzen teilen. Als die Redaktion den State des billing-Aufrufs in einen payments-Aufruf schob, antwortete der Server mit -32602 invalid requestState. Und weil all das den State nicht einmalig macht, braucht eine echte Einmalaktion weiterhin eine serverseitige Prüfung.

Den Retry-Loop muss niemand selbst im Client bauen. Der SDK-Client erledigte beide Roundtrips in einem einzigen CallTool und rief dazwischen den ElicitationHandler auf. In v1.8.0 bricht die Schleife nach zehn Runden ab, und MultiRoundTripOptions.Disabled schaltet sie ab, wenn man die Wiederholungen selbst fahren will. Der Haken liegt bei alten Clients auf dem stateless Handler: Ein auf 2025-11-25 festgelegter Client bekam die Meldung, der Client unterstütze kein Elicitation. Der Grund steht in der SDK-Dokumentation — ein stateless Handler benutzt pro Anfrage eine temporäre Sitzung mit Standard-Initialisierungsparametern, die Fähigkeiten aus dem initialize des alten Clients sind beim Ausführen des Tools also längst verschwunden. Schlichte Tools funktionieren für solche Clients weiter, Tools mit Rückfrage nicht.

Für die Umstellung ergibt sich daraus eine Reihenfolge. Zuerst suchst du alles, was dein Server zwischen zwei Aufrufen hält, und verschiebst es in explizite Handles als Tool-Argumente oder, für die Dauer eines Aufrufs, in requestState. Danach schreibst du die Tools um, die Elicitation, Sampling oder Roots mitten im Aufruf verwenden, und signierst jeden State, den du hinausgibst. Dann hebst du das SDK an und aktivierst den stateless Handler, in Go mit go-sdk ab v1.7.0 und Stateless: true. Falls alte Clients noch eine Rolle spielen und deine Tools von ihnen Eingaben brauchen, bleibt ein sitzungshaltender Endpunkt neben dem stateless einen bestehen, bis sie nachgezogen sind. In handgeschriebenen Clients kommen _meta, MCP-Protocol-Version, Mcp-Method und Mcp-Name dazu, ein fehlendes resultType von einem älteren Server wird als complete behandelt, und ein -32022 wird mit einer Version aus supported wiederholt. Zwei Kleinigkeiten am Rande: Ein nicht gefundenes Resource meldet jetzt -32602 statt -32002, und eine unbekannte Methode über HTTP ist ein 404 mit JSON-RPC-Body und -32601.

Zum Schluss die Deprecations. Roots, Sampling und Logging funktionieren weiter, und die Lebenszyklus-Politik dieser Revision garantiert abgekündigten Merkmalen mindestens zwölf Monate. Neue Implementierungen sollen sie laut Changelog trotzdem nicht mehr einbauen. Vorgeschlagen sind Tool-Parameter oder Resource-URIs statt Roots, ein direkter Aufruf des eigenen LLM-Anbieters statt Sampling und stderr oder OpenTelemetry statt Protokoll-Logging. ping und logging/setLevel sind dagegen ersatzlos entfernt, ein Keepalive auf Basis von ping muss beim Wechsel weichen. Überall gilt dasselbe Muster: Zustand und Nebenwirkungen verlassen den verborgenen Kanal und werden zu sichtbaren, prüfbaren Teilen des Aufrufs. Wer seinen MCP-Server auf stateless umstellen will, sollte das nicht als Protokollwechsel lesen, sondern als Frage, wo seine Daten künftig liegen — beim Client, im Aufruf oder in einem signierten Handle.

Quelle: importstatic.com

Deine Reaktion:
Artikel teilen:
Krötzsch-Check0 — 100
Fakten 88
Relevanz 85
Hype 18
Einschätzung 78
Redaktion 50 Stand 50 · noch keine Stimmen
Ist das Hype?
Sebastian Krötzsch
Autor

Sebastian Krötzsch

Sebastian Krötzsch schreibt auf sebask.de über Künstliche Intelligenz, Automatisierung, digitale Systeme und die Frage, was davon im Alltag wirklich nützlich ist. Ohne Buzzword-Nebel, dafür mit klarem Blick auf Praxis, Tools und echte Wirkung.