MCP 2026-07-28 Spec Migration: 8 Breaking Changes Stateless Servers Must Handle
If you deployed a remote Model Context Protocol server six months ago, you almost certainly built it around three things the protocol no longer has: an initialize handshake, an Mcp-Session-Id header, and a held-open stream for server-initiated requests. The MCP 2026-07-28 spec removed all three. It is the largest revision since remote MCP launched, and it is deliberately a breaking one — the maintainers chose to fix the scalability gaps rather than paper over them. The payoff is real: any request can now land on any instance behind a plain round-robin load balancer, with no sticky routing and no shared session store. The cost is a migration that touches your transport, your gateway config, your auth client, and any agent design that relied on the server calling back into the model.
What this covers: the eight changes that will break a server built against 2025-11-25, the concrete fix for each, the before/after deployment topology, what losing Sampling means for your agent architecture, a compatibility matrix across revisions, and a staged migration checklist.
Context and Background
MCP shipped in November 2024 as a stateful, bidirectional protocol. That shape came from its origin: the first servers were local, spoken to over STDIO by a desktop client, and a long-lived connection was simply how a subprocess works. The initialize/notifications/initialized handshake negotiated capabilities once, and everything afterwards assumed both sides remembered that negotiation.
Remote MCP inherited that design and paid for it. A protocol-level session means a request is only meaningful to the instance that holds it, which in practice means sticky load balancing keyed on Mcp-Session-Id, or an external session store, or both. Neither is free. Sticky routing defeats autoscaling, complicates blue/green deploys, and turns a single pod restart into a wave of client-visible failures. A shared store adds a network hop and a new availability dependency to every single tool call.
The maintainers signalled the direction in December 2025 with The Future of MCP Transports, then executed it across a release candidate and four Tier 1 SDK betas before publishing the normative text on 28 July 2026. According to the official release announcement, the Tier 1 SDKs were seeing close to half a billion downloads a month by that point, with both the TypeScript and Python SDKs past a billion cumulative downloads. This was not a protocol with room to break things casually, which is why the release also introduced a formal feature lifecycle with a minimum twelve-month deprecation window.
Our MCP architecture primer describes the protocol as it stood before this revision, and it remains a useful mental model for the object graph — tools, resources, prompts. What changed is the plumbing underneath it. If you want the security angle on the new auth posture, the MCP server threat model covers the attack surface this revision is hardening.
One reassurance before the alarming part: nothing switched off on 28 July. Servers speaking 2025-11-28-era revisions keep working, and clients that speak the new revision fall back to the initialize handshake when they reach an older server. This is a migration you schedule, not an outage you react to.
The Stateless Core: What Actually Changed at the Wire
The MCP 2026-07-28 spec replaces a stateful, connection-oriented protocol with a request/response one. Every request now carries its own protocol version, client identity and capabilities in _meta, so it is fully self-describing. There is no negotiated session for a server to look up, and therefore no reason for a load balancer to send a client back to the same instance twice.
That single sentence is the source of most of the eight breaking changes. Everything that previously depended on shared connection state — the handshake, the session header, the GET stream for server-to-client messages, stream resumability, per-connection list results, server-initiated sampling — either moved or disappeared.

Figure 1: Session-affinity routing under 2025-11-25 versus stateless round-robin under 2026-07-28.
The upper half of Figure 1 is the topology most remote MCP deployments run today. The client’s Mcp-Session-Id pins it to Server A; Server B physically cannot serve that request without reading shared state, so the balancer must either honour affinity or every instance must consult Redis on the hot path. The lower half is what the new revision permits: three identical stateless instances, a plain round-robin balancer with no affinity configuration, and any cross-call state carried explicitly as a server-minted handle threaded through tool arguments by the model itself.
Sessions and the handshake are gone (breaking change 1 and 2)
SEP-2567 removes protocol-level sessions and the Mcp-Session-Id header from the Streamable HTTP transport entirely. SEP-2575 removes the initialize request and the notifications/initialized notification that followed it.
In their place, each request carries what it needs. The protocol version and client capabilities travel as _meta keys: io.modelcontextprotocol/protocolVersion and io.modelcontextprotocol/clientCapabilities. Clients SHOULD identify themselves on every request via io.modelcontextprotocol/clientInfo, and servers SHOULD identify themselves in each result’s _meta via io.modelcontextprotocol/serverInfo. A version the server cannot speak returns UnsupportedProtocolVersionError.
POST /mcp HTTP/1.1
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: search
{"jsonrpc":"2.0","id":1,"method":"tools/call",
"params":{"name":"search","arguments":{"q":"otters"},
"_meta":{"io.modelcontextprotocol/clientInfo":{"name":"my-app","version":"1.0"}}}}
Note what is absent: no session header, no prior handshake, no assumption that this instance has seen this client before.
There is a consequence buried in SEP-2567 that is easy to miss and expensive to discover late. List endpoints — tools/list, resources/list, prompts/list — no longer vary per connection. If your server currently returns a different tool catalogue depending on what the client negotiated during initialize, that pattern is gone. Tool visibility now has to be a function of the authenticated principal and the request itself, not of connection state. For multi-tenant servers that gated tools by session, this is usually the single largest refactor in the whole migration.
server/discover replaces initialize as the capability probe
Removing the handshake does not mean clients lose the ability to ask what a server can do. SEP-2575 adds server/discover, and servers MUST implement it. It advertises supported protocol versions, capabilities and identity. Clients MAY call it before any other request for up-front version selection, or use it as a backward-compatibility probe — which is how it works on STDIO, where there is no HTTP status code to fall back on.
The important distinction is optional versus mandatory. Under the old model, the handshake was a required round trip before any useful work. Under the MCP 2026-07-28 spec, server/discover is a convenience for clients that want capabilities in advance. A client that already knows what it wants can fire tools/call as its very first request. That removes a full round trip from cold-start latency on every new connection, which matters when an agent opens connections to a dozen servers at the start of a task.
What the new topology actually buys you
The operational gains are concrete rather than theoretical. You can delete sticky-session configuration from your ingress. You can scale to zero and back without stranding clients. A rolling deploy stops being a coordinated session-drain exercise. You can put a CDN or an edge gateway in front of the server and have it do real work, because — as the next section covers — the routing information it needs is now in headers rather than buried in a JSON body.
Statelessness at the protocol level does not force statelessness on your application. The maintainers are explicit about the replacement pattern: 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 to subsequent calls. Their argument for why this is better is worth taking seriously — the model can see the handle. State hidden in the transport is invisible to the reasoning layer, so the model cannot decide to abandon it, branch it, or hand it to a different tool. An explicit handle is data the agent can reason about.
The Remaining Six Breaking Changes
Changes 1 and 2 are the headline. The other six are where migrations actually stall, because they are individually small and collectively pervasive.
3. Mcp-Method and Mcp-Name headers are now mandatory
SEP-2243 requires standard MCP request headers on Streamable HTTP POST requests. Mcp-Method rides on every request; Mcp-Name accompanies requests that name a specific tool, resource or prompt. The same SEP adds x-mcp-header support for passing custom headers derived from tool parameters.
A server that ignores these headers will not break immediately, but a client that omits them is non-conformant, and the spec defines a HeaderMismatchError for the case where the headers and the body disagree. Check both: a proxy that rewrites or strips unknown headers will now silently break conformance.

Figure 3: Header-based routing lets a gateway route, authorize, meter and cache without parsing the JSON-RPC body.
Figure 3 shows why this change exists. Under the old protocol, an edge gateway that wanted to rate-limit tools/call differently from tools/list, or apply a stricter policy to a delete_records tool than to a search tool, had to buffer and parse the request body. That is expensive at the edge and awkward in most WAF rule languages. With method and name in headers, a plain layer-7 rule does the job: route by Mcp-Method, authorize by Mcp-Name, meter per tool, and cache list responses. This is the change that makes MCP behave like an ordinary HTTP workload to the infrastructure that sits in front of it.
4. Every result needs a resultType, and server-initiated requests become MRTR
This is the change with the widest blast radius in client code. SEP-2322 introduces Multi Round-Trip Requests and adds a required resultType field to all results: "complete" for ordinary results, "input_required" for an interim result that needs something from the client.
MRTR replaces the server-initiated elicitation/create, sampling/createMessage and roots/list requests, all of which previously required a stream held open in the server-to-client direction. Instead, the server returns an InputRequiredResult whose inputRequests field carries a map of the requests it needs answered, alongside an opaque requestState blob. The client gathers the answers and retries the original call, this time including inputResponses and echoing back the exact requestState it received.

Figure 2: An MRTR exchange. The retry lands on a different instance, and nothing breaks.
Figure 2 is the whole argument for MRTR in one picture. The first leg of the call reaches Instance A. The user confirmation arrives seconds or minutes later, and the retry lands on Instance B — a different pod, possibly in a different availability zone. Because everything the server needs to resume is in requestState and inputResponses, Instance B can complete the work without ever having seen the first leg. Under the old bidirectional model this exchange was impossible without affinity.
Two compatibility rules matter here. Clients MUST treat a result from an earlier-protocol server that omits resultType as "complete" — so a strict schema validator that rejects results missing the field will break every legacy server you talk to. And the requestState blob is untrusted in both directions: clients must treat it as opaque, servers must treat it as attacker-controlled, and any server that lets requestState influence authorization or resource access needs to integrity-protect it. Signing it is not optional if it carries anything security-relevant.
The same SEP-2322 work removed the notifications/elicitation/complete notification and the elicitationId field on URL-mode elicitation requests, both of which were only introduced in 2025-11-25. Under MRTR the client learns the outcome by retrying, so a server-initiated completion signal no longer fits. Servers that need to correlate an elicitation across retries encode their own identifier inside requestState.
5. subscriptions/listen replaces the GET stream, and resumability is gone
SEP-2575 removes the HTTP GET endpoint along with resources/subscribe and resources/unsubscribe. In their place is subscriptions/listen: a single long-lived POST-response stream that a client opts into, naming the notification types it wants — toolsListChanged, promptsListChanged, resourcesListChanged, resourceSubscriptions. The server acknowledges and tags each notification with io.modelcontextprotocol/subscriptionId.
Request-scoped notifications behave differently and this trips people up. notifications/progress and notifications/message continue to flow on the response stream of the request they relate to — not on the subscriptions/listen stream. If you consolidate all notification handling onto the subscription stream during migration, progress updates will silently vanish.
The harder part of this change is what was removed rather than replaced. SSE stream resumability and message redelivery are gone: no Last-Event-ID header, no SSE event IDs. A broken response stream loses the in-flight request, and the client MUST re-issue it as a new request with a new request ID. If your server has long-running tools whose results were previously recovered by resuming a dropped stream, that safety net no longer exists. The intended replacement is the Tasks extension, covered below, which gives you a pollable handle instead of a resumable stream.
6. ping, logging/setLevel and roots list-changed notifications removed
Three methods disappear outright under SEP-2575: ping, logging/setLevel, and notifications/roots/list_changed.
ping was a liveness check for a connection that no longer exists conceptually; use an ordinary HTTP health check. logging/setLevel was connection-scoped configuration, which is exactly the kind of state the revision is eliminating. Log level is now set per request via io.modelcontextprotocol/logLevel in _meta, and — this is the part that breaks things quietly — servers MUST NOT emit notifications/message for requests that did not include that field. If your server logs to the client by default, it will go silent for every client that has not been updated to set the field. Audit your observability before you cut over, not after.
7. List results must carry ttlMs and cacheScope
SEP-2549 introduces a CacheableResult interface and requires ttlMs and cacheScope on results returned by tools/list, prompts/list, resources/list, resources/read and resources/templates/list. ttlMs is a freshness hint in milliseconds. cacheScope is "public" or "private" and controls whether shared intermediaries may cache the response. Both complement rather than replace the existing listChanged notifications.
Separately, servers SHOULD return tools from tools/list in a deterministic order. That sounds cosmetic and is not. A tool catalogue is injected into the model’s prompt, so a catalogue that reshuffles between calls invalidates the upstream provider’s prompt cache on every reconnect. Deterministic ordering plus a sensible TTL means the tool block stays byte-identical, the prompt cache keeps hitting, and per-call cost drops materially on any agent that reconnects frequently.
The migration work is small but easy to forget: if your server builds list responses by hand rather than through an SDK helper, add both fields. Pick ttlMs conservatively — a long TTL on a catalogue that changes with entitlements is a correctness bug, not a performance win. Use cacheScope: "private" for anything whose content depends on the authenticated principal.
8. Authorization hardening, and DCR gives way to CIMD
The authorization changes are a cluster rather than a single switch, and together they are the most involved part of the migration for any client that talks to third-party servers.
SEP-2468 requires authorization servers to include the iss parameter in authorization responses per RFC 9207, and clients MUST validate a present iss against the recorded issuer before redeeming the code. This closes an authorization-server mix-up hole — a genuine attack, not a theoretical one.
SEP-2352 binds client credentials to the authorization server that issued them. Clients MUST key persisted credentials by issuer identifier, MUST NOT reuse them with a different authorization server, and MUST re-register when the authorization server changes. If your client caches a client_id keyed only by MCP server URL, that cache is now wrong.
SEP-837 requires clients to specify an appropriate application_type during Dynamic Client Registration. Omitting it defaults to "web" under OpenID Connect, which conflicts with localhost redirect URIs — the long-standing reason desktop and CLI clients hit mysterious redirect_uri errors. Native apps, CLI tools and locally-hosted web apps should send application_type: "native".
And the strategic change: DCR itself is formally deprecated in favour of Client ID Metadata Documents.

Figure 4: The client-registration priority order in the new revision, ending in issuer-bound credential storage.
Figure 4 encodes the priority order the spec lays out. A client that supports all options SHOULD try pre-registered credentials first; then CIMD if the authorization server advertises client_id_metadata_document_supported in its OAuth metadata; then DCR as a fallback if a registration_endpoint exists; and only then prompt the user.
CIMD inverts how registration works. Instead of POSTing to a registration endpoint and receiving a client_id, the client hosts a JSON metadata document at a stable HTTPS URL and uses that URL as its client_id. The URL must use the https scheme and contain a path component — https://example.com/client.json — and the document must include at least client_id, client_name and redirect_uris, with client_id matching the document URL exactly.
{
"client_id": "https://app.example.com/oauth/client-metadata.json",
"client_name": "Example MCP Client",
"client_uri": "https://app.example.com",
"redirect_uris": [
"http://127.0.0.1:3000/callback",
"http://localhost:3000/callback"
],
"grant_types": ["authorization_code"],
"response_types": ["code"],
"token_endpoint_auth_method": "none"
}
The practical payoff is portability. A CIMD client_id is a self-hosted URL that any authorization server can resolve on demand, so it works across authorization servers with no re-registration. DCR credentials are, by SEP-2352, permanently bound to one issuer. For a client that connects to hundreds of MCP servers, that difference is the whole argument.
The operational cost lands on whoever hosts the document. It becomes a public availability dependency on your OAuth flow: if it 404s or serves stale JSON, logins fail. Serve it from static hosting with sensible cache headers, version the path rather than mutating in place, and treat a change to redirect_uris as a deploy with a rollback plan. Authorization servers SHOULD cache metadata respecting HTTP cache headers, which means a bad document can be sticky for as long as you told them to keep it.
Compatibility Matrix Across Spec Revisions
The formal feature lifecycle introduced by SEP-2596 defines three states — Active, Deprecated, Removed — with a minimum twelve-month deprecation window and a published registry of deprecated features. That is the rule that turns this table into a planning instrument rather than a snapshot.
| Feature | 2025-11-25 | 2026-07-28 | Migration |
|---|---|---|---|
initialize / notifications/initialized |
Required | Removed | Per-request _meta; server/discover for probing |
Mcp-Session-Id header |
Required for sessions | Removed | Server-minted handles as tool arguments |
Mcp-Method / Mcp-Name headers |
Absent | Required on HTTP POST | Emit from client; stop stripping at proxies |
resultType on results |
Absent | Required | Treat omitted as "complete" from old servers |
| Server-initiated sampling / elicitation / roots | Server-to-client requests | Replaced by MRTR | InputRequiredResult + retry with inputResponses |
HTTP GET stream, resources/subscribe |
Active | Removed | subscriptions/listen with opt-in types |
SSE resumability (Last-Event-ID) |
Active | Removed | Re-issue with a new request ID; or Tasks |
ping, logging/setLevel, roots list_changed |
Active | Removed | HTTP health check; per-request logLevel in _meta |
ttlMs / cacheScope on list results |
Absent | Required | Add to hand-rolled list responses |
| Tasks | Experimental core | Extension io.modelcontextprotocol/tasks |
Poll tasks/get; tasks/update for input |
| Roots, Sampling, Logging | Active | Deprecated (≥12 months) | Tool params; direct LLM APIs; stderr or OpenTelemetry |
| Dynamic Client Registration | Recommended | Deprecated | CIMD, with DCR as fallback |
| HTTP+SSE transport | Deprecated since 2025-03-26 | Deprecated under lifecycle policy | Streamable HTTP |
| Resource-not-found error code | -32002 |
-32602 |
Stop matching on the literal -32002 |
Two error-code details deserve their own mention because they break clients that pattern-match on numbers. A missing resource now returns JSON-RPC -32602 (Invalid Params) instead of the MCP-custom -32002. And the revision defines an allocation policy partitioning the server-error range: -32000 to -32019 stays implementation-defined with existing SDK usage grandfathered, while -32020 to -32099 is reserved for the specification. Codes introduced during the draft were renumbered accordingly — HeaderMismatch from -32001 to -32020, MissingRequiredClientCapability from -32003 to -32021, and UnsupportedProtocolVersion from -32004 to -32022.
Tasks moving out of the experimental core into the io.modelcontextprotocol/tasks extension under SEP-2663 is the intended answer for long-running work. The redesigned extension replaces the blocking tasks/result method with polling via tasks/get, adds tasks/update for client-to-server input mid-task, removes tasks/list, and lets servers return task handles unsolicited without per-request opt-in. If you previously leaned on stream resumability to survive a thirty-second tool call, a task handle is what you reach for now.
Finally, the extensions field added to both ClientCapabilities and ServerCapabilities is what makes the extensions framework formal rather than ad hoc. Tasks, MCP Apps and Enterprise Managed Authorization all sit outside the core protocol and negotiate through it. This is how the spec intends to keep the core small while the ecosystem grows — and it is the mechanism you should reach for before proposing a core change.
What Removing Sampling Means for Your Agent Design
Sampling is deprecated rather than removed, and it keeps working for at least twelve months. But deprecation here is a design signal, and it invalidates a specific and once-popular architecture.
Sampling let a server ask the client to run an inference on its behalf. The appeal was that a server could be genuinely intelligent without holding an API key or paying for tokens — it borrowed the client’s model. Patterns built on this include servers that summarise their own large outputs before returning them, servers that route a request to one of several internal handlers by asking the model to classify it, and servers that generate a natural-language explanation alongside structured data.
Every one of those designs needs rework. The spec’s suggested migration is blunt: integrate directly with LLM provider APIs instead of Sampling. That moves three things onto the server operator that previously sat with the client — the API key, the token bill, and the model choice.
The consequence people underestimate is the model-choice one. A sampling-based server inherited whatever model the user was already using, which meant it automatically benefited from the client’s model upgrades and honoured the user’s model preferences. A server that calls a provider API directly pins a model. When that model is deprecated, your server breaks, and you now own a model-lifecycle problem you did not have before.
There is a cleaner alternative that MRTR makes viable, and it is worth considering before you reach for a provider SDK. Because inputRequests is a map of requests answered in one round trip, a server can ask for a user confirmation and a model completion together and receive both in matching inputResponses. During the deprecation window, that is a way to keep borrowing the client’s model while already being on the stateless transport — the mechanism is MRTR, the capability is still Sampling. It buys you time rather than solving the problem, but a year of time is worth having.
Roots and Logging carry lighter migrations. Instead of Roots, pass directories or files via tool parameters, resource URIs, or server configuration. Instead of Logging, log to stderr on STDIO or emit OpenTelemetry — and the revision helps here, documenting trace-context propagation conventions for the _meta keys traceparent, tracestate and baggage under SEP-414. Cross-service tracing across an agent’s tool calls is now a documented convention rather than something each vendor invents. If you are running the kind of multi-server topology described in our multi-agent orchestration guide, that convention is the difference between a readable trace and a pile of disconnected spans.
Trade-offs, Gotchas, and What Goes Wrong
The SDK upgrade and the protocol upgrade are two separate decisions. Python and TypeScript moved to v2 lines with their own breaking changes — FastMCP becomes MCPServer in Python; TypeScript retires the monolithic @modelcontextprotocol/sdk in favour of @modelcontextprotocol/server and @modelcontextprotocol/client, ESM-only on Node.js 20+. Neither rename has anything to do with the wire protocol. Conflating them turns a transport migration into a framework migration on the same branch, which is how migrations slip a quarter. Our FastMCP versus official SDK comparison is the right place to evaluate the framework question separately.
Upgrading the SDK does not necessarily change what you speak on the wire, and the default differs per language. In the TypeScript and Go SDKs the new revision is an explicit opt-in when you wire up the transport — in Go, StreamableHTTPOptions.Stateless = true; leave it unset and clients negotiate down. In Python and C# the new revision comes with the upgrade: the Python v2 HTTP app answers both revisions from one endpoint, and the C# preview’s HTTP transport defaults to stateless mode. Assuming a uniform default across your polyglot fleet will produce exactly one surprised service.
Pin your Python dependency bound now. If you publish a library depending on the Python mcp package, an upper bound such as mcp>=1.27,<2 prevents the v2 release from surprising downstream users. This is a five-minute change that prevents a class of support ticket you cannot fix retroactively.
Per-connection tool filtering is the hidden refactor. Because list endpoints no longer vary per connection, any server that showed different tools to different sessions needs its authorization model rebuilt around the request principal. Teams routinely scope this as “remove the session header” and discover the real work in week three.
Losing resumability is a real regression for long tools. There is no protocol-level recovery for a dropped stream any more. If you do not adopt the Tasks extension, a network blip during a slow tool call means the client re-issues the whole thing — and if that tool is not idempotent, you have just double-charged someone. Idempotency keys stop being a nicety.
Deprecated is not the same as safe to ignore. Roots, Sampling, Logging, the HTTP+SSE transport and DCR all keep working, with at least twelve months guaranteed. The failure mode is not breakage; it is building something new on a feature with a published expiry date and rediscovering it during a crunch. New implementations should simply not adopt them.
Do not put security-relevant data in requestState without signing it. It round-trips through the client. Treat it as attacker-controlled input on the way back, exactly as you would a cookie.
Practical Recommendations
Treat this as a transport migration with an auth migration riding alongside, and sequence them so you are never debugging both at once. Start by inventorying what you actually depend on: grep for Mcp-Session-Id, initialize, sampling/createMessage, roots/list, logging/setLevel, ping, Last-Event-ID, and the literal -32002. That list is your real scope, and it is usually shorter than the changelog suggests.
Then run the new revision behind the old topology first. Keep sticky routing in place while you validate that requests succeed without session state — that isolates protocol bugs from infrastructure bugs. Only when the server is genuinely stateless do you strip affinity from the ingress, and that step should be a config change you can revert in seconds.
For clients, the auth work is the long pole. CIMD needs a hosted document, a hosting story, and a rollback plan before it needs any code. Build it, serve it, verify it resolves from outside your network, and only then switch the client’s registration path.
A staged checklist:
- [ ] Inventory session-dependent code paths and per-connection tool filtering.
- [ ] Replace session state with server-minted handles passed as tool arguments.
- [ ] Implement
server/discover; it is mandatory for servers. - [ ] Emit and preserve
Mcp-MethodandMcp-Name; check proxies do not strip them. - [ ] Add
resultTypeto all results; treat omitted as"complete"when reading. - [ ] Port server-initiated sampling/elicitation/roots to MRTR; sign
requestState. - [ ] Move change notifications to
subscriptions/listen; leave progress on the request stream. - [ ] Add
ttlMsandcacheScopeto list results; maketools/listordering deterministic. - [ ] Adopt Tasks for anything that previously relied on stream resumability.
- [ ] Add idempotency keys to non-idempotent tools.
- [ ] Validate RFC 9207
iss; key stored credentials by issuer identifier. - [ ] Set
application_typeon any remaining DCR path; host a CIMD document. - [ ] Update error matching:
-32602for missing resources,-32020/-32021/-32022renumbering. - [ ] Remove affinity from the load balancer last, and verify under rolling deploy.
Frequently Asked Questions
Does the MCP 2026-07-28 spec break my existing server on day one?
No. Publishing the normative text was not a switch-off. Servers speaking 2025-11-25 continue to work, and clients that speak 2026-07-28 fall back to the initialize handshake when they reach an older server. Several SDKs serve both revisions from a single endpoint. The pressure comes from the deprecation clock — features marked Deprecated carry a minimum twelve-month window — not from a hard cutover date.
What replaces Mcp-Session-Id for servers that need state across calls?
Explicit, server-minted handles passed as ordinary tool arguments. A tool returns a handle, the model carries it, and subsequent calls include it as a parameter. The maintainers argue this is better than transport-hidden state precisely because the model can see the handle and reason about it. Your server still stores whatever it needs server-side; what changed is that the key travels in the request rather than in the connection.
Is server/discover mandatory?
For servers, yes — the specification says servers MUST implement it. For clients it is optional: a client MAY call it before any other request to select a protocol version up front, or skip it entirely and go straight to tools/call. On STDIO it doubles as a backward-compatibility probe, since there is no HTTP status code to signal an unsupported revision.
Why was Sampling deprecated, and what do I use instead?
Sampling required a server-initiated request over a held-open bidirectional stream, which is incompatible with a stateless request/response core. The spec’s suggested migration is to integrate directly with an LLM provider API. That shifts the API key, the token cost and the model-lifecycle risk onto the server operator. During the deprecation window, MRTR can carry a sampling request as one entry in an inputRequests map, which keeps the old pattern working on the new transport.
Do I have to migrate from DCR to CIMD immediately?
No. Dynamic Client Registration remains available for backwards compatibility with authorization servers that do not support Client ID Metadata Documents, and the spec’s priority order explicitly lists DCR as a fallback. What you should do now is set application_type correctly on your existing DCR path and key stored credentials by issuer identifier — both are required regardless of whether you adopt CIMD.
What happens if my stream drops mid-call now that resumability is gone?
The in-flight request is lost. The client MUST re-issue it as a new request with a new request ID; there is no Last-Event-ID recovery any more. For short calls this is acceptable. For anything long-running or non-idempotent, adopt the Tasks extension so the work has a pollable handle, and add idempotency keys so a retry cannot duplicate a side effect.
Further Reading
- Model Context Protocol architecture explained — the object model and transport primer this migration sits on top of.
- MCP server security architecture and threat model — the attack surface the new authorization hardening addresses.
- FastMCP versus the official MCP SDKs — choosing a framework, a decision worth keeping separate from the protocol upgrade.
- Multi-agent orchestration with MCP, A2A and LangGraph — where trace-context propagation across tool calls starts to matter.
- The 2026-07-28 specification announcement — the maintainers’ own summary, with ecosystem commentary.
- Full 2026-07-28 changelog — the normative list of every change against
2025-11-25.
By Riju — about
