May 28, 2026
· 13 min readMCP 2.0: The Stateless Rewrite That Breaks Every Server You've Shipped
The next MCP spec drops session IDs, removes the initialize handshake, and turns Extensions, Tasks, and MCP Apps into first-class citizens. It's the largest revision since launch — and it contains breaking changes. Here's every change, why it exists, and what you have to migrate before July 28, 2026.

TL;DR
- MCP's next spec — version
2026-07-28— makes the protocol stateless at the protocol layer. Theinitializehandshake andMcp-Session-Idare both gone. - Any request can now hit any server instance, so you can run MCP behind a plain round-robin load balancer with no sticky sessions and no shared session store.
- Six SEPs drive the stateless core; Extensions become first-class (reverse-DNS IDs, independent versioning), shipping two official extensions: MCP Apps (server-rendered UIs) and Tasks (long-running work).
- Authorization hardens around OAuth 2.0 / OpenID Connect, and Roots, Sampling, and Logging are deprecated.
- The RC was locked May 21, 2026; the final spec ships July 28, 2026. This release contains breaking changes.
Source: The 2026-07-28 MCP Specification Release Candidate
Why this matters
If you've shipped a remote MCP server, you already know the pain. Stateful sessions fight with load balancers. Horizontal scaling needs workarounds — sticky routing, a shared session store, or both. And there's no standard way for a registry or crawler to learn what a server does without connecting to it first.
That's not an accident of your architecture. It's baked into how MCP worked. In the 2025-11-25 spec, the server hands you an Mcp-Session-Id during initialization, and every subsequent request has to carry it — which pins the client to whichever instance issued it. Source: MCP Blog
The 2026-07-28 release candidate is the maintainers' answer, and it's a clean break. Think of MCP as a USB-C port for AI applications — one standard connector between models and the outside world. This release rebuilds the part of that connector that decides how requests get routed, and it does it the way the web already solved this problem two decades ago: go stateless.
⚠️ Warning: This is described as the largest revision of the protocol since launch. If you build or maintain MCP servers, the direct effect on production is immediate.
How the stateless core works
The headline change is that MCP is now stateless at the protocol layer. Six Specification Enhancement Proposals work together to get there.
The old way: a session-bound request
In 2025-11-25, calling a tool over Streamable HTTP meant establishing a session first, then carrying its ID on every call:
POST /mcp HTTP/1.1
Mcp-Session-Id: 1868a90c-3a3f-4f5b
Content-Type: application/json
{"jsonrpc":"2.0","id":2,"method":"tools/call",
"params":{"name":"search","arguments":{"q":"otters"}}}That Mcp-Session-Id is the problem. It's a token that only works at the one instance that minted it — like a kirana-store slip that's valid at that shopkeeper and nowhere else. If that instance restarts or you scale out, the slip is worthless.
The new way: a self-contained request
In 2026-07-28, the same call carries everything it needs in one shot:
POST /mcp HTTP/1.1
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: search
Content-Type: application/json
{"jsonrpc":"2.0","id":1,"method":"tools/call",
"params":{"name":"search","arguments":{"q":"otters"},
"_meta":{"io.modelcontextprotocol/clientInfo":{"name":"my-app","version":"1.0"}}}}Breaking it down:
- The
initialize/initializedhandshake is removed (SEP-2575). Protocol version, client info, and capabilities now ride in_metaon every request. A newserver/discovermethod lets clients fetch capabilities up front when they actually need them. - The
Mcp-Session-Idheader and its protocol-level session are removed (SEP-2567). - With both gone, any request can land on any instance — no sticky routing, no shared session store at the protocol layer.
Here's the before/after, side by side:
Stateless protocol, stateful applications
Here's the catch most people miss on the first read: stateless protocol does not mean stateless application. You can still carry state across calls — you just do it the way HTTP APIs always have. Mint an explicit handle from a tool (a basket_id, a browser_id) and have the model pass it back as a normal argument on later calls.
The maintainers argue this is more powerful than hidden session state, not just a workable substitute. The model can compose handles across tools, reason about them, and hand them off between steps — things that session state buried in transport metadata never allowed. The state becomes visible to the model instead of hidden away. Source: MCP Blog
Server-to-client requests, without a connection
A stateless protocol still needs a way for a server to ask the client something mid-call — an elicitation prompt like "Delete 3 files?". With no persistent connection and no session, how?
Picture an auto-rickshaw ride. You said "Connaught Place," the driver started, and now mid-trip he needs to know which gate. He can't restart the whole trip to ask. Two rules make this work:
1. The in-flight rule. Server-initiated requests may only be issued while the server is actively processing a client request (SEP-2260). Earlier spec versions recommended this; now it's required. A user is never prompted out of nowhere — every elicitation traces back to something they (or their agent) started.
2. The special response. Instead of holding an SSE stream open, the server returns an InputRequiredResult (SEP-2322, Multi Round-Trip Requests):
{
"resultType": "inputRequired",
"inputRequests": {
"confirm": {
"type": "elicitation",
"message": "Delete 3 files?",
"schema": { "type": "boolean" }
}
},
"requestState": "eyJzdGVwIjoxLCJmaWxlcyI6WyJhIiwiYiIsImMiXX0="
}The client gathers the answers and re-issues the original call with inputResponses plus the echoed requestState. Because everything the server needs is encoded in that payload, any instance can pick up the retry.
💡 Tip: Don't show the
requestStateto your user. It's an opaque resume token — the client surfaces only the human-facing question, then threads the state back to the server on retry. It's the "form draft survives a page refresh" pattern.
This is also a quiet win for parallel agents. In the old model, the agent that asked a question sat blocked until the answer came back. Now it can fire the request, go do other work, and let any free server instance resume the task from its encoded state.
Routable, cacheable, traceable
Three smaller changes make the resulting traffic far easier to operate. The mental model: a highway works better when ambulances, school buses, and market traffic each get a dedicated lane.
| Change | SEP | What it does |
|---|---|---|
Mcp-Method + Mcp-Name headers |
SEP-2243 | Load balancers, gateways, and rate-limiters route on the operation without inspecting the body. Servers reject requests where headers and body disagree. |
ttlMs + cacheScope |
SEP-2549 | List and resource-read results carry cache hints, modeled on HTTP Cache-Control. Clients know how long a tools/list is fresh and whether it's safe to share across users. |
W3C Trace Context in _meta |
SEP-414 | Locks down traceparent, tracestate, and baggage key names so distributed traces correlate across SDKs and gateways into a single span tree. |
The header change is like a security guard who routes you by reading the label on your bag instead of opening it. The trace context is the part I'm most happy about — it's the difference between a failed UPI payment where you can see which leg of the hop broke versus a black box. A trace that starts in the host app can now follow a tool call through the client SDK, the server, and whatever it calls downstream, showing up as one span tree in any OpenTelemetry-compatible backend.
Extensions become first-class
Extensions existed in 2025-11-25 but had no formal process. SEP-2133 fixes that:
- Identified by reverse-DNS IDs (think
io.modelcontextprotocol.tasks) — the same conflict-free naming Android uses for package IDs. - Negotiated through an
extensionsmap on client and server capabilities. - Live in their own
ext-*repositories with delegated maintainers, versioning independently of the spec. - A new Extensions Track in the SEP process gives them a path from experimental to official.
This is MCP trying to become a platform the way Android is a platform: you rarely "use Android," you use apps on top of it. Two official extensions ship in this release.
MCP Apps: server-rendered UIs
MCP Apps (SEP-1865) lets servers ship interactive HTML interfaces that hosts render in a sandboxed iframe. Tools declare their UI templates ahead of time so hosts can prefetch, cache, and security-review them before anything runs.
The rendered UI talks back over the same JSON-RPC base protocol used everywhere else in MCP — so every UI-initiated action goes through the same audit and consent path as a direct tool call. This is the "pick one of these three hotels, then choose a room, then confirm payment" interaction, formalized.
Tasks graduates to an extension
Tasks shipped as an experimental core feature in 2025-11-25. Production use surfaced enough redesign that its right home is an extension. The Tasks extension:
- A server can answer
tools/callwith a task handle instead of an immediate result. - The client drives it with
tasks/get,tasks/update, andtasks/cancel. - Task creation is server-directed — the client advertises the extension, the server decides when a call runs as a task.
tasks/listis removed because it can't be scoped safely without sessions.
This is the right shape for any long-running background job — a 20-minute data pipeline, a batch of repositories chewing through an analysis queue. The client polls a handle; the server does the work.
⚠️ Warning: Anyone who shipped against the
2025-11-25experimental Tasks API must migrate to the new lifecycle. The handle, the method names, and the creation flow all changed.
Authorization hardening
Six SEPs align the authorization spec with how OAuth 2.0 and OpenID Connect are actually deployed. The headline: MCP is leaning hard into "log in with Google / GitHub"-style flows instead of asking non-technical users to paste raw tokens.
issvalidation is now required per RFC 9207 (SEP-2468) — a low-cost mitigation for mix-up attacks, which are more likely in MCP's single-client, many-server pattern. A future version will reject responses that omitiss, so authorization servers should start supplying it now.- Clients declare their OpenID Connect
application_typeduring Dynamic Client Registration (SEP-837), fixing the common case where a desktop/CLI client gets defaulted to"web"and has its localhost redirect rejected. - Clients bind credentials to the issuing server's
issuerand re-register on resource migration (SEP-2352). - The spec documents refresh-token requests from OIDC-style servers (SEP-2207), and clarifies scope accumulation during step-up (SEP-2350) and the
.well-knowndiscovery suffix (SEP-2351).
If you've ever wired up an OAuth login — client ID, secret, redirect URL, access + refresh tokens, scopes — this will feel familiar. That's the point: building an MCP server now looks a lot like building a web app.
Roots, Sampling, and Logging are deprecated
Three core features are deprecated under the new lifecycle policy (SEP-2577):
| Feature | Replacement |
|---|---|
| Roots | Tool parameters, resource URIs, or server configuration |
| Sampling | Direct integration with LLM provider APIs |
| Logging | stderr for stdio transports; OpenTelemetry for structured observability |
Important: These are annotation-only deprecations. The methods, types, and capability flags keep working in this release and in every spec version published within a year of it. Removing any of them requires a separate SEP.
There's also a bonus pair of changes worth flagging if you maintain a client:
- Tool
inputSchema/outputSchemaare lifted to full JSON Schema 2020-12 (SEP-2106) — now allowingoneOf,anyOf,allOf, conditionals, and$ref/$defs. Implementations must not auto-dereference external$refURIs. - The missing-resource error code changes from MCP-custom
-32002to the JSON-RPC standard-32602Invalid Params (SEP-2164). If your client matches on the literal-32002, update it.
How the protocol evolves from here
This release contains breaking changes — but the maintainers explicitly say that won't be the norm. Three governance SEPs are designed so future revisions evolve without breaking core capabilities:
- The feature lifecycle policy gives every feature an Active → Deprecated → Removed path with at least twelve months between deprecation and the earliest possible removal.
- The Extensions framework means new capabilities ship as opt-in extensions and stabilize there before — if ever — moving into the spec.
- A Standards Track SEP can't reach Final until a matching scenario lands in the conformance suite (SEP-2484) — the same suite the new SDK tier system scores official SDKs against.
Migration checklist
If you target 2026-07-28, work through this before July 28:
- Drop
Mcp-Session-Iddependence. Stop minting and validating protocol-level sessions. - Remove the
initializehandshake path. Move protocol version, client info, and capabilities into_metaon every request; useserver/discoverfor up-front capability fetches. - Move app state to explicit handles. Return a
basket_id-style token from tools and accept it back as an argument. - Emit
Mcp-MethodandMcp-Nameheaders on every Streamable HTTP request, and reject mismatches between header and body. - Adopt
InputRequiredResultfor elicitation; encode resume state inrequestState, and only issue server prompts while a client request is in flight. - Add
ttlMsandcacheScopeto list and resource-read results. - Wire W3C Trace Context (
traceparent,tracestate,baggage) through_meta. - Validate
isson authorization responses (RFC 9207); start supplyingissfrom your auth server. - Migrate the experimental Tasks API to the new extension lifecycle (
tasks/get/tasks/update/tasks/cancel, server-directed creation). - Replace deprecated Roots, Sampling, and Logging usage, and update any client matching on the
-32002error code.
When to move — and when not to
Move now if you run remote MCP servers at any real scale, you've fought sticky-session load balancing, or you're building new servers from scratch. The stateless model is strictly easier to operate, and starting on 2026-07-28 means you adopt future revisions without rewriting transport or lifecycle code.
Hold during the window if you have a stable 2025-11-25 deployment with no scaling pain. That spec keeps working, clients are expected to support both during the transition, and the ten-week window (locked May 21 → final July 28, 2026) exists precisely so implementers can validate against real workloads before committing. Spec text can still shift if blocking issues surface.
One honest limitation: stateless transport does not fix context bloat. Tool metadata — descriptions, parameters, schemas — still eats into the context window across dozens of integrations. That fight stays in the client layer. Source: MCP.Directory analysis
Conclusion
I've watched MCP go from "is this even a protocol?" to something that genuinely earns the name, and 2026-07-28 is the moment it grew up. The stateless rework is the kind of foundational change you only get to make once — a clean break that trades a bit of per-request payload for the ability to run on commodity HTTP infrastructure with a plain round-robin load balancer.
If you build on MCP, start by reading the draft spec and the changelog against 2025-11-25. Then pick one server, strip out the session handling, and run it stateless behind a load balancer. The breaking changes are real — but the architecture is finally the one those long Hacker News threads kept asking for.
Sources
- The 2026-07-28 MCP Specification Release Candidate — Model Context Protocol Blog (primary source)
- The Future of MCP Transports — the December plan the stateless work completes
- The 2026 MCP Roadmap — context on why these priorities were chosen
- MCP draft specification and changelog
- SEPs referenced: 2575, 2567, 2260, 2322, 2243, 2549, 414, 2133, 1865, 2577, 2106, 2164, 2468
- MCP 2026-07-28: The Stateless Release Candidate, Explained — MCP.Directory (independent analysis + critique)
FAQ
Is this officially called MCP 2.0?
No. There is no '2.0' label — the version string is `2026-07-28`. But the maintainers call it the largest revision of the protocol since launch, so '2.0' is a fair informal name for the scale of the change.
When does the 2026-07-28 spec ship?
The release candidate was locked on May 21, 2026. The final specification publishes on July 28, 2026, after a ten-week validation window for SDK maintainers and client implementers.
Will my existing MCP server break?
This release contains breaking changes. The `2025-11-25` spec keeps working and clients are expected to support both during the transition, but any new stateless server, and any code that depends on session IDs or the initialize handshake, will need updates.
What replaces session IDs now that MCP is stateless?
Every request is self-contained — protocol version, client info, and capabilities travel in `_meta` on each call. For application state, you mint an explicit handle (like a `basket_id`) from a tool and have the model pass it back as a normal argument.
Is Server-Sent Events (SSE) gone?
The long-lived SSE stream is no longer the mechanism for server-to-client prompts. Multi Round-Trip Requests replace it with a special `InputRequiredResult` response that any server instance can resume.
What got deprecated?
Roots, Sampling, and Logging are deprecated under the new feature lifecycle policy. These are annotation-only deprecations — they keep working for at least twelve months, and removal requires a separate SEP.