MCP Goes Stateless: Sessions Give Way to Explicit State Handles
On July 20, 2026, Nate Barbettini (Arcade) went public with something that had been maturing in the MCP spec for a while: the Model Context Protocol release candidate from July 28, 2026 removes the session ID (Mcp-Session-Id header) and the initialize handshake entirely from the protocol. In their place comes a simpler pattern: state is passed along via ordinary, tool-returned handles instead of being implicitly bound to a server session. If you run MCP servers behind a load balancer, you know the old problem — every instance had to know which session another instance had handed out, or traffic needed sticky routing. Barbettini put it well: the old design "fights the load balancer instead of working with it." In parallel, the Enterprise-Managed Authorization extension has gone stable, replacing repeated authorization prompts in enterprise environments with centralized identity-provider trust. This post breaks down both changes technically — and what they mean for anyone running their own MCP servers.
TL;DR
TL;DR: The MCP spec release candidate from July 28, 2026 (SEP-2567, SEP-2575) removes Mcp-Session-Id and the initialize handshake — servers become stateless at the protocol level, and state moves into explicit, tool-returned handles (e.g. basket_id). That makes MCP servers deployable behind a plain round-robin load balancer, with no sticky sessions or shared session store needed. It's a breaking change with no deprecation window; per SEP-2567, roughly 3.7% of server implementations with session-bound state require direct migration. In parallel, the Enterprise-Managed Authorization extension (ID-JAG, first implementation: Okta) has gone stable — single sign-on across all company-approved MCP servers instead of repeated authorization prompts.
Why sessions were a problem in the first place
Until now, the MCP spec defined sessions at the protocol level: on first contact, a client like Claude sends an initialize, the server replies with its capabilities and a session ID in the Mcp-Session-Id header. That ID has since scoped five categories of state — negotiated capabilities, elicitation/sampling intermediate state, application state (the textbook example: a shopping cart that exists implicitly per session), variable list endpoints (tools/list, resources/list, prompts/list can return different results per session), and resource subscriptions.
The problem, per SEP-2567: session lifetime is wildly inconsistent in practice. ChatGPT and (now) Claude.ai treat it per tool call, desktop and IDE clients per application launch, web clients per page load — and almost no client resumes a session after a disconnect. Under those conditions, servers can't design reliable stateful workflows, because it's unclear what a "session" even means in a given deployment. And operationally, there's more: a server fleet behind a load balancer has to either force sticky routing to a fixed instance or share session state across all instances — both extra infrastructure that has nothing to do with the server's actual job.
SEP-2567: Explicit state handles instead of session IDs
The core of the change: the Mcp-Session-Id header disappears from the spec with no replacement construct. In its place comes a pattern several production remote MCP servers already use informally — Linear, Notion, GitHub, and Stripe already return IDs today (issue ID, page ID, PR number, customer ID) that subsequent tool calls carry as an ordinary argument. SEP-2567 turns this pattern into the official standard: handles are not a new protocol construct, just ordinary strings in tool results and arguments.
The flow, using a shopping basket as an example
// Step 1: create state, get a handle back
// → tools/call
{ "name": "create_basket", "arguments": {} }
// ← result
{ "content": [{ "type": "text", "text": "Created basket bsk_a1b2c3" }],
"structuredContent": { "basket_id": "bsk_a1b2c3" } }
// Step 2: thread the handle through subsequent calls
// → tools/call
{ "name": "add_item",
"arguments": { "basket_id": "bsk_a1b2c3", "sku": "shoes" } }
// Step 3: final operation with the same handle
// → tools/call
{ "name": "checkout", "arguments": { "basket_id": "bsk_a1b2c3" } }
The handle bsk_a1b2c3 is an ordinary string in structuredContent — it lands in chat history automatically, threads across connection and session boundaries, and is pure tool data, not a protocol field. That solves three problems of the old model at once: lifetime is documented explicitly in the tool description ("basket_id expires after 24h idle"), list endpoints may now only depend on auth context rather than connection state — so clients can cache tools/list results across subagents — and cardinality is no longer fixed at one per session: an orchestrator can create one shared basket for all subagents, or a separate browser instance per subagent — the model decides, per piece of state, what's shared and what's isolated.
What else is in the RC
Removing sessions is the most visible change in the July 28 release candidate, but not the only one:
- No more
initializehandshake (SEP-2575): protocol version, capabilities, and client info now travel as a_metafield on every single request instead of a separate upfront negotiation. Servers can expose capabilities instead via a newserver/discovermethod. - Routing via headers instead of body inspection: the new
Mcp-MethodandMcp-Nameheaders let load balancers and gateways route traffic without parsing the request body. - Tasks becomes an extension instead of a core feature: the previously experimental Tasks API becomes a standalone extension, rebuilt entirely around the stateless model;
tasks/listis gone, clients drive it viatasks/get,tasks/update,tasks/cancel. - Three deprecations with at least a 12-month transition period: Roots (replaced by tool parameters/resource URIs/configuration), Sampling (replaced by direct LLM provider API integration), and Logging (replaced by
stderrfor stdio transports or OpenTelemetry). - Six authorization SEPs bring authorization closer to OAuth 2.0/OpenID Connect practice, including mandatory
issvalidation per RFC 9207. - Error code change: missing resources now return the standard JSON-RPC code
-32602instead of the MCP-specific-32002— if your code matches on the literal value, you'll need to update it.
Security implications of handles
A handle is no longer a secret in the sense of a session ID known only to client and server — it ends up in chat history, subagent prompts, copy-paste buffers, and browser history. SEP-2567 makes the consequence explicit: for authenticated servers, "possession of the handle is not authorization" — every call must validate the combination of handle and auth context, similar to how Google Docs controls access via an ACL rather than by keeping the document ID secret. For unauthenticated servers (bearer-token model), the SEP recommends at least 128 bits of cryptographic entropy (UUIDv4 or 22+ base64 characters), no derivation from predictable inputs, and a bounded lifetime — comparable to "anyone with the link" sharing URLs or password-reset tokens. If you run your own MCP servers with stateful tools, treat both rules as a minimum standard, not an option.
Enterprise-Managed Authorization: single sign-on across all MCP servers
Alongside the session change, the Enterprise-Managed Authorization extension (EMA) has gone stable — a response to a frequently cited problem in enterprise deployments: repeated authorization prompts once multiple MCP servers are in play. Technically, EMA uses an Identity Assertion JWT Authorization Grant (ID-JAG), which the MCP server's authorization server exchanges for an access token — moving the authorization decision from the individual user or server into the company-wide identity provider. The boundary matters: the identity layer decides whether a client may connect to a server at all, but doesn't inspect the running MCP traffic — it doesn't replace separate policy enforcement for security-critical runtime decisions. Okta is the first identity provider with support (via its Cross App Access approach); on the client side, Claude, Claude Code, and Cowork as well as Visual Studio Code already support it, and on the server side, Asana, Atlassian, Canva, Figma, Granola, Linear, and Supabase among others, with Slack in progress. Identity providers without EMA support still need a fallback auth path — the protocol is additive, not mandatory.
What this means if you run your own MCP servers
SEP-2567 itself quantifies the migration burden: roughly 90% of all server implementations don't reference sessions at all and aren't affected. Another 6.3% are pure SDK/transport internals (session routing in the TypeScript SDK, the sessionIdGenerator option) that go away anyway once you switch to the stateless SDK transport variant. That leaves roughly 3.7% who need to act concretely: if you've bound application state to the session (2.5%), you need to switch to explicit handles or the auth principal. If you run proxies/gateways with session-based sticky routing (0.7%), you need to switch to routing by auth principal or a self-issued gateway header. If you've bound OAuth flows (PKCE, JWT) to the session (0.5%), you need a server-generated nonce in the OAuth flow's state parameter.
Important: there's no deprecation window — this is a breaking change tied to the protocol version. Servers on the old version keep speaking the old protocol, newer ones speak the new one; version negotiation keeps both running in parallel, but deliberately prevents a permanent "supports both" variant that would otherwise undo the caching benefits of the new list semantics. If you run your own MCP servers, check before July 28 which of the categories above your implementation falls into — and whether the SDK version you're using already supports the stateless transport variant.
Frequently asked questions
How does the Enterprise-Managed Authorization extension relate to the stateless change?+
Not directly, technically — both changes are part of the same release cycle but solve different problems: sessions/statelessness is about server scaling and state management, while EMA is about authorizing clients against multiple servers in enterprise environments.
Are handles more or less secure than the previous session IDs?+
Neither fundamentally more nor less secure, but they need to be handled differently: handles end up visibly in chat history and prompts, where session IDs mostly stayed hidden in transport. For authenticated servers, that's not a problem as long as access is checked via auth context rather than handle secrecy; unauthenticated servers need to ensure sufficient entropy and a bounded lifetime for handles.
What happens to the experimental Tasks API if I'm already using it?+
You'll need to migrate to the new, standalone Tasks extension — tasks/list is gone, and control now runs via tasks/get, tasks/update, and tasks/cancel.
Does this affect me if my MCP server has no session logic of its own?+
Per SEP-2567's own breakdown, this doesn't directly affect roughly 90% of implementations — if you've never worked explicitly with Mcp-Session-Id, you'll likely just need an SDK update.
Do I need to do anything as a user of Claude/Cowork/other MCP clients?+
No, the change directly affects MCP server operators and developers. As a user of a client like Claude Code or Cowork, nothing changes in how you use it — the protocol details operate below the application layer.
Is this already the final spec or still a release candidate?+
Per the sources we checked: July 28, 2026 marks a release candidate (SEP-2567 itself already carries "Final" status as a standards-track proposal). Whether and when it becomes the finally ratified spec version is something to follow via the official MCP blog before migrating anything to production on top of it.
Conclusion
Two independent changes, made visible on the same day, hit the core of how MCP servers run in production: sessions disappear from the protocol in favor of explicit, model-visible state handles, and authorization for enterprise environments moves into a centralized identity layer. For most server operators, that means an SDK update with no code changes; for the minority with session-bound application state, sticky-routing gateways, or session-bound OAuth flows, it's a mandatory deadline before July 28 — with no transition period.
Sources
- TechCrunch — AI's most important protocol is getting a little bit easier to use
- InfoQ — MCP Enterprise-Managed Authorization
- Arcade — MCP Going Stateless
- MCP — SEP-2567: Sessionless MCP via Explicit State Handles
- MCP Blog — The 2026-07-28 MCP Specification Release Candidate
- GitHub — SEP-2567 Pull Request
- GitHub — SEP-2575: Make MCP Stateless
I help migrate your MCP server landscape to the stateless protocol — from figuring out which category affects you to switching to explicit state handles.
SDK version audit across your MCP server fleet, migrating session-bound application state to explicit handles, and connecting to centralized identity providers via Enterprise-Managed Authorization where company policy requires it.
Platform operations, not consulting on paper: I audit, migrate, and harden your AI agent infrastructure on an ongoing basis.
About the author
![[Translate to English:] Foto von Kai Ole Hartwig.](/fileadmin/_processed_/e/9/csm_ole-neu_73323ad80d.jpeg)
Kai Ole Hartwig
Programming since 2002 – self-taught, set up my own business with KO-Web in 2012. Over 100 projects, with a focus on security, performance, automation and quality. Today freelance: DevSecOps consulting, training and software development.
