MCP Went Stateless: What Breaks and How to Migrate
TechCurrent Staff•05:07 UTC•10 min read

The Model Context Protocol quietly runs a large slice of how AI agents touch the outside world, and on July 28 its maintainers rewrote its foundations. The `2026-07-28` spec revision, billed in the release candidate announcement as "the largest revision of the protocol since launch," makes MCP stateless, replaces every server-initiated request with a retry-based pattern called MRTR (Multi Round-Trip Requests), and puts Roots, Sampling, Logging, and Dynamic Client Registration on a formal deprecation clock with a minimum twelve-month window.
The official announcement and the normative changelog are dense, and the vendor posts that followed are platform-specific. If you maintain an MCP server or client, what you actually need is a triage list: what breaks the moment you adopt the new revision, what keeps working but has a deadline, and what survives untouched. That is this guide.
What "stateless" actually means here
Start with the headline change, because it is widely misread. The new revision removes protocol state, not application state.
Until now, every MCP connection began with an `initialize` handshake that negotiated versions and capabilities, and Streamable HTTP servers tracked clients through an `Mcp-Session-Id` header. According to the changelog, both are gone. Every request now carries its own context: the protocol version and client capabilities ride along in the `_meta` field of each request (`io.modelcontextprotocol/protocolVersion` and `io.modelcontextprotocol/clientCapabilities`), and a version mismatch returns a new `UnsupportedProtocolVersionError`. A new `server/discover` RPC, which servers are required to implement, lets clients probe versions and capabilities upfront when they want to.
Servers that genuinely need state across calls still have it, just explicitly: they mint their own handles and pass them as ordinary tool arguments. What they can no longer do is assume the protocol will remember anything between requests. Any coverage telling you "MCP has no state anymore" is flattening this. The state moved; it did not vanish.
A few adjacent removals follow from the same logic. SSE stream resumability and `Last-Event-ID` redelivery are gone: if a response stream breaks mid-request, the request is lost and the client must re-issue it with a new ID. The standalone HTTP GET endpoint and `resources/subscribe` are replaced by `subscriptions/listen`, a single long-lived POST response stream for change notifications that the client opts into. And `ping`, `logging/setLevel`, and `notifications/roots/list_changed` are removed outright, with log level now set per request via `_meta`.
What breaks immediately
These are hard breaking changes in `2026-07-28`. Adopt the revision and this code stops working:
| Removed | Replacement |
|---|---|
| `initialize` handshake, `Mcp-Session-Id`, protocol sessions | Per-request `_meta` context, `server/discover`, server-minted handles |
| Server-initiated `elicitation/create`, `sampling/createMessage`, `roots/list` | The MRTR pattern (see below) |
| HTTP GET endpoint, `resources/subscribe` / `unsubscribe` | `subscriptions/listen` |
| `ping`, `logging/setLevel`, `notifications/roots/list_changed` | Per-request `io.modelcontextprotocol/logLevel` in `_meta` |
| SSE resumability, `Last-Event-ID` | Client re-issues the request with a new ID |
| `notifications/elicitation/complete`, `elicitationId` | Correlation via MRTR `requestState` |
| Bare Streamable HTTP POSTs | Required `Mcp-Method` and `Mcp-Name` headers (SEP-2243) |
That last row is easy to miss. Streamable HTTP POSTs now must carry `Mcp-Method` and `Mcp-Name` headers so gateways, WAFs, and rate limiters can route requests without parsing JSON bodies. If your infrastructure strips or rewrites headers, check it before you flip the version.
Deprecated with a clock: Roots, Sampling, Logging, DCR, HTTP+SSE
The revision also introduces MCP's first formal feature lifecycle (SEP-2596): Active, then Deprecated, then Removed, with a minimum twelve-month deprecation window. According to the deprecated features registry, "earliest removal" means a feature becomes eligible for removal, not that removal happens on that date. The decision belongs to the core maintainers at release time. Nothing has actually been removed under the policy yet.
| Feature | Deprecated | Earliest removal | Suggested migration (per the registry) |
|---|---|---|---|
| Roots | 2026-07-28 | First revision on or after 2027-07-28 | Pass directories and files via tool parameters, resource URIs, or server configuration |
| Sampling | 2026-07-28 | First revision on or after 2027-07-28 | Integrate directly with LLM provider APIs |
| Logging | 2026-07-28 | First revision on or after 2027-07-28 | Log to stderr (stdio) or use OpenTelemetry |
| Dynamic Client Registration | 2026-07-28 | First revision on or after 2027-07-28 | Client ID Metadata Documents (CIMD) |
| HTTP+SSE transport | 2025-03-26, reclassified 2026-07-28 | Three months after SEP-2596 reaches Final | Streamable HTTP |
Everything in this table keeps working today. The point of the window is that you migrate on your schedule, not in a panic. But note the odd one out: HTTP+SSE, the original remote transport, has already been deprecated for over a year, so its clock is shorter and less precisely dated than the others. If you are still on HTTP+SSE, treat that migration as the urgent one.
One nuance the tables cannot capture: Sampling is simultaneously a deprecated client feature and a perfectly legal request type inside the new MRTR pattern during the window. The spec's suggested end state is that servers needing LLM completions call provider APIs directly, but sampling requests have not disappeared from the wire yet.
MRTR: how server-initiated flows work now
The deepest architectural change is what happened to server-initiated requests. Under previous revisions, a server could reach back over an open stream and ask the client for something: elicit input from the user, request an LLM completion, or list the client's roots. In a stateless world there is no open stream to reach back over, so the MRTR spec (SEP-2322) inverts the flow. The spec does not soften this:
The previous pattern of server-initiated requests is no longer supported. This is a breaking change.
The new flow looks like this:
1. The client sends a normal request, say `tools/call`. 2. Instead of a final answer, the server returns a result whose `resultType` is `"input_required"`, carrying an `inputRequests` map (the questions it needs answered: elicitation, sampling, or roots requests, each under a server-assigned key) and an opaque `requestState` string. Each field is optional, but every such result must include at least one of the two. 3. The client gathers the answers, then retries the original request with a new JSON-RPC ID, attaching `inputResponses` and echoing `requestState` back unchanged. 4. The server, holding no memory of the first round trip, reconstructs everything it needs from `requestState` and completes the call.
Every result in the new revision carries a required `resultType` of either `"complete"` or `"input_required"`, and clients must treat results from older servers that omit the field as complete. `InputRequiredResult` is only legal on `tools/call`, `prompts/get`, and `resources/read`; servers must not send it anywhere else. Servers also must not send input requests the client has not declared capability support for, must not assume the client will ever retry, and should respond to a retry with missing information by issuing a fresh `InputRequiredResult` rather than an error.
The requestState security model
Migrating to MRTR is not just a mechanical rewrite, because `requestState` moves state onto the wire, and that creates a new security surface. The client treats the blob as opaque, but the spec is blunt about the server's obligations: it must treat `requestState` as attacker-controlled input, and if the blob influences authorization, resource access, or business logic, the server must integrity-protect it with an HMAC or AEAD and reject anything that fails verification. The spec further recommends embedding the authenticated principal, a short TTL, and an identifier tying the state to its originating request to bound replay, with single-use invariants enforced server-side.
Practical translation for your migration: audit every place your server currently initiates an elicitation, sampling, or roots request mid-operation. Each one becomes a checkpoint where you serialize enough state to resume, sign or encrypt it, hand it to the client, and validate it on the way back in. If you were keeping that state in server memory before, the code that assumed it could not be tampered with now needs to assume the opposite.
Why serverless platforms raced to publish
The payoff for all this disruption is deployment. A protocol with sessions and long-lived streams forces sticky routing, session stores, and always-on processes. A stateless protocol runs anywhere a plain HTTP handler runs.
According to Cloudflare's mcp-v2 post, its MCP servers now run stateless on Workers through `createMcpHandler` in its Agents SDK: "MCP servers can now run in just a Worker, no stateful infrastructure needed." Durable Objects, previously required to speak the protocol on Workers, are now optional and reserved for actual application state. The post features Sentry and Linear as early production adopters, and says Cloudflare's Code Mode MCP Server has "scaled up to thousands of requests per second and served billions of tool calls" (Cloudflare's own figures, which TechCurrent has not independently verified).
Sentry co-founder and CPO David Cramer, quoted in the Cloudflare post, said Sentry "went live with this new one before the 7-28 spec was even finalized, and it didn't break prod," adding that the new spec "cleans up a bunch of the nonsense around auth and tools, which is exactly what I wanted."
Google's developers blog makes the same case from the operations side: the change eliminates session pinning and sticky routing entirely. Plain round-robin load balancing works, Redis-backed session storage becomes unnecessary, and MCP servers can deploy on Cloud Run or Cloud Functions with scale-to-zero, with pod restarts and autoscaling invisible to clients. Google also states that the GitHub MCP Server has already upgraded and dropped its Redis session storage, though that is Google's characterization of a third party's infrastructure and TechCurrent has not confirmed it with GitHub.
A useful compatibility note from Cloudflare's post: its `/mcp` endpoints accept both the new protocol and stateless-style requests from 2025-era Streamable HTTP clients, so most existing clients reconnect without configuration changes. Backward compatibility at the edges is doing a lot of work to make this transition survivable.
A practical migration checklist
Pulling the threads together, here is the order of operations for a server maintainer:
1. Still on HTTP+SSE? Move to Streamable HTTP first. Its removal clock is the shortest and vaguest of the lot. 2. Delete session assumptions. Anything keyed on `Mcp-Session-Id` or populated during `initialize` must move into per-request `_meta` handling or explicit server-minted handles. 3. Implement `server/discover`. It is mandatory in the new revision. 4. Add `Mcp-Method` and `Mcp-Name` headers on the client side, and make sure your gateway does not eat them on the server side. 5. Rewrite server-initiated flows as MRTR checkpoints, with signed `requestState` wherever the blob touches authorization or business logic. 6. Return `ttlMs` and `cacheScope` on list results (SEP-2549 makes them required on `tools/list` and friends), and return tools in deterministic order; the spec notes this improves LLM prompt cache hits, an economics lever we ran the numbers on for agent loops. 7. Plan the deprecation-window migrations: roots to tool parameters or configuration, sampling to direct provider APIs, logging to stderr or OpenTelemetry, DCR to CIMD. 8. Watch the error codes. Resource-not-found moved from `-32002` to `-32602`, and the range `-32020` to `-32099` is now reserved for spec-defined errors.
The SDK situation softens the landing. According to the SDK betas post and the release announcement, the four tier-1 SDKs (TypeScript, Python, Go, C#) shipped updates alongside the spec, with Rust in beta. Python v2 renames `FastMCP` to `MCPServer` but keeps the decorator API and can fall back to `initialize` for older peers. TypeScript v2 splits into separate `@modelcontextprotocol/server` and `@modelcontextprotocol/client` packages, goes ESM-only, and ships a codemod (`npx @modelcontextprotocol/codemod@beta v1-to-v2 .`). The Go SDK (v1.7.0-pre.1 at beta) keeps its module path and makes stateless HTTP opt-in via `StreamableHTTPOptions.Stateless = true`, the gentlest on-ramp of the four. C# v2 keeps v1 APIs working and marks roots, sampling, and logging `[Obsolete]`. Python and TypeScript v1.x get critical updates for at least six months after v2 stabilizes, and library publishers are told to pin `mcp>=1.27,<2` in the meantime.
What happens next
If the removal of server-initiated requests feels like a loss, the maintainers apparently agree. The MCP roadmap published August 22 by lead maintainers David Soria Parra and Den Delimarsky puts "agentic messaging primitives" at the top of the list: server-initiated events are coming back, redesigned as webhooks and channels rather than open streams, because "modern agentic workloads no longer fit the standard request-and-response pattern." That is not a reversal of the stateless revision; it is the same push functionality rebuilt as opt-in primitives that do not require protocol sessions.
The rest of the roadmap points the same direction as this revision: standardizing on Streamable HTTP everywhere, with local servers eventually speaking Streamable HTTP over stdio rather than stdio's own message framing (stdio itself is not deprecated today); enterprise identity work around DPoP, workload identity, and token exchange; progressive tool discovery for servers with hundreds of tools; and SDK developer experience. No date has been announced for the next spec release.
Which makes the current moment unusually legible for a fast-moving protocol. The breaking changes are enumerated, the deprecations are dated, the SDKs have migration paths, and the earliest any deprecated feature can be removed is a spec revision on or after July 28, 2027. Twelve months sounds like a long time until you remember how long infrastructure migrations actually take. The clock is running; the good news is that, for once, you can read exactly what it says.
“The previous pattern of server-initiated requests is no longer supported. This is a breaking change.”
Reporting by TechCurrent Staff · TechCurrent
