All posts
engineering architecture infrastructure

The stateless MCP spec goes final, and the session was the easy part

Article Writer
Article Writer · Marketing
July 8, 2026 · 6 min read

The Model Context Protocol’s 2026-07-28 specification goes final on July 28. The release candidate was locked on May 21, and the ten weeks between the two dates were left open on purpose, a validation window for the ecosystem to build against the candidate and report what breaks. We spent part of that window migrating our own servers, and we wrote in June about what removing the protocol-level session did to our topology. That was one change. The full revision is the largest since the protocol shipped, and now that its shape is settled, the parts beyond statelessness are the ones setting our agenda for the rest of the year.

State moves from the wire to the payload

The headline change is easy to state. The initialize/initialized handshake is gone. The Mcp-Session-Id header is gone. Protocol version and client info travel in a _meta field on every request, and a client that wants to know what a server can do asks server/discover. Any request can land on any instance, which means a fleet of MCP servers can sit behind a plain round-robin load balancer with no sticky routing and no shared session store.

What took us longer to appreciate is that the spec does not remove state. It relocates it. Where a server used to hold context implicitly, keyed by a session the client could not see, the spec now recommends an explicit-handle pattern: a tool returns an identifier, the model passes that identifier back as an argument on the next call, and the state behind it lives somewhere any instance can fetch it. The state is still there. It is just visible, named, and carried in the conversation instead of hidden in a header.

The same relocation happens to server-initiated communication. The old model held a stream open and pushed server-sent events down it, which quietly assumed the stream’s two ends stayed put. The new model replaces that with multi round-trip requests. A server that needs something from the client returns an InputRequiredResult carrying its questions and a requestState blob. The client gathers answers and reissues the call with the responses attached. Nothing is held open, so the reissued call can land on a different instance than the one that asked, and the requestState blob is what makes that safe.

We find the pattern clarifying beyond MCP. Our June migration taught us that most of our session usage was habit rather than necessity, and the habits were expensive: drain-before-deploy, warm-before-scale, retries that only worked because they returned to the same box. The final spec takes the position that if state matters, it should be legible in the payload. Having now operated both ways, we agree.

Long-running work becomes an extension

Statelessness has an obvious tension with slow tools. A tool call that takes ten minutes cannot hold a connection open for ten minutes in a protocol built around any-instance retries. The spec resolves this by moving long-running work into a Tasks extension, redesigned from the old experimental core feature around stateless principles. A tools/call that starts something slow returns a task handle, and the client drives the rest through tasks/get, tasks/update, and tasks/cancel. The handle is the explicit-handle pattern again, applied to time instead of data.

This matches how we already run slow work internally, where a long job is a record the caller polls rather than a connection the caller holds, so adopting the extension is mostly a matter of deleting our homegrown version. The more interesting change is the machinery around it. Extensions are now first-class: identified by reverse-DNS names, negotiated through capability maps, and versioned independently of the core. Changes arrive through Specification Enhancement Proposals, and a Standards Track SEP has to ship conformance suite scenarios before it can reach Final status. Two official extensions launch with the spec, Tasks and MCP Apps, the latter letting servers render interfaces as sandboxed HTML iframes.

The structural bet here is that the core stays small and slow-moving while capabilities evolve at the edges on their own clocks. We have made the same bet in our own platform’s plugin layer, and we know the failure mode: extensions that everyone effectively requires become the core with extra steps. Whether Tasks stays optional in practice will be worth watching. Every agent team we know has long-running work.

The deprecation clock starts

Three core features enter formal deprecation: Roots, Sampling, and Logging. Each has a designated replacement. Roots gives way to tool parameters, resource URIs, or configuration. Sampling gives way to calling the model provider’s API directly. Logging gives way to stderr for stdio servers and OpenTelemetry for structured needs. The lifecycle policy behind this guarantees at least twelve months between deprecation and the earliest possible removal.

Of everything in the revision, this may matter most in the long run, and it is not any single deprecation. It is the existence of a lifecycle at all. Features now move through Active, Deprecated, and Removed as defined phases with a minimum clock between them. Until now, planning around the spec meant guessing. Our June migration happened on a timeline the release candidate chose for us, and the lesson we took then was to budget for protocol movement the way we budget for dependency upgrades. A published deprecation policy is that lesson made official. Churn becomes scheduled maintenance instead of surprise.

For our own servers, the practical exposure is small. We log to stderr already, and we never leaned on Sampling, in part because we prefer the auditability of direct provider calls. But we depend on third-party servers whose maintainers may read the twelve-month window differently, and part of our next quarter is an inventory of which tools we consume still sit on deprecated features.

Authorization tightens on the same schedule. Six SEPs align the auth model with OAuth 2.0 and OpenID Connect: mandatory iss parameter validation per RFC 9207, an application_type declared at dynamic client registration, credentials bound to their issuer so a migrated resource forces re-registration. Alongside the core spec, the Enterprise-Managed Authorization extension was promoted to stable, with Anthropic, Microsoft, and Okta among the adopters. It moves the decision about which servers a user may connect to from per-server consent prompts to the organization’s identity provider. What it deliberately does not do is authorize individual actions after connection. That boundary still belongs to the runtime, which is where we have long argued it should live. A protocol that knows which humans may reach which servers, and a runtime that decides what an agent may do once connected, is a division of labor we recognize.

The version we migrated to in June was a candidate, and some part of us kept a rollback plan in case the final spec moved under our feet. On July 28 that hedge expires. The more durable change is quieter than any wire format: the protocol we depend on most now has governance, conformance tests, and a clock. Infrastructure earns trust less through features than through predictability, and this is the release where MCP started offering the second kind.