AI Systems

Model Context Protocol Goes to Production: What the 2026-07-28 Spec Change Actually Fixes

MCP's 2026-07-28 revision removes sessions and the initialize handshake. The gain is horizontal scaling; the cost is resumability and a real migration.

By Institute for Joint Cognition & AI · 5 min · 29 September 2026

Rows of server racks in a data centre aisle with status lights

The 2026-07-28 Model Context Protocol revision removes protocol-level sessions, the initialize/notifications/initialized handshake, and the Mcp-Session-Id header from the Streamable HTTP transport. Every request is now self-contained, carrying its protocol version and client capabilities in _meta rather than negotiating them once at connection setup.

This is the largest structural change the protocol has made, and it is worth separating what it fixes from what it costs. Both are substantial.

The problem sessions created

A session-bound protocol requires that every request in a conversation reach the same server instance, or that instances share state. Neither is free.

Sticky routing means a load balancer must parse or track session identity, and it means instance failure destroys in-flight work. Shared state means an external store on the hot path of every request, with the latency and availability characteristics that implies. Both constrain horizontal scaling in ways that are familiar from a decade of web architecture — and that the rest of the web mostly solved by going stateless.

The maintainers describe statelessness as one of the most requested features from developers seeking better reliability and scalability. The stated payoff: any request can land on any instance behind a simple round-robin balancer, with no shared storage requirement, and gateways can route on headers without parsing JSON bodies.

That last point is the underrated one. The revision requires standard Mcp-Method and Mcp-Name headers on Streamable HTTP POST requests. An infrastructure layer that can route, rate-limit, and authorize on headers alone does not need to understand MCP semantics at all, which is what allows existing HTTP infrastructure to carry agent traffic without modification.

What replaced the removed pieces

The removals are not simple deletions; most have a designated successor.

Version negotiation moves to server/discover, a new RPC that servers MUST implement to advertise supported protocol versions, capabilities, and identity. Clients MAY call it before any other request, or use it as a backward-compatibility probe on STDIO. Version mismatches now return UnsupportedProtocolVersionError.

Cross-call state becomes an application concern rather than a protocol one. Servers needing it use explicit, server-minted handles passed as ordinary tool arguments. This is the honest version of what session state was doing, and making it explicit means the server author decides the lifetime and scope rather than inheriting the transport’s.

Server-initiated requests — roots/list, sampling/createMessage, elicitation/create — are replaced by the Multi Round-Trip Requests pattern. A server returns an InputRequiredResult with resultType: "input_required" and an inputRequests field; the client retries the original request carrying inputResponses. All results now require a resultType field, and clients MUST treat results from earlier-protocol servers that omit it as "complete".

MRTR is the change with the most design consequence. Inverting server-initiated requests into client retries is what makes a request self-contained, but it moves correlation work to the server, which must encode its own identifier in requestState to track an elicitation across retries. The notifications/elicitation/complete notification and elicitationId field, both introduced in 2025-11-25, are gone because a server-initiated completion signal no longer fits.

Change notifications consolidate into subscriptions/listen, a single long-lived POST-response stream replacing the HTTP GET endpoint and resources/subscribe/unsubscribe. Clients opt into specific notification types. Request-scoped notifications such as notifications/progress and notifications/message continue to flow on the response stream of the request they relate to.

Caching gains real primitives. ttlMs and cacheScope are now required on results from the list methods and resources/read, via a CacheableResult interface. Because list endpoints no longer vary per connection, their results are genuinely cacheable — including by shared intermediaries when cacheScope is "public". Servers SHOULD also return tools in deterministic order, which improves client-side caching and LLM prompt cache hit rates.

The cost that is not being advertised

Removing SSE stream resumability is the change most likely to surprise an operator. The Last-Event-ID header and SSE event IDs are gone from the Streamable HTTP transport. A broken response stream loses the in-flight request, and clients MUST re-issue it as a new request with a new request ID.

For short tool calls this is unremarkable. For long-running operations over unreliable networks it is a meaningful regression, and it shifts the burden onto the tasks extension — which itself moved out of the core protocol into io.modelcontextprotocol/tasks, replacing the blocking tasks/result with polling via tasks/get, adding tasks/update for client-to-server input, and removing tasks/list.

The net effect is that durable long-running work is no longer something the transport helps with. It is an extension you adopt deliberately.

There is also a deprecation slate that will affect existing integrations: Roots, Sampling, and Logging are all deprecated, with suggested migrations to tool parameters or resource URIs, direct LLM provider APIs, and stderr or OpenTelemetry respectively. ping and logging/setLevel are removed outright, with log level now set per-request via io.modelcontextprotocol/logLevel in _meta. HTTP+SSE transport is reclassified as deprecated. OAuth Dynamic Client Registration is deprecated in favour of Client ID Metadata Documents.

The governance change may outlast the technical one

Alongside the protocol work, the project adopted a feature lifecycle policy defining Active, Deprecated, and Removed states, a minimum twelve-month deprecation window, and a registry tracking every deprecated feature.

For anyone deciding whether to build production infrastructure on MCP, this is arguably more consequential than statelessness. A protocol that removed sessions and the initialize handshake in a single revision is one that can break integrations; a twelve-month minimum window and a published registry make that risk bounded and visible. The error code allocation policy — reserving -32020 to -32099 for the specification while grandfathering existing SDK usage in -32000 to -32019 — points at the same maturation.

Where this leaves an implementer

Adoption scale is real: the maintainers cite close to half a billion monthly downloads across Tier 1 SDKs, with the TypeScript and Python SDKs each past a billion cumulative. All four Tier 1 SDKs — TypeScript, Python, Go, and C# — supported the revision at release.

There is no mandatory migration deadline, and the twelve-month deprecation window applies to the deprecated features rather than to the previous protocol revision. The migration cost falls hardest on implementations that depended on session identifiers, which now need explicit server-minted handles, and on anything relying on stream resumability, which needs the tasks extension or application-level retry.

The reasonable sequencing is to move to the new SDKs first, replace session dependence with explicit handles, and treat resumability as a separate design decision rather than assuming the transport still provides it.