MCP 2026-07-28 migration: a practical stateless-server checklist
The Model Context Protocol's 2026-07-28 revision changes how remote MCP servers handle connections. The initialize and notifications/initialized handshake is gone, the Mcp-Session-Id header has been removed, and each request now carries the protocol information it needs.
Applications may still keep state. The difference is that state can no longer hide inside a transport session. When one tool call depends on an earlier call, the server must expose that dependency through an explicit handle or use the new multi-round-trip request flow.
The checks below cover the parts of a production server that need attention: session state, SDK entry points, gateways, retries, authorization, and compatibility.
Find every session assumption
Before changing an SDK version, search the server for anything keyed by Mcp-Session-Id, a transport session ID, or connection lifetime. Look for cached credentials, selected workspaces, temporary files, in-progress approvals, and per-client tool lists.
Request-local data can be rebuilt for each call. Durable application state belongs in a database or another application store. Short-lived continuation state should travel through an explicit opaque handle.
The official changelog says list endpoints such as tools/list, resources/list, and prompts/list no longer vary per connection. A server that gives each session a different catalog must move that selection into authorization, request metadata, or a separate endpoint before adopting the new revision.
An unsigned blob is not a safe substitute for a session ID. A handle returned by a client is untrusted input. Bind it to the user, original operation, and an expiry, then protect its integrity. The TypeScript SDK migration guide provides a signed requestState codec for multi-round-trip flows and notes that its payload is signed, not encrypted.
Change the wire flow
A package upgrade does not necessarily activate the new revision. In TypeScript SDK v2, hand-constructed Client, Server, and McpServer instances keep speaking the older protocol unless the application opts into modern version negotiation or uses the new serving entry points.
For an HTTP server, the v2 migration path uses createMcpHandler:
import { createMcpHandler, McpServer } from "@modelcontextprotocol/server";
const handler = createMcpHandler(() => {
const server = new McpServer(
{ name: "search-server", version: "2.0.0" },
{ capabilities: { tools: {} } },
);
// Register tools, resources, and prompts here.
return server;
});
The handler creates a fresh server for each request. By default, it can serve both the 2026 revision and legacy stateless traffic. For stdio, use serveStdio(() => buildServer()) instead of directly connecting a hand-built server to StdioServerTransport.
Clients make a separate version choice. TypeScript SDK v2 supports automatic probing with versionNegotiation: { mode: "auto" }, strict pinning to 2026-07-28, or the legacy default. Automatic mode calls server/discover and can fall back to the older initialization handshake. Test each mode instead of relying on the SDK version shown by the package manager.
Adapt gateways and telemetry
Streamable HTTP requests for the new revision carry MCP-Protocol-Version, Mcp-Method, and, when applicable, Mcp-Name headers. A reverse proxy can identify a tools/call request and its tool name without parsing the JSON-RPC body.
Those headers are useful routing, rate-limit, and audit dimensions. The application must still authenticate the caller and validate the full request. Record the negotiated protocol revision beside latency, errors, and tool names so rollout failures can be separated into modern and legacy traffic.
List responses now support ttlMs and cacheScope. Clients can cache tool, prompt, and resource catalogs according to the server's hints. Deterministic ordering keeps equivalent lists stable and avoids needless changes in model input. Test invalidation when a catalog changes, and confirm that cached lists never cross an authorization boundary.
This fits an agent context-budget strategy: cache stable catalogs, retrieve changing evidence when needed, and delay large tool surfaces until they are relevant.
Replace server-initiated calls with MRTR
Older MCP flows could send elicitation/create, sampling/createMessage, or roots/list from the server while a stream stayed open. The 2026 revision replaces those server-initiated calls with Multi Round-Trip Requests (MRTR).
When a tool needs user input or another client-side action, it returns resultType: "input_required" with one or more inputRequests. The client fulfills them and retries the original tool call with inputResponses. Continuation data can travel as requestState.
This flow changes retry behavior. Give the operation an idempotency strategy, expire continuation state, and prevent a replay from performing the consequential step twice. An approval flow must not charge a card, create a project, or delete data because the client retried after losing a response. The least-privilege checklist for AI agent tools covers controls for those actions.
MCP Apps is a separate extension. If a tool needs a visual form or dashboard, the MCP Apps guide explains the sandboxed UI path. MRTR defines how a stateless tool call pauses and resumes. MCP Apps defines how a host renders an interactive interface.
Test authorization and deprecated paths
The release changes OAuth handling. Authorization servers should return the RFC 9207 iss parameter, and clients must validate it before redeeming an authorization code. Client credentials are bound to the issuer that created them. Dynamic Client Registration remains available for compatibility but is deprecated in favor of Client ID Metadata Documents.
Include desktop and CLI redirects, issuer mismatches, insufficient-scope recovery, credential storage, and logout in the migration tests. Run them against every authorization server you support.
Roots, Sampling, Logging, and the legacy HTTP+SSE transport are deprecated. The release policy provides at least twelve months between deprecation and removal, but new code should avoid these paths. Tasks now live in the io.modelcontextprotocol/tasks extension. Change notifications move to subscriptions/listen.
Roll out against a protocol matrix
Use a modern client, a legacy client, and a deliberately unsupported version. Cover HTTP and stdio if the product ships both. For every supported combination, verify discovery or initialization, tool listing, a normal tool call, cancellation, cached-list invalidation, authorization failures, an MRTR approval, and recovery after a server instance disappears.
Deploy modern support with protocol-version telemetry. Keep legacy fallback while active clients need it, and schedule a date to check that requirement again.
The acceptance criteria are observable. Any request can reach any healthy server instance. Continuation state is explicit, expiring, and integrity-protected. Retrying a consequential call does not repeat its side effect. Once those checks pass for both modern and fallback traffic, the server is ready for the new wire model.



