MCP Goes Stateless on July 28, 2026: What Breaks and How to Migrate Your Server
MCP Goes Stateless on July 28, 2026: What Breaks and How to Migrate Your Server
If you're running an MCP server in production, July 28, 2026 is the date the protocol's biggest revision since launch ships as final. The core change: MCP drops session state entirely. No more initialize/initialized handshake, no more Mcp-Session-Id header, no more sticky routing or shared session stores. Every request becomes self-contained. If you built your server by hand against the transport spec rather than through an official SDK, this is a real migration, not a changelog footnote โ budget time for it before the release candidate locks into GA.
This post works from the spec's actual SEPs (Specification Enhancement Proposals) and the GitHub MCP Server's own migration notes, not secondhand summaries.
Why MCP is dropping sessions
The stateful design made sense when MCP servers were mostly local stdio processes talking to a single client. It breaks down the moment you deploy a remote MCP server behind a load balancer: the Mcp-Session-Id forced sticky routing, a shared session store, and โ in some implementations โ deep packet inspection at the gateway just to figure out which backend instance owned a given session.
The GitHub MCP Server team's own writeup is blunt about the operational cost: their server previously ran a Redis-backed session store purely to satisfy the handshake. Under the new spec, that store is gone. A stateless MCP server can sit behind a plain round-robin load balancer, route on the new Mcp-Method header without touching the payload, and let clients cache tools/list responses using the new ttlMs field instead of relying on a live connection to signal freshness.
What actually changed
1. No more handshake, no more session header
initialize and initialized are gone. Client protocol version and identity now travel in _meta on every request instead of being negotiated once at connection time:
1{
2 "jsonrpc": "2.0",
3 "id": 7,
4 "method": "tools/call",
5 "params": {
6 "name": "search_repos",
7 "arguments": { "query": "mcp servers" },
8 "_meta": {
9 "io.modelcontextprotocol/protocolVersion": "2026-07-28",
10 "io.modelcontextprotocol/clientInfo": { "name": "my-client", "version": "1.4.0" }
11 }
12 }
13}
The Mcp-Session-Id header is removed outright. If your server keys anything โ rate limits, per-connection auth context, in-flight tool state โ off that header, that logic has to move.
2. Two new mandatory headers on Streamable HTTP
Every POST now requires:
MCP-Protocol-Version: 2026-07-28Mcp-Methodโ must match the JSON-RPCmethodfield in the bodyMcp-Nameโ must match the tool name ontools/callrequests
1POST /mcp HTTP/1.1
2MCP-Protocol-Version: 2026-07-28
3Mcp-Method: tools/call
4Mcp-Name: search_repos
5Content-Type: application/json
Servers must reject requests where the header and body disagree, using the new error code -32020 (HeaderMismatch). This is what lets a load balancer or gateway route and log by header alone โ no payload parsing required, which is exactly the packet-inspection cost GitHub's team called out.
3. Multi-round-trip requests replace SSE for elicitation
Instead of holding a Server-Sent Events stream open while a tool waits on user input, servers now return an InputRequiredResult containing inputRequests and a base64-encoded requestState. The client collects the input, then re-sends the original call with inputResponses and the echoed requestState. GitHub's Go SDK wraps this so URL-based elicitation works as a normal follow-up HTTP request rather than a held-open stream โ useful if you're running behind an infrastructure layer (many managed load balancers, some serverless platforms) that doesn't tolerate long-lived connections well.
4. List and read results need cache metadata
tools/list, resources/list, and similar results must now include ttlMs and cacheScope, modeled directly on HTTP Cache-Control. Clients are expected to cache accordingly instead of re-fetching on every connection. If your server currently returns the full tool list on demand with no caching hints, that still works, but you're leaving a real latency win on the table.
5. Tracing is now standardized
_meta gets standard W3C Trace Context keys โ traceparent, tracestate, baggage โ for OpenTelemetry interop. If you're already running OTel across your stack (see our CloudWatch OpenTelemetry piece for the AWS side of this), this is the first spec-level hook to correlate an MCP tool call with the rest of a distributed trace instead of bolting on custom headers.
6. Error code cleanup
Missing-resource errors move from the MCP-specific -32002 to the standard JSON-RPC -32602 (Invalid Params). If you have client-side error handling that pattern-matches -32002, it will silently stop firing after the migration โ this is the kind of break that doesn't throw an exception, it just quietly does nothing.
Authorization got materially stricter
Six SEPs tighten MCP's OAuth 2.1 alignment. The ones that require code changes:
- Issuer validation (RFC 9207). Clients must verify which authorization server actually issued a token before trusting it โ this closes a mix-up attack where a token from the wrong AS gets accepted.
- Protected Resource Metadata (RFC 9728). Servers must now expose a
.well-known/oauth-protected-resourceendpoint (or returnresource_metadatainWWW-Authenticate) so clients can auto-discover the correct authorization server instead of hardcoding it. - Resource Indicators (RFC 8707). Clients must include the target MCP server's URI in token requests. Without this, a malicious server could obtain and replay a token that was meant for a different server โ a confused-deputy attack.
- CIMD replaces DCR. Client ID Metadata Documents are now the preferred registration method; Dynamic Client Registration (RFC 7591) is deprecated but still works during the transition.
application_typedeclaration. Clients now declareweb,native, orpublic_clientat registration โ this fixes the long-standing annoyance where a CLI or desktop client got treated as a web client and had itslocalhostredirect URI rejected.
If you built OAuth support against the earlier draft, the Protected Resource Metadata endpoint and Resource Indicators are the two most likely to actually be missing from an existing implementation.
What gets deprecated (not removed yet)
Three features enter a formal deprecation window under the new lifecycle policy (SEP-2577). They keep working through 2027 at minimum, but plan the migration now rather than when removal is announced:
| Deprecated feature | Replace with |
|---|---|
| Roots | Tool parameters, resource URIs, or server-side configuration |
| Sampling | Direct calls to your LLM provider's API |
| Logging (protocol-level) | stderr for stdio servers; OpenTelemetry for structured observability |
Migration checklist
If you're using an official SDK (TypeScript, Python, Go, etc.): you mostly wait. Tier 1 SDKs are expected to ship 2026-07-28 support within a ten-week window of the final spec. Check your SDK's release milestone, bump the version when it lands, and re-run your test suite โ most of the wire-level work happens inside the SDK.
If you hand-rolled your server against the transport spec directly:
- Remove
initialize/initializedhandling; read protocol version and client info from_metaon every request instead. - Drop any logic keyed on
Mcp-Session-Id. Anywhere you relied on session-scoped state, return an explicit handle from the tool call instead (e.g., acart_idorjob_idthe client passes back on the next call). This also has a nice side effect: the state becomes visible to the model instead of hidden in transport metadata, which tends to produce more reliable multi-step tool use. - Emit
Mcp-MethodandMcp-Nameheaders on requests you send, and validate them on requests you receive โ reject mismatches with-32020. - Add
resultType: "complete", plusttlMs/cacheScope, to list and read results. - Stop pattern-matching on error code
-32002; watch for-32602instead. - If you serve tool-required user input, move off held-open SSE streams and implement
InputRequiredResult/inputResponses. - If you implement your own OAuth flow: add a Protected Resource Metadata endpoint, add Resource Indicators to token requests, and validate the issuer on every auth response.
Best practices
- Test against the conformance suite, not just your own client. The MCP project ships an official conformance suite alongside the spec โ run it before you assume you're compliant.
- Don't remove your old session logic yet if you serve mixed clients. Existing 2025-11-25 clients don't break on July 28; they just don't get the new capabilities. Keep backward compatibility until you're confident your client population has moved.
- Audit gateways and proxies, not just your server code. Any intermediary that inspects
Mcp-Session-Idfor routing or logging needs to switch toMcp-Method/Mcp-Nameโ this is exactly the class of bug that shows up in production, not in local testing, because local testing rarely goes through the real gateway.
Common mistakes to avoid
- Assuming SDK auto-upgrade means zero work. The SDK handles wire-level compliance; it doesn't automatically migrate your session-scoped application logic to explicit handles. You still have to redesign any tool that quietly depended on "the server remembers what happened three calls ago."
- Silently swallowing the
-32002โ-32602change. If your error handling is a big switch statement on error codes, this migration will pass code review and still be wrong in production. - Forgetting the header/body validation is bidirectional. Servers must reject mismatched
Mcp-Method/body pairs โ if you only add the headers without the validation, you get the routing benefit but not the spec compliance. - Treating Roots/Sampling/Logging removal as urgent. It isn't, yet โ but "deprecated through 2027 minimum" is not "permanent." Track it as a backlog item, not a fire drill.
Troubleshooting
Client and server disagree on protocol version. Look for error -32022 (UnsupportedProtocolVersion) โ this replaces the old implicit failures you'd get from a version mismatch during the initialize handshake, which no longer exists.
Tool calls that used to "just work" across multiple turns now fail. Almost always a session-state dependency you haven't replaced with an explicit handle yet. Grep your server for anything reading connection-scoped state outside the request body.
Load balancer suddenly can't route MCP traffic correctly. Confirm it's reading Mcp-Method/Mcp-Name and not still trying to extract routing info from Mcp-Session-Id, which no longer exists on the wire.
FAQ
Does my MCP server break on July 28, 2026 if I do nothing?
Not immediately. Existing 2025-11-25 servers keep working with clients that still speak the old version. The risk is with clients that upgrade and expect the new request shape, or hand-rolled servers/gateways that inspected Mcp-Session-Id directly.
Do I need to rewrite my server if I use an official SDK? Usually no beyond a version bump โ most of this is a wire-protocol change the SDK absorbs. You still need to update any application logic that depended on session-scoped state.
What replaces session state if I relied on it? Explicit handles: return an identifier from one tool call, have the client pass it into the next call as an argument. It's more visible to the model and easier to debug than implicit session state.
Is this the same MCP that ships in GitHub Copilot / GitHub's MCP Server? GitHub's MCP Server has already implemented the new spec ahead of the final release, including dropping its Redis-backed session store โ a useful real-world reference if you want to see the migration applied to a production server.
Will Roots, Sampling, and Logging stop working right away? No โ they're deprecated with a minimum support window through 2027, and actual removal requires a separate SEP approval. Migrate on your own schedule, but don't build new features on top of them.
Key takeaways
| Change | Action required |
|---|---|
Session/initialize removed | Move client identity into _meta; replace session state with explicit handles |
Mcp-Method / Mcp-Name headers required | Emit and validate on every request; reject mismatches with -32020 |
| SSE elicitation replaced | Implement InputRequiredResult / inputResponses round trips |
ttlMs / cacheScope on list results | Add to responses; lets clients cache instead of re-fetching |
OTel trace context in _meta | Wire up traceparent/tracestate/baggage for distributed tracing |
-32002 โ -32602 | Update error-code handling, especially pattern-matched switches |
| Auth: issuer validation, PRM, Resource Indicators, CIMD | Required for spec-compliant OAuth 2.1 servers/clients |
| Roots, Sampling, Logging | Deprecated, supported through 2027 minimum โ migrate, don't panic |
Further Reading
- Amazon Bedrock AgentCore Runtime: Stateful MCP Revolutionizing AI in 2026
- Amazon Bedrock Agents Classic Enters Maintenance Mode: What to Do Before July 30, 2026
- CloudWatch Embraces OpenTelemetry: Simplified Metrics in AWS (Finally!)
- Model Context Protocol: 2026-07-28 Release Candidate (official)
- GitHub MCP Server supports the next MCP specification (official)