Creuto is now an OpenAI Select Partner Read More

Software Architecture & Technical

Stateless MCP: your server no longer needs sticky sessions

Stateless MCP landed in the 2026-07-28 spec: no initialize handshake, no Mcp-Session-Id. What to delete, what older clients still need, and where state goes.

Stateless MCP: your server no longer needs sticky sessions

Stateless MCP is now what the specification says, not a deployment style you opt into. The 2026-07-28 revision removed the initialize and notifications/initialized handshake and deleted the Mcp-Session-Id header from the Streamable HTTP transport, so any instance behind a plain round-robin load balancer can answer any request. The session store in front of your MCP server is now dead weight.

AWS put a figure on the smallest part of that saving: a two-node ElastiCache cache.t4g.micro session store costs about $23 a month. The figure is not the point. The class of infrastructure it belongs to is — stickiness rules, session replication, drain-on-scale-in behaviour, and the observability plumbing someone wrote to follow a session across instances.

What the 2026-07-28 revision actually deleted

MCP calls this its largest revision since launch, and four removals decide how you deploy. Every one of them is in the published changelog, not in commentary about it.

  • The initialize and notifications/initialized handshake is gone. Every request carries its own protocol version and client capabilities in _meta, under the keys io.modelcontextprotocol/protocolVersion and io.modelcontextprotocol/clientCapabilities. A client's first message can be the tool call itself.
  • Protocol-level sessions and the Mcp-Session-Id header are gone. List endpoints such as tools/list no longer vary per connection.
  • The standalone HTTP GET stream is gone, along with resources/subscribe. Change notifications now arrive on the response stream of a single subscriptions/listen request that the client opts into.
  • SSE resumability is gone. There is no Last-Event-ID and no message redelivery: a broken stream loses the in-flight request, and the client must re-issue it with a new request ID.

Three additions replace the session as the thing your infrastructure keys on. server/discover is an RPC every server must implement, returning supported protocol versions, capabilities and identity in one response; calling it is optional for the client. Mcp-Method and Mcp-Name are now required headers on Streamable HTTP POSTs, which is what lets a gateway route and throttle without parsing a JSON-RPC body. And MCP-Protocol-Version must match the version inside the body — a mismatch is a 400 with JSON-RPC error -32020, HeaderMismatch.

Deployment concernSession-based MCP2026-07-28
Load balancingALB stickiness per sessionPlain round-robin
Shared stateDynamoDB or ElastiCache session storeNone required
Gateway routingParse the request body to read the methodRoute on Mcp-Method and Mcp-Name
Broken responseResume with Last-Event-IDRe-issue the call; make tools idempotent

Stateless MCP removes the reason for session affinity, not the reason for care

The architectural consequence is narrow and large at once: any instance can serve any request, so retries need no affinity and scale-in never drains sessions. AWS's own reading is that the old design fought horizontal scaling, because a session lived on whichever instance issued it and running more than one instance meant either pinning clients or externalising session state. Both were correct for that protocol. Neither is required now.

The care that remains is idempotency. Removing stream resumability moves recovery from the transport to your tool code: if a stream breaks mid-call, the client re-issues the request, and a tool that charges a card or creates a record twice is now your bug rather than the transport's. That is the same discipline good API development and integrations work already demands, and it is the single item on the migration list most likely to be skipped.

Can you run an MCP server on Lambda now?

Yes, for the request-response path, and AWS says so plainly: Lambda is a natural fit because the protocol is now request in, response out. The mechanism that makes it true is Multi Round-Trip Requests. A server that needs user input no longer pushes a request down a held-open stream; it returns an input_required result carrying inputRequests and an opaque requestState token, and the client re-sends the original call with inputResponses and the echoed token. Any instance can pick that up, because the token carries the context.

The honest limit is subscriptions/listen. A client that wants tool-list or resource-change notifications opens a long-lived POST whose response stream stays open, and the spec encourages periodic SSE keep-alive comments on it. That is a connection-duration workload, not a request-response one. If your clients use it, that path belongs on Fargate or another container behind an ALB doing ordinary round-robin, even when the rest of your serverless architecture handles tool calls on Lambda. Function timeouts are a real constraint here, and Lambda's 90-minute timeout comes with conditions worth reading before you assume a long stream fits.

What your gateway can do now that it could not before

Three of the smaller changes land on the edge rather than in the server, and together they are the reason the migration is worth doing even on a single instance.

Routing moves out of the body. Because Mcp-Method and Mcp-Name are required on every POST, a load balancer rule, a WAF rule or a per-tool rate limit can read the operation without deserialising JSON. The specification is strict about the trade: a server that processes the body must reject any request whose headers disagree with it, precisely so a load balancer routing on the header and a server executing on the body cannot be made to disagree. Servers must decode the Base64 sentinel form of Mcp-Name before comparing.

Caching becomes declared rather than guessed. Results from tools/list, prompts/list, resources/list, resources/read and resources/templates/list now carry ttlMs and cacheScope, where cacheScope is public or private and decides whether a shared intermediary may hold the response at all. Tool lists should also come back in deterministic order, which is what makes a client-side or prompt cache hit. If you serve more than one tenant, private is the default you want, and the mistake to avoid is marking a tenant-scoped list public because it looks identical in your own test account.

Tracing stops being bespoke. The revision documents OpenTelemetry trace context propagation through _meta keys — traceparent, tracestate and baggage — and deprecates MCP's own logging feature in favour of stderr or OpenTelemetry. For teams already running an OTel collector as part of their DevOps and cloud engineering setup, the protocol change is mostly a deletion: the custom correlation ID you threaded through the session goes away.

How to support older MCP clients without keeping sticky sessions

This is where summaries of the change get loose, and it is worth being precise. AWS's guidance says the 2026-07-28 spec "includes a backward-compatible lane that preserves session semantics for older clients" and tells you to keep ALB stickiness and the session store until pre-2026-07-28 traffic reaches zero. The operational advice is right. The description is not quite what the specification says.

Read the Streamable HTTP transport and the compatibility route is version negotiation, not a session lane inside the new revision. A server that implements only 2026-07-28 must answer HTTP GET or DELETE on the MCP endpoint with 405 Method Not Allowed, must ignore an Mcp-Session-Id header without minting or echoing one, and must ignore Last-Event-ID. Serving older clients means implementing the earlier revision's behaviour as well, alongside the fallback probe: a dual-era client tries a modern request first, and on 400 inspects the body before falling back, because modern servers also return 400 for unsupported-version and header-validation errors.

In practice that argues for splitting the eras rather than blending them. Keep the legacy listener on its own target group with stickiness and its session store, run the 2026-07-28 endpoint stateless behind round-robin, log the protocol version on every request at the gateway, and set a sunset date you actually tell client teams. MCP's new feature lifecycle policy sets a minimum twelve-month deprecation window for features it retires, which is a reasonable anchor for your own legacy window — not a promise that anyone else's client will move inside it.

Where application state belongs once the session is gone

The line to keep is AWS's: stateless describes the protocol, not your application. Stateful use cases still work, and the specification says how — servers that need cross-call state mint explicit handles and pass them as ordinary tool arguments. The state stays in your datastore. The model carries the key.

That has a security consequence teams miss. The identifier now sits in the model's context instead of a header, so it is visible to the model and to anything that can influence it. Servers must enforce ownership on every call rather than treating possession of an identifier as proof of entitlement, and requestState tokens are untrusted input whose integrity you protect with HMAC or AEAD, rejecting anything that fails verification. In the systems we build, that check belongs in the tool handler, not the gateway, because the gateway does not know which tenant owns row 41,902.

When this is the wrong migration to run now

Three cases. If you run behind a managed MCP host, protocol management and backward compatibility are handled for you and the deployment work is not yours to do. If your server is local and speaks stdio, none of the load-balancing argument applies. And if you run a single instance with no affinity problem, the change buys you tidiness rather than capacity — worth doing, not worth a sprint.

One more caution on timing. The same revision deprecates Roots, Sampling and Logging, moves tasks into an extension, and deprecates Dynamic Client Registration in favour of Client ID Metadata Documents. Those are deprecations with a window, not removals. Ripping them out in the same change as the transport work turns a mechanical migration into a rewrite, and the transport work is the part that pays for itself immediately.

The first thing to do is not a code change. Turn on protocol-version logging at your gateway for a fortnight and look at the split. If pre-2026-07-28 traffic is already near zero, deleting the stickiness rule and the session store is an afternoon. If it is not, the number you get back is the one that sets your sunset date — and the argument you need for the client teams who have to move.

Frequently asked questions

MCP no longer has protocol-level sessions. The 2026-07-28 revision removed the initialize handshake and the Mcp-Session-Id header, and every request now carries its protocol version and client capabilities in _meta. A server that needs cross-call state mints its own identifier and passes it as an ordinary tool argument instead.

Yes. AWS describes Lambda as a natural fit for an MCP server under the 2026-07-28 specification, because the protocol is now request in, response out. The exception is the subscriptions/listen stream, which stays open for change notifications and suits a container such as Fargate better.

Implement the earlier revision alongside the new one rather than expecting the new revision to carry sessions. Keep the legacy endpoint on its own target group with stickiness and its session store, run the 2026-07-28 endpoint behind round-robin, log protocol version per request, and set a sunset date.

The Mcp-Method and Mcp-Name headers are now required on Streamable HTTP POST requests, so a gateway can route and throttle without parsing the JSON-RPC body. MCP-Protocol-Version must match the version in the body, and a mismatch returns HTTP 400 with JSON-RPC error code -32020, HeaderMismatch.

A broken response stream loses the in-flight request, and the client must re-issue it as a new request with a new ID. Recovery moves from the transport to your tool code, so any tool with side effects needs to be idempotent before you migrate a stateless MCP server to production.

Written by

Akash Mohapatra

Akash Mohapatra

Co Founder & Director

26 Sep 2026

·

9 min read

Share

LET'S CONNECT

Connect with Creuto!

Ready to take the first step towards unlocking opportunities, realizing goals, and embracing innovation? We're here and eager to connect.

We don't just aim to fit in – we strive to stand out. Experience the perfect blend of innovation, excellence, and trust that makes us truly unforgettable. Discover the difference with Creuto.

© 2026 Creuto All Rights Reserved