← All articles

MCP Stateless Protocol Migration Guide: What the 2026-07-28 Release Candidate Changes for Every AI Gateway

The MCP 2026-07-28 specification removes session IDs and the initialize handshake. This step-by-step migration guide covers before/after code diffs, load balancer changes, SDK upgrades, the explicit-handle pattern, and a rollback path for every gateway operator.

· TheRouter

MCP Stateless Protocol Migration Guide: What the 2026-07-28 Release Candidate Changes for Every AI Gateway

The MCP 2026-07-28 specification removes session IDs and the initialize handshake. If you run an MCP server, proxy, or gateway, every request is now self-contained and any server instance can handle it. This guide walks you through the migration step by step — before/after code diffs, infrastructure changes, SDK upgrades, and a rollback path — so you can ship confidently before the July 28 final spec date.

We wrote this guide because the stateless MCP change directly affects how gateways route AI traffic. TheRouter routes OpenAI-compatible requests through configured providers, and when gateway operators change how they handle MCP traffic, their routing and fallback policies need to follow. Source: MCP 2026-07-28 Release Candidate, retrieved 2026-07-20.

What changed: before vs. after

The core change in MCP 2026-07-28 is the removal of two protocol features that forced stateful deployments:

  1. The initialize/initialized handshake (removed by SEP-2575). Protocol version, client info, and capabilities now travel in _meta on every request. A new server/discover method replaces the capability exchange.
  2. The Mcp-Session-Id header and protocol-level sessions (removed by SEP-2567). No more session pinning, no more shared session stores.

Before (2025-11-25): A client must first POST an initialize request, receive a session ID, and include that Mcp-Session-Id header on every subsequent call:

// Step 1: Initialize handshake
POST /mcp
{"jsonrpc":"2.0","id":1,"method":"initialize",
 "params":{"protocolVersion":"2025-11-25","capabilities":{},
 "clientInfo":{"name":"my-app","version":"1.0"}}}

// Server responds with Mcp-Session-Id: 1868a90c-3a3f-4f5b

// Step 2: Every subsequent call carries the session header
POST /mcp
Mcp-Session-Id: 1868a90c-3a3f-4f5b
{"jsonrpc":"2.0","id":2,"method":"tools/call",
 "params":{"name":"search","arguments":{"q":"otters"}}}

After (2026-07-28): A single self-contained request that any instance can handle:

POST /mcp
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: search
Content-Type: application/json

{"jsonrpc":"2.0","id":1,"method":"tools/call",
 "params":{"name":"search","arguments":{"q":"otters"},
 "_meta":{"io.modelcontextprotocol/clientInfo":{"name":"my-app","version":"1.0"}}}}

No handshake, no session ID, nothing to pin. Source: MCP 2026-07-28 Release Candidate, retrieved 2026-07-20.

Migration checklist: infrastructure

  1. Swap three values, not three SDKs. Change api_key, base_url, and model in the existing OpenAI client. Keep your request/response code unchanged.
  2. Map model IDs explicitly. The target provider's model id is almost never identical to the OpenAI id. Keep a single dict of { openai_id: target_id } outside business logic.
  3. Verify streaming format. SSE chunks must follow the OpenAI data: {...} + data: [DONE] contract. Test one streaming call before moving production traffic.
  4. Check rate-limit headers. Some providers omit x-ratelimit-* headers. Add a wrapper that defaults safely when headers are absent.
  5. Keep a rollback path. Ship the swap behind a feature flag, run both endpoints in shadow for 24 hours, then cut over.

Step 1: Remove sticky sessions and session affinity

If your load balancer (NGINX, HAProxy, AWS ALB, Azure App Service ARR) uses Mcp-Session-Id-based sticky routing or cookie affinity for MCP endpoints, disable it. With the protocol now stateless, any server instance can serve any request.

NGINX before:

upstream mcp_backend {
    ip_hash;  # or sticky cookie
    server mcp1:8080;
    server mcp2:8080;
}

NGINX after:

upstream mcp_backend {
    # Round-robin, no sticky sessions needed
    server mcp1:8080;
    server mcp2:8080;
}

Azure App Service: Set clientAffinityEnabled: false in your ARM template or App Service configuration. Source: Microsoft Tech Community — MCP Stateless on App Service, retrieved 2026-07-20.

Step 2: Route on Mcp-Method headers

The Streamable HTTP transport now requires an Mcp-Method header on every request, and Mcp-Name on tool calls, resource reads, and prompt gets (SEP-2243). This means your gateway can route, rate-limit, or log MCP traffic without parsing the JSON-RPC body.

NGINX example — route by Mcp-Method:

location /mcp {
    # Rate-limit tools/call separately from tools/list
    if ($http_mcp_method = "tools/call") {
        set $rate_zone tool_calls;
    }
    if ($http_mcp_method = "tools/list") {
        set $rate_zone tool_lists;
    }
    proxy_pass http://mcp_backend;
}

Servers must reject requests where the headers and body disagree. Make sure your reverse proxy preserves the Mcp-Method and Mcp-Name headers when forwarding.

Step 3: Decommission shared session stores

If you were running Redis, Memcached, or a database to share Mcp-Session-Id → state mappings across MCP server instances, that infrastructure is no longer needed for protocol state. You can decommission it or repurpose it for application-level state (see the explicit-handle pattern below). Source: 4sysops — MCP 2026-07-28 Overview, retrieved 2026-07-20.

Step 4: Enable response caching

List and resource-read results now carry ttlMs and cacheScope fields (SEP-2549), modeled on HTTP Cache-Control. Your gateway can cache tools/list responses for as long as ttlMs permits, and cacheScope tells you whether the response is safe to share across users ("global") or per-user only.

This removes the need to keep an SSE stream open just to learn that a tool list changed.

Migration checklist: SDK and server code

Step 5: Update your MCP SDK

Beta releases of all four Tier 1 SDKs are available now. Source: Beta SDKs for the 2026-07-28 MCP Spec Release Candidate, retrieved 2026-07-20.

SDKBeta versionInstall command
Pythonv2.0.0b1uv add "mcp[cli]==2.0.0b1" or pip install "mcp[cli]==2.0.0b1"
TypeScriptv2 betanpm install @modelcontextprotocol/server@beta
Gov1.7.0-pre.1Use pre-release tag on existing module
C#2.0.0-preview.1Install ModelContextProtocol preview package

Important for library authors: If you publish a library that depends on the Python mcp package, add an upper bound now (e.g. mcp>=1.27,<2) so the v2 release does not surprise your users.

Step 6: Remove initialize handshake code

If your server implements a custom initialize handler, you can remove it. In the new model:

  • Client info comes from _meta.io.modelcontextprotocol/clientInfo on each request
  • Protocol version comes from the MCP-Protocol-Version HTTP header
  • Server capabilities are served via the new server/discover method instead of the initialize response

Python v1 (before):

@app.handle("initialize")
async def handle_initialize(params):
    return {"protocolVersion": "2025-11-25",
            "capabilities": {"tools": {}},
            "serverInfo": {"name": "my-server", "version": "1.0"}}

Python v2 (after):

from mcp.server import MCPServer

mcp = MCPServer("my-server")

@mcp.tool()
def search(q: str) -> str:
    """Search for something."""
    return f"Results for {q}"
# server/discover is handled automatically by the SDK

Step 7: Adopt the explicit-handle pattern for stateful tools

Removing the protocol session does not mean your application cannot have state. If your tools need to carry state across calls, mint an explicit handle and let the model pass it back as an argument:

@mcp.tool()
def create_session(config: dict) -> dict:
    """Create a work session and return its handle."""
    session_id = store.create(config)  # persist in Redis/DB
    return {"session_id": session_id}

@mcp.tool()
def continue_work(session_id: str, action: str) -> dict:
    """Continue work in an existing session."""
    state = store.get(session_id)  # retrieve from shared store
    result = process(state, action)
    store.update(session_id, state)
    return result

This pattern is more powerful than hidden session state: the model can see the handle, compose it across tools, reason about it, and hand it off between steps. Source: MCP 2026-07-28 Release Candidate — Stateless Protocol, Stateful Applications, retrieved 2026-07-20.

Step 8: Update error code handling

The error code for a missing resource changes from the MCP-custom -32002 to the JSON-RPC standard -32602 (Invalid Params) per SEP-2164. Update any client code that matches on the literal -32002 value.

- if error.code == -32002:
-     handle_resource_not_found()
+ if error.code == -32602:
+     handle_resource_not_found()

Step 9: Handle deprecated features

Three features are deprecated in this release (SEP-2577):

FeatureReplacementUrgency
RootsTool parameters, resource URIs, or server configurationLow — works for 12+ months
SamplingDirect integration with LLM provider APIsLow — works for 12+ months
Loggingstderr for stdio transports; OpenTelemetry for structured observabilityLow — works for 12+ months

These are annotation-only deprecations. The methods, types, and capability flags continue to work in this release and in every specification version published within a year. But plan your migration now.

Server-to-client requests: Multi Round-Trip Requests (MRTR)

If your server sends prompts to the client mid-tool-call (e.g., confirmation dialogs via elicitation), the mechanism has changed. Instead of holding an SSE stream open, the server now returns an InputRequiredResult:

{
  "resultType": "inputRequired",
  "inputRequests": {
    "confirm": {
      "type": "elicitation",
      "message": "Delete 3 files?",
      "schema": {"type": "boolean"}
    }
  },
  "requestState": "eyJzdGVwIjox..."
}

The client gathers answers and re-issues the original call with inputResponses and the echoed requestState. Any server instance can pick up that retry because everything it needs is in the payload. Source: MCP 2026-07-28 Release Candidate — Multi Round-Trip Requests, retrieved 2026-07-20.

Rollback path

Nothing breaks on July 28. The new SDKs support backward compatibility:

  • New clients connecting to old servers: Clients that speak 2026-07-28 automatically fall back to the initialize handshake when they reach a server on 2025-11-25 or earlier.
  • New servers accepting old clients: Python v2 servers answer both the legacy initialize handshake and server/discover, so existing clients keep connecting.
  • Mixed deployments: You can run 2025-11-25 and 2026-07-28 server instances side by side during the transition. Old instances keep session affinity; new instances are stateless.

If your migration encounters issues, revert the SDK pin to v1 and re-enable sticky sessions. The old protocol continues to work.

Cost delta

The stateless protocol reduces infrastructure costs in three ways:

  1. No shared session store. You no longer need Redis or Memcached for protocol-level session state across instances.
  2. Simpler load balancing. Round-robin replaces sticky sessions, removing the need for affinity-aware load balancers or advanced health checks.
  3. Response caching. ttlMs and cacheScope on list results let gateways cache tools/list responses, reducing redundant calls.

The trade-off: if your tools have complex stateful workflows, you now manage that state explicitly (Redis, database, etc.) rather than relying on the protocol. But this state was always better externalized — the protocol session was a convenience, not a solution.

TheRouter integration

TheRouter routes OpenAI-compatible requests through configured providers and supports provider/model routing and fallback when live product paths support it. The stateless MCP change affects how gateway operators proxy MCP traffic alongside standard LLM API calls:

  • Header-based routing. The Mcp-Method and Mcp-Name headers let you route MCP and LLM API traffic through the same gateway with different rate-limit and routing rules.
  • Unified fallback. If an MCP server is down, the same fallback routing patterns used for LLM API providers apply. See our LLM API Fallback Routing Guide for the engineering pattern.
  • Migration alignment. If you are migrating from OpenAI to TheRouter, the MCP stateless change simplifies the gateway layer. See our OpenAI to TheRouter Migration Guide for the full walkthrough.

Timeline and next steps

DateEvent
May 21, 2026Release candidate locked
Now (July 2026)Beta SDKs available for all four Tier 1 SDKs
July 28, 2026Final specification published
~Jan 2027Deprecated features (Roots, Sampling, Logging) earliest possible removal

Action items:

  1. Test the beta SDKs against your workloads now.
  2. Audit your load balancer and gateway config for session affinity.
  3. Inventory any code that depends on Mcp-Session-Id, initialize, or error code -32002.
  4. Plan your explicit-handle migration for stateful tools.
  5. Update monitoring to track the new Mcp-Method header for observability.

Sources

Help & contact