What the new MCP specification breaks in your server
Table of contents
- Key takeaways
- What a stateless core actually means
- What travels in every request now
- MRTR and asking the user for input without an open stream
- Why a gateway can now authorize without reading the body
- Cacheable list results with ttlMs and cacheScope
- What the extensions framework is
- What is deprecated and when it really goes
- What to change in your server, and in what order
- What changes in authorization
- Where the SDKs stand
- What stops being true in what we published before
- Frequently asked questions
- Do I have to migrate my MCP server now?
- Does removing the session make my server stateless by magic?
- Who governs the MCP specification today?
- Conclusion
- Sources
The 2026-07-28 revision turns MCP into a stateless protocol. The initialize exchange and the Mcp-Session-Id header are gone, and the protocol version plus client capabilities now travel inside the _meta field of every request. In exchange, Roots, Sampling and Logging are deprecated with a twelve-month window.
You have an MCP server in production and a revision has just landed that removes its opening handshake and its session. The 2026-07-28 specification is the biggest break in the protocol since remote MCP existed, and most of what was published about MCP servers over the last two years describes an architecture this revision repeals. Here is what changes, what gets deprecated, and in what order to touch your server.
Key takeaways
- The
initialize/notifications/initializedexchange and theMcp-Session-Idheader are gone. Every request describes itself inside_meta. - Roots, Sampling and Logging are deprecated. Earliest removal is the first revision released on or after 2027-07-28.
- Server-to-client requests now resolve through MRTR: the server answers
resultType: "input_required"and the client retries. Mcp-MethodandMcp-Nameare required on every POST, and the server must reject any header/body mismatch with-32020.ping,logging/setLeveland stream resumption viaLast-Event-IDare not deprecated: they are removed.
What a stateless core actually means
Until this revision, an MCP conversation opened with initialize, continued with notifications/initialized, and stayed pinned to a session the server identified through the Mcp-Session-Id header. That model forced every request from one client onto the same instance, or forced you to share session state between instances.
The 2026-07-28 revision removes both: the opening exchange under SEP-2575, the session header under SEP-2567. Every request now carries its own protocol version and the client capabilities inside the _meta field, under the keys io.modelcontextprotocol/protocolVersion and io.modelcontextprotocol/clientCapabilities. Clients should identify themselves with io.modelcontextprotocol/clientInfo, and servers should return io.modelcontextprotocol/serverInfo in the _meta of every result.
The practical outcome is that any request can land on any instance behind a plain round-robin load balancer with no shared storage. Sticky sessions stop being a protocol requirement.

This is the startup that no longer exists. The three arrows in the diagram, initialize, its response and initialized, are exactly what the revision retires.
The small print is worth reading. The maintainers put it this way in the release post: "Dropping the protocol-level session doesn’t force your application to be stateless. If your server needs to carry state across calls, mint an explicit handle from a tool and have the model pass it back as an argument. We found this works better than session state hidden in the transport – the model can see the handle and thread it between tools."
What travels in every request now
A tool call under the new revision looks like this:
POST /mcp HTTP/1.1
Content-Type: application/json
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: get_weather
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_weather",
"arguments": { "location": "Seattle, WA" },
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientInfo": { "name": "ExampleClient", "version": "1.0.0" },
"io.modelcontextprotocol/clientCapabilities": {}
}
}
}
The server still has a way to announce itself: server/discover, which servers must implement and which clients may call before anything else to pick a version up front. The difference with the old initialize is that it is optional for the caller and opens no session.
The standalone GET stream is gone too. Change notifications are requested with subscriptions/listen, a single long-lived response the client opts into by type: toolsListChanged, promptsListChanged, resourcesListChanged and resourceSubscriptions. Log level becomes per-request through io.modelcontextprotocol/logLevel in _meta, and servers must not emit notifications/message for requests that did not ask for it.
MRTR and asking the user for input without an open stream
The Multi Round-Trip Requests pattern (SEP-2322) replaces the requests the server used to initiate. The specification’s own note leaves no room: "Servers MUST send server-to-client requests (such as roots/list, sampling/createMessage, or elicitation/create) using the MRTR pattern. The previous pattern of server-initiated requests is no longer supported. This is a breaking change."
The mechanism is a retry. The server answers with resultType: "input_required", an inputRequests map of what it needs, and an opaque requestState. The client gathers the answers and re-issues the original request with inputResponses and that requestState copied verbatim. The JSON-RPC id must differ between the attempt and the retry, because they are independent requests.
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"resultType": "input_required",
"inputRequests": {
"github_login": {
"method": "elicitation/create",
"params": {
"mode": "form",
"message": "Please provide your GitHub username"
}
}
},
"requestState": "AEAD-protected blob"
}
}
Only three requests accept this response: prompts/get, resources/read and tools/call. On any other one, the server must not send it.
The pitfall in this revision is requestState. The specification requires treating it as attacker-controlled input: if it influences authorization, resource access or business logic, you must protect it with HMAC or AEAD and reject state that fails verification. It also recommends encoding the authenticated principal, a short expiry and an identifier of the originating request inside it, to bound reuse.
Why a gateway can now authorize without reading the body
Every Streamable HTTP POST must carry three headers: MCP-Protocol-Version, Mcp-Method (copied from method) and Mcp-Name (copied from params.name or params.uri, and only on tools/call, resources/read and prompts/get). They are required for compliance, under SEP-2243.
The stated goal is that gateways, rate limiters and web application firewalls route and meter on headers instead of opening the JSON. There is also an extension, x-mcp-header, that lets a server mark tool parameters so the client mirrors them into Mcp-Param-{Name} headers. It is optional for servers but clients must support it, it only covers primitive types, and non-ASCII values are encoded with the =?base64?…?= sentinel.
The counterweight is that those headers are not free. A server that processes the body must reject with 400 Bad Request and error -32020 (HeaderMismatch) any request whose header does not match the body. That way, a load balancer and the server do not end up deciding from different sources of truth.
And the specification itself warns intermediaries to check that MCP-Protocol-Version names a version that mandates header/body validation before trusting the header. Routing on headers without that check is a hole, not a shortcut.
Cacheable list results with ttlMs and cacheScope
Results from tools/list, prompts/list, resources/list, resources/read and resources/templates/list gain two fields required by SEP-2549 through the new CacheableResult interface. ttlMs is a freshness hint in milliseconds that lets clients cache instead of polling. cacheScope is "public" or "private" and decides whether a shared intermediary may store the response.
One detail slips by and matters if you pay for tokens: servers should return tools from tools/list in a deterministic order. A catalogue that reshuffles on every call invalidates the upstream model’s prompt cache. Sorting it costs nothing and shows up on the bill.
What the extensions framework is
Extensions stop being an improvisation and get a vendor-prefixed identifier in the form {prefix}/{name}. Official ones use io.modelcontextprotocol. There are four today: MCP Tasks, MCP Apps, OAuth Client Credentials and Enterprise-Managed Authorization.
Tasks is the one that affects existing code most: it moves out of the experimental core into the io.modelcontextprotocol/tasks extension, swaps the blocking tasks/result for a polling tasks/get, adds tasks/update for client input, and drops tasks/list (SEP-2663).
Extensions are advertised in the extensions field of the capabilities, evolve on their own schedule, and are always disabled by default: they need an explicit opt-in from the developer. If your client does not support an extension, whoever offers it must fall back to core behaviour or reject the request.
What is deprecated and when it really goes
The revision introduces a formal feature lifecycle policy (SEP-2596) with three states and a twelve-month minimum window. The deprecated features registry is the normative reference, and its dates do not match the announcement’s summary:
| Feature | Deprecated in | Migration | Earliest removal |
|---|---|---|---|
| Roots | 2026-07-28 |
Pass files or directories as tool parameters, resource URIs or configuration | First revision released on or after 2027-07-28 |
| Sampling | 2026-07-28 |
Integrate directly with the LLM provider API | First revision released on or after 2027-07-28 |
| Logging | 2026-07-28 |
Log to stderr on stdio, or use OpenTelemetry |
First revision released on or after 2027-07-28 |
| Dynamic Client Registration | 2026-07-28 |
Client ID Metadata Documents (CIMD) | First revision released on or after 2027-07-28 |
| HTTP+SSE transport | 2025-03-26 |
Streamable HTTP | Three months after SEP-2596 reaches Final |
That last row deserves a warning. The release announcement talks about a year-long offramp for HTTP+SSE, while the normative registry gives it three months from the moment the policy reaches Final. When the blog and the specification disagree, the specification wins: HTTP+SSE has the shortest exit in the batch, not the longest.
And there is a separate category, the things that are not deprecated but removed with no window: ping, logging/setLevel, notifications/roots/list_changed, resources/subscribe, resources/unsubscribe, the standalone GET stream and resumption via Last-Event-ID. If a long response is cut, the whole request is now lost and the client must re-issue it with a new id.
What to change in your server, and in what order
- Inventory session usage. Find every place you store something keyed by
Mcp-Session-Idand decide whether it becomes an explicit handle returned by a tool or moves into your own store. - Implement
server/discover. It is the only new piece a server is obliged to expose, and it lets you advertise supported versions while old and new clients coexist. - Add the headers and their validation.
MCP-Protocol-Version,Mcp-MethodandMcp-Name, rejecting with400and-32020when they do not match the body. - Convert Sampling, Elicitation and Roots to MRTR. This is the change with the most code behind it, because your tool goes from waiting for an answer to finishing and starting again with signed state.
- Fill in
ttlMsandcacheScope, and sorttools/listdeterministically. - Adjust the error codes.
HeaderMismatchmoves from-32001to-32020,MissingRequiredClientCapabilityfrom-32003to-32021,UnsupportedProtocolVersionfrom-32004to-32022, and resource-not-found from-32002to-32602. - Move logging to
stderror OpenTelemetry, and drop Roots and Sampling from any new capabilities.
What changes in authorization
Three hardening moves and one replacement. Authorization servers should return the iss parameter from RFC 9207, and clients must validate it against the recorded issuer before redeeming the code (SEP-2468), which closes the authorization-server mix-up hole.
Clients must set application_type during dynamic registration (SEP-837). It is the reason OAuth flows for desktop and command-line applications kept failing with a localhost redirect_uri error. And credentials are bound to the issuer that minted them: key them by issuer identifier, never reuse them with another authorization server, and re-register when it changes (SEP-2352).
The replacement is RFC 7591 Dynamic Client Registration, now deprecated in favour of Client ID Metadata Documents. It keeps working for compatibility with authorization servers that lack CIMD support, but it is code with an expiry date.
Where the SDKs stand
Checked against the repositories on 30 August 2026:
| SDK | Version with 2026-07-28 |
Date | Status today |
|---|---|---|---|
| TypeScript | @modelcontextprotocol/server and @modelcontextprotocol/client 2.0.0 |
2026-07-27 | Stable. The v1 line stays on @modelcontextprotocol/sdk 1.30.0 |
| Python | mcp 2.0.0 |
2026-07-28 | Stable, 2.1.1 today (2026-08-25) |
| Go | v1.7.0 | 2026-07-28 | Stable |
| C# | v2.0.0 | 2026-07-28 | Stable, v2.2.0 today (2026-08-13) |
| Rust | rmcp 3.0.0 |
2026-07-28 | Stable, 3.1.4 today (2026-08-20) |
Two details that keep getting repeated wrong. First: the 28 July announcement said the Rust SDK supported the revision in beta, and that same day rmcp 3.0.0 shipped stable. Its README declares that it implements the stable 2026-07-28 specification while staying compatible with 2025-11-25 and earlier. Quoting "Rust in beta" now means quoting a twelve-hour-old snapshot.
Second: TypeScript v1 is not dead, it receives bug fixes and security patches for at least six months from the v2 release.
What stops being true in what we published before
This site has six MCP articles and the most recent is from May 2026, so all of them describe the older protocol. It is worth naming which sentence expires in each.
In our balance sheet on MCP maturity in 2025 we wrote that MCP is a session protocol with state, resources, notifications and permissions, and that this structural difference was what enabled the ecosystem. The 2026-07-28 revision repeals exactly that sentence: the core is stateless request and response.
The complete MCP guide for 2026 describes starting a local server as a handshake exchange before discovering tools. Under the new revision there is no handshake: discovery is an optional call and every request describes itself.
Building your own MCP server mentions that a client can offer the server sampling, roots and elicitation. All three still exist, but they are no longer requested the old way and two of them are deprecated: if you start today, do not adopt them.
The maps in the consolidated ecosystem and multi-vendor patterns hold up on what they say about adoption. And installing a local MCP server for your editor still works because stdio changed far less than HTTP.
Frequently asked questions
Do I have to migrate my MCP server now?
There is no obligation. Revision 2025-11-25 remains valid and modern clients fall back to initialize when they reach an older server. The pressure is calendar pressure: the deprecated features are not removed before the first revision released on or after 2027-07-28, so you have nearly a year to plan.
Does removing the session make my server stateless by magic?
No. The specification removes state from the transport, not from your application. If you need continuity across calls, the maintainers recommend minting an explicit handle from a tool and having the model pass it back as an argument. That way the handle is visible in the context.
Who governs the MCP specification today?
Anthropic donated MCP to the Agentic AI Foundation, a directed fund under the Linux Foundation, on 9 December 2025, alongside goose from Block and AGENTS.md from OpenAI as founding projects. At the time the project reported 97 million monthly SDK downloads and 10,000 active servers.
Conclusion
If your MCP server speaks 2025-11-25 and works, it is not broken and nobody is switching it off tomorrow. What changed is that there is now a date: the deprecated features become eligible for removal from 2027-07-28, and the HTTP+SSE transport runs out before that.
The migration that actually costs is MRTR, because it forces you to split in two any tool that asks something mid-execution. Start there and leave the tools/list ordering and the error codes for last. The protocol’s growth justifies the bother: from 97 million monthly downloads in December 2025 to close to 500 million in July 2026. TypeScript and Python have each crossed a billion cumulative.
La versión en español de este artículo está en Qué rompe la nueva especificación de MCP en tu servidor.
Sources
- Model Context Protocol Blog, the 2026-07-28 specification
- Model Context Protocol, key changes in the 2026-07-28 revision
- Model Context Protocol, deprecated features registry
- Model Context Protocol, Multi Round-Trip Requests pattern
- Model Context Protocol, Streamable HTTP transport
- rmcp 3.0.0, Rust SDK release notes
- RFC 9207, OAuth 2.0 authorization server issuer identification
- Linux Foundation, formation of the Agentic AI Foundation