
100% private · no tracking · works offline100% client-side/no data leaves your browser/no accounts/works offline
A practical migration plan for the stateless MCP release candidate, covering request metadata, explicit state handles, caching, retries, deprecated features, and staged rollout.

Free toolkit
85+ private dev tools
Everything runs in your browser. Zero tracking, no sign-up.
Browse toolsMCP 2026-07-28 migration is not a routine SDK upgrade. The release candidate removes the initialization handshake and protocol sessions, moves negotiation data into every request, and changes how clients handle streaming, caching, and server-initiated interaction. The final specification is scheduled for July 28, 2026; as of July 20, teams should validate against the locked candidate without pretending it is already final.
In this guide, you'll learn:
The short answer: MCP becomes stateless at the protocol layer. A client no longer establishes a session with initialize, retains an Mcp-Session-Id, and assumes later requests reach the same logical connection. Each request must carry enough version and capability information for any compatible server instance to process it independently.
The official MCP 2026-07-28 release-candidate announcement describes this as the protocol's largest revision since launch. The authoritative draft changelog lists the changes relative to 2025-11-25.
| Area | 2025-11-25 behavior | 2026-07-28 candidate | Migration impact |
|---|---|---|---|
| Startup | initialize and initialized handshake |
No mandatory handshake | Send negotiation metadata per request |
| Sessions | Mcp-Session-Id may bind requests |
No protocol session | Remove affinity and externalize real app state |
| Discovery | Capabilities exchanged at startup | server/discover available |
Probe support before choosing a version |
| Results | Ordinary result shape | Required resultType |
Accept legacy omission as complete |
| Streaming recovery | SSE event IDs and Last-Event-ID |
No stream resumability | Retry as a new request with a new JSON-RPC ID |
| List caching | Often connection-scoped | ttlMs and cacheScope |
Cache by server, identity, and authorization context |
| Server-to-client flow | Server-initiated requests | Multi Round-Trip Requests | Return input_required, then retry with responses |
| Long-running work | Experimental core tasks | Tasks extension | Negotiate the extension explicitly |
This is a protocol boundary change, not an instruction to make every application stateless. Shopping baskets, browser sessions, repository worktrees, and long-running jobs still need state. The difference is that state becomes an explicit application concern instead of being hidden inside the transport connection.
If your server is remote and authenticated, pair this migration with the site's MCP authentication guide. Removing a session identifier does not remove OAuth resource binding, token validation, consent, or per-tool authorization.
Under the candidate specification, the client sends the protocol version in the MCP-Protocol-Version HTTP header and repeats protocol version, client capabilities, and preferably client identity in request _meta. Streamable HTTP requests also carry routing headers such as Mcp-Method; tool calls add Mcp-Name.
The following command is a complete request template. Set MCP_URL and MCP_TOKEN for your own candidate-compatible test server before running it.
export MCP_URL="https://mcp.example.com/mcp"
export MCP_TOKEN="replace-with-a-test-token"
curl --fail-with-body --silent --show-error \
--request POST "$MCP_URL" \
--header "Authorization: Bearer $MCP_TOKEN" \
--header "Content-Type: application/json" \
--header "Accept: application/json, text/event-stream" \
--header "MCP-Protocol-Version: 2026-07-28" \
--header "Mcp-Method: tools/call" \
--header "Mcp-Name: search" \
--data '{
"jsonrpc": "2.0",
"id": "req-001",
"method": "tools/call",
"params": {
"name": "search",
"arguments": { "query": "stateless MCP" },
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {},
"io.modelcontextprotocol/clientInfo": {
"name": "migration-probe",
"version": "1.0.0"
}
}
}
}'
Do not simply delete initialize and leave everything else untouched. Inventory every value your implementation used to remember after initialization: negotiated features, client identity, log level, subscriptions, authorization context, and server affinity. Decide whether each value now belongs in request metadata, a signed token, an explicit tool argument, or server-side application storage.
Treat server/discover as a compatibility probe, not as a new mandatory session opener. The draft says servers must implement it, while clients may call it for up-front version selection. A client should cache the discovery result conservatively and recover from an UnsupportedProtocolVersionError rather than assuming every endpoint upgrades simultaneously.

When a workflow genuinely spans calls, let a tool create an opaque handle and require later tools to receive it as an ordinary argument. A basket_id, workspace_id, or job_id makes ownership visible in schemas, logs, authorization checks, and model context.
Use these rules for handles:
This dependency can be modeled without an MCP-specific SDK. The following Node.js module creates, owns, and resolves expiring state handles; the same functions can sit behind your tool handlers.
import { randomUUID } from "node:crypto";
const states = new Map();
export function createState(ownerId, value, ttlMs = 15 * 60 * 1000) {
const handle = randomUUID();
states.set(handle, {
ownerId,
value,
expiresAt: Date.now() + ttlMs,
});
return handle;
}
export function readState(ownerId, handle) {
const record = states.get(handle);
if (!record || record.expiresAt <= Date.now()) {
states.delete(handle);
throw new Error("State handle is missing or expired");
}
if (record.ownerId !== ownerId) {
throw new Error("State handle belongs to another principal");
}
return record.value;
}
The in-memory Map is suitable only for a single-process example. Production instances need a shared durable store when later calls may land on different servers. That is still simpler than treating every protocol connection as an invisible state container: the state has a defined owner, lifetime, schema, and cleanup path.
Explicit state also improves context design. Instead of repeatedly injecting a whole hidden session into the model, tools expose only the handle and the result needed for the next decision. The site's context engineering guide covers the broader discipline of controlling what enters an agent's working context.

The candidate adds infrastructure-friendly routing and caching signals. Mcp-Method lets a gateway route without parsing the JSON body; Mcp-Name adds the tool, prompt, or resource name where applicable. List and read results include ttlMs plus cacheScope, and tool ordering should be deterministic to improve downstream prompt-cache reuse.
Do not interpret ttlMs as permission to share private tool catalogs between users. A safe cache key includes at least the server origin, protocol version, operation, authenticated principal or authorization partition, and relevant arguments. cacheScope: "private" must not enter a shared intermediary cache.
export function cacheKey({
serverOrigin,
protocolVersion,
method,
principalId,
argumentsHash,
}) {
return JSON.stringify([
serverOrigin,
protocolVersion,
method,
principalId,
argumentsHash,
]);
}
export function expiresAt(result, receivedAt = Date.now()) {
if (!Number.isFinite(result.ttlMs) || result.ttlMs < 0) {
throw new Error("Invalid ttlMs");
}
return receivedAt + result.ttlMs;
}
At the load balancer, strip any client-supplied internal routing headers you do not intend to trust, then reconstruct or validate the standard MCP headers against the JSON-RPC body. The draft defines a header mismatch error because header-only routing creates a new failure mode: the gateway and application must agree about the method and name.
Trace propagation belongs in the same boundary. The changelog documents W3C traceparent, tracestate, and baggage keys in _meta. Propagate identifiers, but do not put prompts, tokens, tool outputs, or credentials into baggage; it travels widely and is commonly recorded by infrastructure.
The candidate removes SSE event IDs, Last-Event-ID, and in-flight stream resumption. If a response stream breaks, the client issues a new JSON-RPC request with a new request ID. That is straightforward for reads and dangerous for non-idempotent writes.
A JSON-RPC ID correlates messages; it is not a business idempotency key. For a payment, email, deployment, or database mutation, accept a separate operation key inside the tool arguments and persist its outcome atomically. A retry with the same operation key should return the stored result rather than repeat the side effect.
const completedOperations = new Map();
export async function runOnce(operationKey, action) {
if (!operationKey) throw new Error("operationKey is required");
const previous = completedOperations.get(operationKey);
if (previous) return previous;
const result = await action();
completedOperations.set(operationKey, result);
return result;
}
Again, replace the Map with transactional durable storage in production. A real implementation must prevent two concurrent requests from both executing before either writes the result. Database uniqueness on (tenant_id, operation_key) plus a transactional state machine is a common boundary.
High-impact tools should also remain approval-gated. The AI coding-agent permission contract explains why prompts are not an authorization mechanism; enforce permissions in the executor that owns the side effect.
Deprecated does not mean removed on July 28. The new lifecycle policy provides a minimum twelve-month deprecation window, but new implementations should avoid taking dependencies on features already scheduled for removal.
| Deprecated feature | Keep working now? | Recommended direction |
|---|---|---|
| Roots | Yes, during deprecation | Pass directories/files through tool arguments, resource URIs, or server config |
| Sampling | Yes, during deprecation | Integrate with the model provider directly |
| Logging | Yes, during deprecation | Use stderr for stdio or OpenTelemetry |
| HTTP+SSE transport | Already deprecated | Move to Streamable HTTP |
| Dynamic Client Registration | Backward-compatible | Prefer Client ID Metadata Documents |
Tasks are not deprecated; they move out of the core protocol into the io.modelcontextprotocol/tasks extension. MCP Apps are also an optional extension. Do not enable either merely because the server advertises it. Negotiate explicit support, isolate untrusted UI content, and apply the same user-consent boundary you use for ordinary tools.
Multi Round-Trip Requests replace direct server-initiated requests. A server returns an input_required result containing its input requests, and the client retries the original operation with corresponding input responses. This changes control flow substantially: cap round trips, validate every returned request, preserve user approval, and prevent a server from turning elicitation into an open-ended loop.
Do not flip a shared production endpoint from 2025-11-25 to 2026-07-28 in one deployment. Client and server support will arrive at different times, especially when community SDKs sit between your code and the wire format.
A safer rollout has five stages:
Version negotiation must fail closed. Do not silently parse a candidate request with legacy defaults or silently downgrade an authenticated client after a mismatch. A clear unsupported-version response is easier to debug and safer than partial interoperability.
Keep the final-date distinction visible in release notes. On July 20, 2026-07-28 is a locked release candidate, not the current finalized specification. Recheck the official changelog on July 28 before removing the feature flag or calling the migration complete.
Unit tests for tool business logic are not enough. The breakage lives in envelopes, headers, identity propagation, caches, streams, and multi-request behavior.
Your migration suite should cover:
Mcp-Method and Mcp-Name match the JSON-RPC body;resultType are treated as complete;resultType values;Add a canary server behind the same gateway, authentication middleware, cache, and telemetry exporters as production. A local happy-path exchange will not expose header normalization, proxy buffering, cache partitioning, or identity loss. The goal is not merely schema validity—it is preserving authorization and side-effect semantics when the connection no longer carries hidden context.
No. As of July 20, 2026, it is a locked release candidate; the maintainers state that the final specification is scheduled for July 28. Teams can test and prepare against the candidate now, then verify the final changelog before completing rollout.
No. The protocol no longer owns a hidden session, but applications can keep state behind explicit server-minted handles passed as tool arguments. Bind each handle to an authenticated owner, expire it, and store it where any eligible server instance can resolve it.
No. They are deprecated and remain functional during the formal deprecation window, which is at least twelve months. New implementations should avoid adding dependencies and existing implementations should plan the documented replacements.
Not in the 2026-07-28 candidate. SSE resumability and message redelivery are removed; the client creates a new request with a new JSON-RPC ID. Mutating tools therefore need a separate idempotency mechanism to prevent duplicate side effects.
No. Servers must implement server/discover, but clients may call it for up-front version and capability selection. A client can send a self-contained request directly and handle an unsupported-version error, depending on its compatibility strategy.
The safest MCP 2026-07-28 migration starts by making hidden dependencies visible: request metadata, application state, cache ownership, retry identity, and authorization. Build dual-version handling, test through real infrastructure, and keep the candidate behind a rollout flag until the final specification is published and checked on July 28.
Comments
Sign in to join the discussion.
No comments yet. Be the first to share your thoughts.