On 28 July 2026, the Model Context Protocol published its most consequential revision since the protocol became an industry default. The headline is easy to state and hard to overstate: MCP is now stateless.

Protocol-level sessions are gone. The initialize handshake is gone. The Mcp-Session-Id header is gone. SSE stream resumability is gone. Three core features — Roots, Sampling, and Logging — are formally deprecated. Server-initiated requests have been replaced by an entirely different interaction pattern.

If you have MCP servers in production, this is not a version bump you absorb with a dependency update. If you run an API gateway in front of those servers, however, it is the release you have been waiting for.

MCP 2026-07-28: The Stateless Rewrite
Switch between the 2025-11-25 and 2026-07-28 revisions to compare how a single tool call travels through each, then review everything removed, added, and deprecated.

What actually changed

The changes cluster into four themes. It is worth separating them, because they have very different blast radii.

1. Statelessness: the connection no longer carries meaning

Previously, an MCP interaction opened with initialize, received notifications/initialized, and established a session identified by Mcp-Session-Id. Everything after that was interpreted in the context of that session. List endpoints could legitimately return different results per connection.

That model is gone. Every request now carries its own context in _meta:

  • io.modelcontextprotocol/protocolVersion — the version this request speaks
  • io.modelcontextprotocol/clientCapabilities — what the client supports
  • io.modelcontextprotocol/clientInfo — who the client is (SHOULD be sent on every request)

Servers reciprocate by identifying themselves in each result’s _meta via io.modelcontextprotocol/serverInfo. A version mismatch returns UnsupportedProtocolVersionError.

Critically, tools/list, resources/list, and prompts/list no longer vary per connection. A server that needs cross-call state must now mint an explicit handle and pass it back as an ordinary tool argument. State becomes application data, not protocol plumbing.

A new RPC, server/discover, replaces the handshake’s discovery role. Servers MUST implement it to advertise supported protocol versions, capabilities, and identity. Clients MAY call it before anything else for up-front version negotiation, or use it as a backward-compatibility probe over STDIO.

2. Notifications and streaming were restructured

The HTTP GET endpoint and the resources/subscribe/resources/unsubscribe pair are replaced by a single method: subscriptions/listen. It opens one long-lived POST-response stream, and clients opt in explicitly to the notification types they want — toolsListChanged, promptsListChanged, resourcesListChanged, resourceSubscriptions. The server acknowledges and tags each notification with io.modelcontextprotocol/subscriptionId.

Request-scoped notifications behave differently and this distinction matters in implementation: notifications/progress and notifications/message continue to flow on the response stream of the request they belong to, not on the subscriptions/listen stream.

SSE resumability is also gone. The Last-Event-ID header and SSE event IDs have been removed from Streamable HTTP. When a response stream breaks, the in-flight request is lost, and the client MUST re-issue it as a new request with a new request ID. There is no message redelivery.

Three methods were removed outright: ping, logging/setLevel, and notifications/roots/list_changed. Log level is now set per-request via io.modelcontextprotocol/logLevel in _meta, and servers MUST NOT emit notifications/message for requests that did not include that field.

3. Multi Round-Trip Requests replace server-initiated calls

This is the subtlest change and the one most likely to break mental models.

Previously, a server that needed something from the client mid-request — filesystem roots, an LLM completion via sampling, a user decision via elicitation — issued a server-initiated request back down the connection. That pattern is gone, because in a stateless protocol there is no connection to send it down.

In its place is the Multi Round-Trip Requests (MRTR) pattern. When a server needs more information, it returns an InputRequiredResult with resultType: "input_required", and its inputRequests field carries what it needs. The client then retries the original request, supplying inputResponses.

Supporting this, all results now carry a required resultType field: "complete" for ordinary results, "input_required" for MRTR interim results. Clients MUST treat results from earlier-protocol servers that omit the field as "complete".

MRTR also explains a removal that would otherwise look arbitrary: notifications/elicitation/complete and the elicitationId field are gone. Under MRTR, the client learns the outcome of an out-of-band interaction by retrying, so a server-initiated completion signal no longer fits the protocol. Servers that need to correlate an elicitation across retries encode their own identifier in requestState.

4. Extensions, caching, and a real deprecation policy

Tasks — experimental in the previous revision — moved out of the core protocol into an official extension, io.modelcontextprotocol/tasks. The redesign replaces the blocking tasks/result method with polling via tasks/get, adds tasks/update for client-to-server input, removes tasks/list, and lets servers return task handles unsolicited without per-request opt-in.

The extensions field now appears on both ClientCapabilities and ServerCapabilities, giving optional capabilities a first-class home outside the core spec.

For caching, a new CacheableResult interface requires ttlMs and cacheScope on results from tools/list, prompts/list, resources/list, resources/read, and resources/templates/list. ttlMs is a freshness hint in milliseconds; cacheScope ("public" or "private") controls whether shared intermediaries may cache the response. Servers SHOULD also return tools from tools/list in a deterministic order — explicitly to improve client-side caching and LLM prompt cache hit rates.

Streamable HTTP POST requests now require the standard Mcp-Method and Mcp-Name headers, and custom headers can be sourced from tool parameters via x-mcp-header.

Finally, the project adopted a formal feature lifecycle and deprecation policy defining Active, Deprecated, and Removed states, with a minimum twelve-month deprecation window and a public registry of deprecated features.

What is deprecated, and what to do instead

Deprecated features remain functional during the window, but new implementations should not adopt them.

DeprecatedSuggested migration
RootsPass directories or files via tool parameters, resource URIs, or server configuration
SamplingIntegrate directly with LLM provider APIs
LoggingLog to stderr (stdio), or use OpenTelemetry
HTTP+SSE transportMigrate to Streamable HTTP
includeContext values "thisServer" / "allServers"Omit the field or use "none"
OAuth 2.0 Dynamic Client Registration (RFC 7591)Client ID Metadata Documents

The Roots, Sampling, and Logging deprecations deserve attention because they signal a philosophical position: MCP is narrowing to tool access and getting out of the business of being an application framework. Sampling in particular was always awkward — it asked the tool server to borrow the client’s model. Removing it clarifies the boundary. As I have argued before, the client is the right place for a great deal of this responsibility, and this revision moves decisively in that direction.

The authorization changes are quieter but material

Four changes tighten the authorization story, and enterprises should read them as a set:

  1. Authorization servers SHOULD include the iss parameter in authorization responses per RFC 9207, and clients MUST validate a present iss against the recorded issuer before redeeming the authorization code. This closes a mix-up attack vector.
  2. Clients MUST specify an appropriate application_type during Dynamic Client Registration, avoiding OpenID Connect redirect URI conflicts.
  3. Client credentials are explicitly bound to the issuing authorization server. Clients MUST key persisted credentials by issuer identifier, MUST NOT reuse them across authorization servers, and MUST re-register when the authorization server changes.
  4. Dynamic Client Registration itself is deprecated in favour of Client ID Metadata Documents, remaining available only for backwards compatibility.

Point 4 is the one to plan around. DCR was the pragmatic answer to a real problem — an agent discovering a tool server at runtime cannot have pre-registered with it. Client ID Metadata Documents solve the same problem by letting the client publish its metadata at a URL that serves as its identifier. If your architecture assumed DCR, this is a migration, not a tweak. It is also the direction AAuth has been pointing, and it reinforces why agent identity cannot be an afterthought.

Two smaller items: the resource-not-found error code moved from -32002 to -32602 (Invalid Params) for JSON-RPC alignment, and a new error code allocation policy partitions the server-error range — -32000 to -32019 stays implementation-defined with existing SDK usage grandfathered, -32020 to -32099 is reserved for the specification. Draft codes were renumbered accordingly: HeaderMismatch -32001-32020, MissingRequiredClientCapability -32003-32021, UnsupportedProtocolVersion -32004-32022.

Why this is good news if you run an API gateway

For anyone who has spent a career in API management, the statelessness change reads less like a rewrite and more like a correction.

Stateful protocols are hostile to the infrastructure enterprises already own. A session identifier in a header means your load balancer needs affinity. It means horizontal scaling requires either sticky routing or a shared session store. It means a server restart drops live conversations. It means your cache cannot safely serve a tools/list response to a second client, because the first client’s answer might have been different. And it means the gateway patterns you would normally reach for — rate limiting per consumer, response caching, blue/green deploys, canary routing — all need special-casing.

Statelessness removes those exceptions. tools/list becomes cacheable across clients, and ttlMs plus cacheScope tell your intermediary exactly how to treat it. Any replica can serve any request. Restarts stop being conversation-ending events. The required Mcp-Method and Mcp-Name headers give a gateway something to route and meter on without parsing the JSON-RPC body — a genuinely thoughtful addition for anyone enforcing policy at the edge.

The documented OpenTelemetry trace context conventions for _meta (traceparent, tracestate, baggage) matter for the same reason. Distributed tracing across agent-to-tool boundaries stops being something each vendor invents, which makes observability for agentic systems meaningfully more tractable.

The cost of all this is paid elsewhere. Per-request _meta means every call re-sends its protocol version, capabilities, and client identity. That is real token and bandwidth overhead, and on high-frequency tool calls it is not nothing. The specification has traded a small, constant per-request cost for the removal of an entire class of infrastructure problem. For enterprise deployments, that is close to unambiguously the right trade — but it is a trade, and the economics belong in your model.

Losing stream resumability is the more painful loss. Long-running tool calls over unreliable networks now fail completely rather than resuming, and the client must retry from scratch. For expensive operations this pushes work into the application layer: idempotency keys, checkpointing, and the io.modelcontextprotocol/tasks extension for anything genuinely long-running. This is exactly the territory covered in error handling and recovery in agentic systems, and it just became more important rather than less.

What this does not solve

MCP still does not specify authentication mechanisms, service discovery beyond server/discover, rate limiting, tenancy, or operational governance. The authorization changes harden how OAuth is used; they do not make MCP an identity system. The MCP Registry remains a separate concern, and AuthZEN remains the answer for fine-grained authorization decisions rather than anything in this spec.

Nor does statelessness make agents reliable. Removing sessions eliminates a class of infrastructure fragility; it does nothing about the reasoning limits that make agents fail in the first place, and nothing about where you draw autonomy borders.

What to do now

If you operate MCP servers. Inventory your dependence on the removed features first — sessions, initialize, ping, logging/setLevel, SSE resumability, and any per-connection variation in your list endpoints. That last one is the sleeper: a server that quietly returned different tools to different clients was previously legal and is now not. Implement server/discover, since it is a MUST. Then plan the MRTR migration for anything using server-initiated requests.

If you build MCP clients. The resultType handling is mandatory and has a compatibility rule worth encoding carefully: absent means "complete". Build the MRTR retry loop deliberately — it is a genuine control-flow change, not a wrapper. Start keying persisted OAuth credentials by issuer if you do not already.

If you run the infrastructure. This release is an invitation to treat MCP traffic like ordinary HTTP traffic. Revisit affinity rules, enable response caching guided by ttlMs and cacheScope, meter on Mcp-Method and Mcp-Name, and wire the OpenTelemetry _meta keys into your existing tracing backend.

Everyone should note the twelve-month clock. The formal deprecation policy means Roots, Sampling, and Logging have a defined runway rather than an indefinite one. Twelve months is the minimum window, not a promise of more.

Key takeaways

The 2026-07-28 revision converts MCP from a stateful, session-oriented protocol into a stateless request-response protocol that behaves like the rest of the HTTP ecosystem — removing sessions, the initialize handshake, ping, logging/setLevel, and SSE resumability, and requiring every request to carry its own protocol version, capabilities, and identity in _meta. Server-initiated requests are replaced by the Multi Round-Trip Requests pattern, in which servers return resultType: "input_required" and clients retry with inputResponses; Roots, Sampling, and Logging are deprecated on a minimum twelve-month clock alongside HTTP+SSE and OAuth Dynamic Client Registration. For enterprises the trade is favourable but not free: statelessness makes MCP traffic cacheable, horizontally scalable, and governable with infrastructure you already own, at the cost of per-request metadata overhead and the loss of stream resumability that now has to be handled in the application layer. The formalisation of an extensions framework, a CacheableResult interface, standard request headers, OpenTelemetry conventions, and a real deprecation policy signals a protocol maturing into something enterprises can plan around — which is a more useful property than feature richness.