1078 words
5 minutes

MCP Stateless HTTP: Migrate Away from Protocol Sessions Safely

2026-08-10
2026-08-11
AI
MCP
/
AI
/
DevOps
/
Migration
/
Troubleshooting

MCP stateless HTTP does not mean your application must forget everything between requests. The final 2026-07-28 Streamable HTTP specification removes protocol-level sessions from the modern transport path, but your application can still persist jobs, permissions, carts, or conversations behind explicit identifiers. Carry that state deliberately, send the required request metadata, and keep a separate compatibility path for older clients.

Separate protocol state from application state#

The migration becomes easier when the two kinds of state are named separately:

StateOlder design assumptionStateless HTTP design
Protocol capability negotiationinitialize creates a sessionEach request declares MCP-Protocol-Version and mirrors it in _meta
Transport correlationMcp-Session-Id identifies the conversationEach POST can stand on its own; request-scoped SSE is still possible
Request identityMethod and tool name are inferred from the parsed bodyMcp-Method and, where required, Mcp-Name mirror the JSON-RPC request
Product stateHidden in a server-side sessionStored behind an explicit handle such as cart_id, job_id, or tenant_id
Request metadataInferred from connection stateSent with the request and its reserved _meta values

If a tool call needs a cart, job, or tenant, make that value part of the tool input or an authenticated request context. Do not reuse a transport session ID as a business record key; the two lifecycles have different ownership and retention rules.

Send a complete Streamable HTTP request#

The modern transport uses one MCP endpoint. Clients send JSON-RPC messages with HTTP POST; the server can answer with JSON or a request-scoped SSE stream. Every POST must include Accept: application/json, text/event-stream, MCP-Protocol-Version, and Mcp-Method. Mcp-Name is also required for methods such as tools/call, resources/read, and prompts/get.

Use the same protocol version in the header and the reserved body metadata. This is the smallest useful shape for a tool call:

POST /mcp HTTP/1.1
Accept: application/json, text/event-stream
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": {}
}
}
}

Treat the example as a protocol boundary, not a complete authentication design. The exact JSON-RPC method, tool arguments, authorization, and response handling still belong to the server contract. The important change is that the request carries what the server needs instead of relying on a previously negotiated transport session.

Handle discovery and application state explicitly#

Use the 2026-07-28 discovery mechanisms when a client needs to learn what a server supports. Discovery is a capability decision; it is not a replacement for authorization or durable product state.

The specification reserves _meta values for request-level metadata and mirrors the protocol version at the HTTP boundary. Keep metadata small, non-secret, and scoped to the request. Authentication credentials, tenant authorization, and durable application state should continue to use their intended security and storage mechanisms.

If a tool needs continuity, pass an application-owned job_id, cart_id, or similar handle in its arguments or an authenticated request context. Give that handle its own authorization, expiry, and idempotency rules. Do not reuse a transport session ID as a business record key.

Treat a 400 response as a protocol signal#

The final transport spec requires the server to reject missing or mismatched required headers with HTTP 400. A JSON-RPC HeaderMismatch error uses code -32020. This makes an invalid modern request distinguishable from an older server that does not understand the new boundary.

For a client that must support both generations, use this decision tree:

  1. Send the modern POST with the required headers and body metadata.
  2. If the response is HTTP 400 with a recognized modern JSON-RPC error, fix the headers, version negotiation, or body rather than falling back.
  3. If the 400 body is empty or unrecognized, treat the endpoint as a possible legacy server and try the older initialize plus HTTP+SSE flow.
  4. Return a useful upgrade error when neither lane can provide the requested capability.
  5. Measure legacy traffic before removing the fallback.

Do not use every 400 response as permission to fall back. That can hide a malformed modern request and turn a client bug into a misleading compatibility path. A modern server may also return 405 for old GET or DELETE session operations; Mcp-Session-Id is not a substitute for the modern request headers.

Cloudflare’s Agents SDK v0.20.0 changelog describes a factory-based createMcpHandler path that can support stateless and legacy ordinary tools on the same route, while marking McpAgent as deprecated and feature-frozen. If your server relies on sessions, pushed requests, or stream replay, use a dual-route migration until those behaviors have a tested replacement.

Test state boundaries instead of only tool calls#

A migration test should prove more than “the tool returned 200.” Exercise these cases:

  • a fresh stateless request with no protocol session and all required headers;
  • a request whose MCP-Protocol-Version disagrees with _meta.io.modelcontextprotocol/protocolVersion;
  • a request missing Mcp-Method or a required Mcp-Name and the expected HTTP 400 response;
  • discovery followed by a tool call with explicit application state;
  • two requests from different connections using the same authorized job_id;
  • an old client that still sends initialize and a session header;
  • an empty or unrecognized 400 body that triggers the legacy fallback;
  • an expired or unauthorized application handle;
  • a retried request that must not create duplicate side effects.

If you are deploying the handler on Workers, a Cloudflare Workers test harness can help you exercise request and failure paths before switching production traffic.

A safe migration sequence#

  1. Inventory hidden session state. List every tool or resource that reads server memory keyed only by a protocol session.
  2. Define explicit state handles. Choose the durable store, authorization rule, expiry, and idempotency behavior for each handle.
  3. Add discovery and stateless request handling. Make the new route observable without deleting the legacy route.
  4. Run both compatibility paths. Test current and older clients against the same business invariants.
  5. Move traffic gradually. Track legacy requests, errors, reconnects, and duplicate side effects.
  6. Remove only unused protocol state. Keep application state and audit records according to their own retention policy.

FAQ#

Q: Is MCP stateless HTTP the same as an application with no state?#

A: No. It removes a transport-level session requirement from the newer protocol direction. Your application can still persist jobs, carts, permissions, or conversation records behind explicit identifiers.

Q: Do all MCP clients stop sending initialize immediately?#

A: No. Older protocol revisions and clients still use the handshake and session model. Try the modern request first when supported, classify 400 responses correctly, retain a tested compatibility path, and remove it only after observing migration data.

Q: What should replace Mcp-Session-Id for a long-running job?#

A: Create an application-owned job identifier with explicit authorization, expiry, and idempotency rules. Pass that identifier as part of the request or tool input instead of treating a transport session ID as the job record.

References:

MCP specification: Streamable HTTP transport

MCP 2026-07-28 release candidate context

Cloudflare Agents SDK v0.20.0 and MCP SDK v2

Cloudflare MCP servers support the 2026-07-28 protocol

MCP Stateless HTTP: Migrate Away from Protocol Sessions Safely
https://laplusda.com/en/posts/mcp-stateless-http-session-migration/
Author
Zero
Published at
2026-08-10
License
CC BY-NC-SA 4.0
Was this article useful?

Report a typo or broken link, or suggest a related topic.