MCP's Biggest Overhaul: What Going Stateless Actually Changes
The MCP 2026-07-28 spec rewrites the protocol core to be fully stateless — sessions are gone, handshakes are dead, and Extensions become first-class. Here's what the changes mean and what you need to fix in your MCP Server.
Last weekend I was refactoring an MCP Server. It ran fine on a single machine, but the moment I put it behind a load balancer, things went sideways — session timeouts, unstable sticky sessions, and the shared session store alone tripled the architecture's complexity. I was complaining about this to a colleague when he said, "Hold on. MCP is about to drop a major revision, and the entire session concept is gone."
I went and read through the Release Candidate for the 2026-07-28 spec. He wasn't exaggerating. This isn't a patch release. It's the largest architectural change since MCP launched, and the core idea is simple: strip all state out of the protocol layer and make MCP genuinely stateless. The final spec ships on July 28, but the RC has locked in all changes — you can start evaluating your migration right now.
Sessions Are Gone. Now What?
Under the 2025-11-25 spec, a typical MCP flow went like this: the client sends an initialize request to establish a session, the server responds with an Mcp-Session-Id, and every subsequent request must carry that header. The practical consequence: your load balancer has to route all requests from the same session to the same server instance. Sticky sessions become non-negotiable. If you want horizontal scaling, you either bring in a shared session store (Redis, etc.) or live with a single point of failure.
The new spec deletes the entire session concept. No more initialize handshake. No more Mcp-Session-Id. Client info, protocol version, and capabilities — previously exchanged once at connection time — now travel in the _meta field on every request.
The effect is immediate: any request can land on any server instance and be handled independently. Your MCP Server goes from "requires sticky sessions and shared storage" to "a plain round-robin load balancer will do."
// Old: initialize first, then session ID on every call
POST /mcp
Mcp-Session-Id: 1868a90c-3a3f-4f5b
{"jsonrpc":"2.0","method":"tools/call",...}
// New: a single self-contained request, any instance handles it
POST /mcp
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: search
{"jsonrpc":"2.0","method":"tools/call","params":{...,"_meta":{...}}}
There's a nice side benefit: Mcp-Method and Mcp-Name are now required HTTP headers. Gateways and load balancers can route, rate-limit, and audit traffic based on headers alone, without parsing JSON bodies. Ops-friendly by design.
Where Does State Go? Explicit Handle Pattern
The protocol no longer manages state for you, but that doesn't mean your application can't have state. The recommended replacement is called the "explicit handle pattern," and the idea is straightforward: the server generates an explicit ID from a tool call (say, basket_id or browser_id), and the model passes that ID as an ordinary argument on subsequent calls.
// Step 1: create a basket, server returns basket_id
tools/call: create_basket
→ { "basket_id": "basket_abc123" }
// Step 2: add an item — the model passes basket_id as a parameter
tools/call: add_item
→ { "basket_id": "basket_abc123", "item": "...", "quantity": 1 }
My first reaction was that this just shifts the session burden to the application layer. But after sitting with it, I think this pattern is actually more powerful: the state becomes visible to the model. The model can compose handles across tools, pass them between steps flexibly, and even reason about which handle to use next. The old session state, hidden in transport metadata, was invisible to the model. This is an upgrade, not a workaround.
Extensions Become First-Class
Extensions existed in 2025-11-25, but without a formal governance process. They were more of a placeholder. The new spec, via SEP-2133, gives Extensions proper structure: reverse-DNS identifiers, their own ext-* repositories with delegated maintainers, independent versioning, and a clear path from experimental to official. The big idea: MCP can evolve without touching the core spec every time. New capabilities ship as Extensions on their own timeline.
Two official Extensions ship with this release.
MCP Apps: Servers can return interactive HTML interfaces that hosts render in sandboxed iframes. Tools declare their UI templates upfront so hosts can prefetch, cache, and security-review them before anything runs. UI actions flow back through the same JSON-RPC channel as ordinary tool calls — meaning every UI-initiated action goes through the same audit and authorization path. In practical terms: your MCP tool is no longer limited to returning markdown. It can return a sortable table, a form, a data visualization panel.
Tasks: Long-running work graduates from an experimental core feature to an official Extension. The old Tasks API relied on holding an SSE stream open to drive the lifecycle. The new version is stateless by design: tools/call returns a task handle, and the client drives progress through tasks/get, tasks/update, and tasks/cancel. tasks/list is removed — without sessions, there's no safe way to scope the list.
Breaking Changes: What You Need to Fix
This release has breaking changes, and they're not optional. Here's what you must handle:
| Change | Impact | Action |
|---|---|---|
Mcp-Session-Id removed |
Any code reading this header breaks | Switch to explicit parameter passing |
initialize handshake removed |
Capability negotiation logic breaks | Use _meta per-request + server/discover |
| New required Streamable HTTP headers | Requests without Mcp-Method / Mcp-Name will be rejected |
Add both headers to all requests |
| Tools list caching | Can't assume clients poll in real-time | Declare reasonable ttlMs based on data freshness |
| W3C Trace Context standardized | Distributed tracing key names are locked in | Update traceparent and related fields |
Also worth noting: inputSchema and outputSchema are upgraded to full JSON Schema 2020-12. You now get oneOf, anyOf, allOf, $ref, and $defs. Output schemas are no longer constrained to object types, and structuredContent can be any JSON value. If you've been fighting the old schema limitations for complex tool parameters, this is a significant quality-of-life improvement.
Three features are deprecated: Roots, Sampling, and Logging. Deprecated doesn't mean removed — they continue to work in this release and for at least 12 months after. But you should start planning alternatives now.
Migration Timeline
- May 21, 2026: RC published, changes locked
- July 28, 2026: Final spec ships; Tier 1 SDKs expected to support it by this date
- July–August 2026: Major SDKs roll out updates
- 2027+: Old version support phases out as the 12-month deprecation window closes
If you start evaluating now, you have plenty of time. Three things to do first:
- Find every piece of code that depends on sessions and rewrite it using the explicit handle pattern
- Audit all Streamable HTTP endpoints to ensure
Mcp-MethodandMcp-Nameheaders are handled - If you're using the experimental Tasks API, map out the migration to the new lifecycle
Closing Thoughts
My takeaway from this release: MCP has graduated from "it works" to "it scales." The stateless redesign, the Extensions framework, the deprecation policy — each addresses a real production pain point. The protocol layer sheds a lot of complexity, and what you get in return is deployment simplicity and extensibility.
For teams running their own MCP Servers, the migration isn't trivial, but it's worth it. The operational cost difference between an MCP Server that needs sticky sessions and shared storage versus one that runs on commodity HTTP infrastructure is an order of magnitude.
I particularly like one small detail: the Mcp-Method header. It's a tiny change, but it means your MCP traffic can be governed with existing API gateway tooling — no custom middleware required. That's good protocol design: use the wheels that already exist instead of inventing new ones.