In December 2024 — barely a month after Anthropic open-sourced the Model Context Protocol — MCP co-creator Justin Spahr-Summers opened a public design discussion with an uncomfortable observation: the protocol's long-lived, stateful connections were a poor fit for serverless deployments. He sketched three possible paths forward, including a fully stateless option. Eighteen months later, the protocol has largely embraced exactly that option: the 2026-07-28 spec revision deletes protocol-level sessions, the Mcp-Session-Id header, the initialize handshake, and the GET/DELETE stream endpoints outright.
So here is the verdict up front, for anyone running MCP servers on their own infrastructure: going stateless costs you nothing per request and buys you scale-to-zero, load-balancer-friendly deployments with no session affinity — but it deletes every server-to-client primitive (push notifications, mid-call confirmations, sampling), so any long-running or human-gated operation like a deploy or rollback must be redesigned as an explicitly polled task, not a held-open conversation. That is the whole post in one paragraph — the rest is the receipts and the boundary line:
| Capability | Stateful (2025-11-25 and earlier) | Stateless (2026-07-28) |
|---|---|---|
| Connection model | Long-lived session, Mcp-Session-Id per client | One HTTP POST per request, no session |
| Session affinity | Required — sticky routing or shared store | None — any replica serves any request |
| Scale-to-zero / serverless | Blocked (a sleeping function drops the session) | Native (Lambda, Workers, Knative all work) |
| Server → client push (progress, logs) | Yes, over the session stream | Gone — client must poll |
| Mid-call confirmation ("approve this deploy?") | Elicitation over the open channel | Gone — redesign as task states the client polls |
| Long-running ops | Held-open stream plus Tasks (SEP-1686) | Tasks plus polling only |
| Per-session memory | Grows with concurrent agents | Flat — one operator measured 2.2 GB across 36 sessions collapsing to ~80 MB in a single daemon |
Two things to read off this table before the detail. First, the bottom row is not hypothetical: a token-saving MCP proxy operator measured roughly 130 MB of resident processes per session — 2.2 GB for 36 sessions, extrapolating to ~6.5 GB at 50 — and collapsed it to a single ~80 MB daemon by moving to the stateless transport. Session state was the cost center.
Second, the middle rows are the real migration work: everything your server currently pushes has to become something the client asks for. Keep reading for exactly how.
How a serverless complaint became the default in six dates
November 2024: MCP launches stateful. Anthropic engineers David Soria Parra and Justin Spahr-Summers open-source the Model Context Protocol, a JSON-RPC-based standard — consciously modeled on the Language Server Protocol — for connecting AI applications to tools and data. Like LSP, it assumes a standing connection: client and server shake hands, hold the line open, and talk.
December 2024: the stateless question is asked in public. Within weeks of launch, Spahr-Summers opens a design discussion flagging that long-lived, stateful connections limit serverless deployments, sketching three paths including the fully stateless one. The tension is nearly as old as the protocol itself — a point VentureBeat's later coverage of the change would call out explicitly.
March 2025 (spec 2025-03-26): Streamable HTTP replaces HTTP+SSE, sessions become optional. The old HTTP+SSE transport — a POST endpoint plus a dangling server-sent-events stream — is replaced by Streamable HTTP, and the spec downgrades sessions from mandatory to MAY: a server may assign an Mcp-Session-Id at initialization, and one that doesn't is a legal stateless server. Frameworks pick this up fast: FastMCP exposes it as a stateless_http run option, and AWS publishes sample MCP servers running natively on Lambda plus API Gateway with no bridging components — deployments that were previously impossible without sticky sessions.
June–November 2025: the protocol grows up around async. Spec 2025-06-18 requires the MCP-Protocol-Version header on subsequent requests and adds structured tool outputs and elicitation. Spec 2025-11-25 — the current stable revision — adds Tasks (SEP-1686) for long-running operations, an extensions framework, and server-side agent loops. Tasks matter enormously for what comes next: they are the protocol's answer to "how does anything take longer than one request/response without a held-open connection."
December 2025: maintainers commit to the stateless direction. At a maintainer meeting on the future of MCP transports, covered by VentureBeat as "MCP just got its biggest update ever," the core team formally commits to the stateless path Spahr-Summers sketched a year earlier.
July 2026 (spec 2026-07-28): sessions are deleted, not deprecated. Two spec proposals land together: SEP-2567 removes protocol-level sessions and the Mcp-Session-Id header from Streamable HTTP, and SEP-2575 removes the initialize/notifications/initialized handshake entirely — every request now carries its own protocol version, client identity, and capability metadata. GET and DELETE on the MCP endpoint answer 405. Servers must ignore a client-sent session id rather than honor it. Early adopters' changelogs tell the story plainly: "HTTP mode is now stateless," per-session server accumulation gone, sticky-routing requirement gone.
Note the honest caveat from the ecosystem: servers pinned to 2025-11-25 keep working — SDKs and clients migrate over time — but the deprecation clock on the session-era primitives starts now.
What stateless actually deletes
Be precise about the casualty list, because "stateless" undersells it — this revision removes a whole interaction model:
- Protocol sessions and
Mcp-Session-Id. No issued ids, no accepted ids, no session count on health endpoints. If your monitoring dashboards read a sessions gauge, update them. - The
initializehandshake. There is no more "hello, let's agree on versions and capabilities once." Each request is self-describing, carrying_metaprotocol version, client info, and capabilities. This is overhead per request — small, but real — traded for zero connection setup. - GET/DELETE streams. POST-only. The server-sent-events channel that carried server-initiated messages is gone at the transport level.
- Server-initiated elicitation, sampling, roots subscriptions, and unsolicited notifications. This is the sharp edge. Anything where the server starts talking — asking the operator to confirm a destructive action mid-tool-call, requesting the client's LLM to draft text, pushing a progress line — has no channel in stateless mode. SDK changelogs are explicit: stateless forces the "no server→client features" path.
- Replay and resumability via
Last-Event-ID. Per-request SSE can still stream a single response, but session-scoped replay across reconnects goes with sessions. Durability becomes your problem, one layer up.
What you keep: tools, resources, prompts, and Tasks. The request/response core of the protocol is untouched — which is why stateless servers feel identical for the most common operations (list tools, call a fast tool, read a resource) while diverging sharply the moment an operation outlives one round trip.
What breaks on a deploy/rollback tool server — a worked example
Make it concrete. Suppose your platform exposes deploy and rollback as MCP tools so coding agents can ship tenant apps: the agent calls deploy with an app id and a git SHA, and the server builds, pushes, and reports. Under the session-era transport, the natural implementation holds the stream open: progress lines flow down as notifications, and if the deploy needs a human decision — "staging is red, promote to production anyway?" — the server fires an elicitation request back up the same channel and waits.
Every half of that design breaks stateless. Walk through the redesign:
- The call returns a task, not a result.
deployimmediately returns a task id (Tasks, SEP-1686) instead of holding anything open. The build runs detached — in your job system, not in the request handler. - Progress becomes pollable state. The agent polls
tasks/result(or your status tool) on its own cadence. You lose push latency — the agent learns about completion one poll interval late — and you gain something real: if the agent's process dies mid-deploy, the replacement agent resumes polling the same task id instead of inheriting a dead socket. The spec's own client guidance says it outright: store task IDs durably so polling can resume after a client restart. - Confirmations become task states, not interruptions. "Promote anyway?" can no longer arrive as a server-initiated question mid-call. Model it as a task that transitions to an explicitly
input_required-style awaiting state, which the agent observes on its next poll and answers with a follow-up tool call (approve_promotionwith the task id). This is the multi-round-trip pattern the TODO-era discussions pointed at: the round trips still happen, but the client drives every one of them. Design your tool schemas so the awaiting states are enumerable — an agent that has to guess what a task is waiting for will guess wrong. - Audit gets simpler, not harder. This is the underappreciated win for deploy tooling specifically. A session-era audit trail reconstructs "what happened" from a stream of pushes interleaved across connections. A task-based trail is a state machine with timestamps: created, running, awaiting-input, approved, succeeded. If your platform already audits deploys for compliance, the stateless shape is the shape you wanted anyway.
The honest cost ledger for this server: one extra poll interval of latency on every status change, per-request metadata overhead on every call, and a real redesign of any elicitation-based approval flow. The honest gain ledger: no session store to operate, no sticky routing to configure, deploys that survive agent restarts, and an audit trail shaped like a state machine.
Where to run which mode — the decision
Not everything should go stateless on day one. The split that falls out of the table:
- Run stateless, scale-to-zero, when: the tools are fast and side-effect-free (list apps, read logs, fetch status), traffic is bursty (agent fleets that thunder at 9am and vanish at night), or you run behind load balancers that make stickiness painful. This is also the right default for any new MCP server: the ecosystem's direction of travel is unambiguous, and building on sessions now means migrating later under the deprecation clock.
- Keep a durable, stateful-ish server when: the operations are long, destructive, or audited — deploys, rollbacks, migrations — and you value the task-state durability and audit shape more than scale-to-zero. Note "stateful-ish": even here, prefer the Tasks-plus-polling pattern over held-open streams. The session transport is deprecated; the durability you want lives in your task store, not in a socket.
- Budget for the SDK migration either way. The 2026-07-28 revision is breaking: SDK majors are landing the stateless core (some as hard forks that delete the session spine), and the deprecation clock on roots, sampling, and logging has started under the new feature lifecycle policy. Pin your spec revision explicitly, test both modes if your SDK offers a dual-mode flag, and don't discover the 405s from your agents' error logs.
One more consideration for the self-hosting audience: stateless MCP servers are better tenants on a git-push PaaS than session-era ones ever were. No connection state means no affinity rules in your router, no memory growth per connected agent on your smallest instance size, and horizontal scaling that actually works by just adding replicas. The protocol met your platform halfway — the remaining work is redesigning the three stateful habits (push, interrupt, hold-open) out of your tool servers.
The connection was scaffolding; the tasks were the building
Zoom out and the arc is familiar: a protocol launches with the simplest correct interaction model (hold the line open, like LSP on a developer's laptop), the serverless world complains within weeks, and two years of spec work convert every stateful convenience into an explicit, durable, client-driven equivalent. Sessions became optional, then vestigial, then deleted; the handshake dissolved into per-request metadata; server push became client polling over tasks.
If you operate MCP servers, the migration checklist fits on an index card: enumerate every server-initiated message your tools send, convert each to a pollable task state, store task ids durably, pin your spec revision, and update the dashboards that counted sessions. Do that, and statelessness stops being a breaking change and starts being what it was supposed to be all along — one less piece of connection state for you to babysit at 3am.
Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Stateless MCP tool servers (no session affinity, no sticky routing, replicas that just work) are exactly the kind of workload it loves to host. Star the repo on GitHub or deploy your first app today.


