Deploying your MCP server stateless will silently break sampling and elicitation

Published: 2026-02-24 Last verified: 2026-04-19 independently-confirmed
8 claims 8 tested finding

Deploying your MCP server stateless will silently break sampling and elicitation

From Theory Delta | Methodology | Published 2026-02-24

What you expect

MCP Streamable HTTP (spec 2025-03-26, replacing deprecated SSE) supports two modes: stateless for serverless deployment (Lambda, Workers) and stateful for full-featured servers. The spec presents this as a deployment choice with a clear tradeoff — you pick the mode that fits your infrastructure.

What actually happens

The tradeoff is not a dial — it is a hard binary with three production failure modes that are not obvious from the spec:

Failure 1: Sampling silently hangs in stateless mode. FastMCP with stateless_http=True permanently hangs (no error, no timeout) when the server attempts sampling. Maintainers closed the original report (FastMCP #678) as “expected behavior.” Bidirectional communication is architecturally impossible when each request creates and destroys a fresh server instance. FastMCP #1156 later added explicit detection and error messages, but the default experience before that was an infinite hang with no diagnostic output.

Failure 2: Load balancers strip Mcp-Session-Id. Nginx, ALB, and most proxies require explicit configuration to route on this header. MCP clients use fetch() and do not forward cookies, making cookie-based sticky sessions non-functional. Without header-based routing, stateful requests from the same client hit different backend instances and silently lose session context. (python-sdk #880 — labeled P1, documents the horizontal scaling gap)

Failure 3: CORS silences the session ID. The MCP Inspector itself had this bug through v0.18.0 — it stored the session ID but did not attach it to follow-up requests, producing false-negative debugging results. Developers debugging stateful servers with the Inspector see the server appear broken when the failure is in the debugging tool. (Inspector #905, still open as of v0.21.2 in Apr 2026)

Additionally: DNS rebinding protection is off by default in both SDKs. Any MCP server running on localhost is vulnerable to malicious webpages without explicit origin validation.

What this means for you

If you are building an MCP server and deploying it on serverless infrastructure (Lambda, Cloudflare Workers) to get easy scaling, you are choosing stateless mode. That choice silently removes sampling and elicitation from your feature set — permanently, at the protocol level, not fixable by configuration.

If your tools are pure functions today and you later want to add a tool that asks the client for information mid-task, you cannot. Stateless mode cannot support that interaction pattern by design. You will need to redeploy with sticky sessions and a load balancer configured for header-based routing — a non-trivial infrastructure change.

The stateful path also cannot scale horizontally today without custom session storage. The Python SDK stores sessions in-memory with no external persistence interface. The AWS reference implementation explicitly documents: “As of early May 2025, none of the official MCP SDKs support external session persistence.” Two patterns exist in the official sample repo because neither fully works: stateless loses features, stateful cannot scale without sticky session hacks.

The current stateless pattern is explicitly a workaround per MCP spec maintainers — a spec redesign targeting June 2026 aims to eliminate the binary by replacing the initialize handshake with per-request capability metadata. Builders adopting the fresh-instance-per-request pattern today should expect to refactor when the new spec ships. ^[unlinked — source is MCP maintainer comments in SDK issues, no single canonical post-mortem URL]

What to do

  1. If your tools are pure functions (no sampling, no cross-request state, no elicitation): stateless on Cloudflare Workers + Hono is the lowest-friction path. Accept the feature loss explicitly and document it so future developers on your team don’t add sampling tools expecting them to work.
  2. If your server needs to ask the client for information mid-task: you cannot use stateless. Deploy stateful with sticky sessions via load balancer header routing on Mcp-Session-Id.
  3. Do not test stateful servers with MCP Inspector alone — the Inspector’s session ID bug (#905, unfixed through v0.18.0, still open as of v0.21.2 in Apr 2026) produces false negatives. Test with a real client first.
  4. Enable DNS rebinding protection explicitly: enableDnsRebindingProtection: true (TypeScript SDK) or TransportSecuritySettings (Python SDK). Bind to 127.0.0.1, not 0.0.0.0. Validate Origin and Host headers.
  5. Expect to refactor. The current pattern is explicitly a workaround. Monitor modelcontextprotocol/modelcontextprotocol for the June 2026 spec redesign.

Falsification criterion: This claim would be disproved by demonstrating a stateless MCP HTTP deployment where sampling or elicitation works correctly without persistent session state — i.e., a serverless MCP server (Lambda/Workers) that successfully completes a sampling round-trip.

Evidence

ToolVersionResult
MCP TypeScript SDKv1.x / v2 pre-releaseindependently-confirmed: DNS rebinding off by default (CVE-2025-66414)
MCP Python SDKv1.xindependently-confirmed: session persistence in-memory only (#880)
FastMCPlatest (Feb 2026)independently-confirmed: sampling hangs in stateless mode (#678)
Google ADKlatest (Feb 2026)docs-reviewed: Streamable HTTP transport reviewed
Cloudflare Workers (Hono)latest (Feb 2026)source-reviewed: stateless deployment path validated
AWS Lambdalatest (Feb 2026)docs-reviewed: stateless deployment path reviewed
MCP Inspectorthrough v0.18.0 (issue still open as of v0.21.2, Apr 2026)independently-confirmed: session ID not attached to follow-up requests (#905)

Confidence: source-reviewed + independently-confirmed — 7 environments reviewed. No commands were executed against running instances. Failure modes independently confirmed by CVEs (CVE-2025-66414, CVE-2025-66416), GHSAs, and GitHub issues linked above. Note: scope_matches=false because the core claim is about “MCP stateless mode” at the protocol level, but primary evidence is from FastMCP (one library) plus independent issue reports. Multiple implementations were reviewed but not executed.

Unlinked claim: The June 2026 spec redesign timeline is sourced from maintainer comments across multiple SDK issues, not a single canonical URL.

Open questions: Has anyone implemented external session persistence (Redis/DynamoDB) for the MCP Python SDK? Does the proposed June 2026 spec redesign eliminate the stateless/stateful binary? Are there MCP clients that correctly handle Mcp-Session-Id through standard proxies without custom configuration?

Seen different? Contribute your evidence — theory delta is what makes this knowledge base work.