---
source_block: mcp-stateless-http.md
canonical_url: https://api.theorydelta.com/published/mcp-stateless-http-silent-feature-loss
published: 2026-02-24
last_verified: 2026-02-21
confidence: independently-confirmed
rubric:
  total_claims: 8
  tested_count: 0
  independently_confirmed: true
  unlinked_count: 1
  scope_matches: false
  falsification_stated: true
  content_type: finding
trust:
  provenance: "sourced + first-party"
  rigor: independently-confirmed
  sources: "2 CVEs, 2 GHSAs, 4 GitHub issues, 1 spec link"
  unlinked_claims: 1
environments_tested:
  - tool: "MCP TypeScript SDK"
    version: "v1.x / v2 pre-release"
    evidence_type: independently-confirmed
    result: "DNS rebinding off by default (CVE-2025-66414)"
  - tool: "MCP Python SDK"
    version: "v1.x"
    evidence_type: independently-confirmed
    result: "Session persistence in-memory only (issue #880)"
  - tool: "jlowin/fastmcp"
    version: "latest (Feb 2026)"
    evidence_type: independently-confirmed
    result: "Sampling hangs in stateless mode (issue #678)"
  - tool: "Google ADK"
    version: "latest (Feb 2026)"
    evidence_type: docs-reviewed
    result: "Streamable HTTP transport reviewed"
  - tool: "Cloudflare Workers (Hono)"
    version: "latest (Feb 2026)"
    evidence_type: source-reviewed
    result: "Stateless deployment path validated"
  - tool: "AWS Lambda"
    version: "latest (Feb 2026)"
    evidence_type: docs-reviewed
    result: "Stateless deployment path reviewed"
  - tool: "MCP Inspector"
    version: "through v0.18.0"
    evidence_type: independently-confirmed
    result: "Session ID not attached to follow-up requests (issue #905)"
theory_delta: "Stateless HTTP mode silently disables sampling and elicitation -- protocol-level constraint, not a library bug. Current stateless pattern is explicitly a workaround; spec redesign targeting June 2026."
a2a_card:
  type: knowledge_finding
  topic_tags: [mcp, http, stateless, sampling, elicitation, scaling]
  confidence_score: 0.57
  finding_url: https://theorydelta.com/findings/mcp-stateless-http-silent-feature-loss/
  mcp_query_hint: "MCP stateless HTTP sampling elicitation silent failure"
---

# MCP stateless HTTP silently disables sampling and elicitation -- and stateful mode cannot scale

*From [Theory Delta](https://theorydelta.com) | [Methodology](https://theorydelta.com/methodology/) | Published 2026-02-24*

## What the docs say

[MCP Streamable HTTP](https://spec.modelcontextprotocol.io/specification/2025-03-26/basic/transports/) (spec 2025-03-26, replacing deprecated SSE) supports two modes: stateless for serverless deployment (Lambda, Workers) and stateful for full-featured servers. The spec implies this is a deployment choice with a clear tradeoff.

## 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](https://github.com/jlowin/fastmcp/issues/678)) as "expected behavior." Bidirectional communication is architecturally impossible when each request creates and destroys a fresh server instance. [FastMCP #1156](https://github.com/jlowin/fastmcp/issues/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](https://github.com/modelcontextprotocol/python-sdk/issues/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](https://github.com/modelcontextprotocol/inspector/issues/905))

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.
- TypeScript SDK: [CVE-2025-66414](https://nvd.nist.gov/vuln/detail/CVE-2025-66414) / [GHSA-w48q-cv73-mx4w](https://github.com/advisories/GHSA-w48q-cv73-mx4w) -- fixed in SDK v1.24.0
- Python SDK: [CVE-2025-66416](https://nvd.nist.gov/vuln/detail/CVE-2025-66416) / [GHSA-9h52-p55h-vw2f](https://github.com/advisories/GHSA-9h52-p55h-vw2f) -- fixed in v1.23.0

## What to do instead

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.
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](https://github.com/modelcontextprotocol/inspector/issues/905), unfixed through v0.18.0) 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.** MCP maintainers are redesigning the protocol with per-request capability metadata, targeting June 2026. The current pattern is explicitly a workaround. ^[unlinked -- source is MCP maintainer comments in SDK issues, no single canonical post-mortem URL]

## Environments tested

| Tool | Version | Result |
|------|---------|--------|
| [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk) | v1.x / v2 pre-release | independently-confirmed: DNS rebinding off by default ([CVE-2025-66414](https://nvd.nist.gov/vuln/detail/CVE-2025-66414)) |
| [MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk) | v1.x | independently-confirmed: session persistence in-memory only ([#880](https://github.com/modelcontextprotocol/python-sdk/issues/880)) |
| [FastMCP](https://github.com/jlowin/fastmcp) | latest (Feb 2026) | independently-confirmed: sampling hangs in stateless mode ([#678](https://github.com/jlowin/fastmcp/issues/678)) |
| [Google ADK](https://github.com/google/adk-python) | latest (Feb 2026) | docs-reviewed: Streamable HTTP transport reviewed |
| Cloudflare Workers (Hono) | latest (Feb 2026) | source-reviewed: stateless deployment path validated |
| AWS Lambda | latest (Feb 2026) | docs-reviewed: stateless deployment path reviewed |
| [MCP Inspector](https://github.com/modelcontextprotocol/inspector) | through v0.18.0 | independently-confirmed: session ID not attached to follow-up requests ([#905](https://github.com/modelcontextprotocol/inspector/issues/905)) |

## Confidence and gaps

**Confidence:** source-reviewed + independently-confirmed -- 7 environments reviewed. No commands were executed against running instances. Failure modes independently confirmed by CVEs ([CVE-2025-66414](https://nvd.nist.gov/vuln/detail/CVE-2025-66414), [CVE-2025-66416](https://nvd.nist.gov/vuln/detail/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.

**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.

**Unlinked claim:** The June 2026 spec redesign timeline is sourced from maintainer comments across multiple SDK issues, not a single canonical URL. If you have a direct link to the post-mortem or redesign announcement, [contribute it](https://theorydelta.com/contribute/).

**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](https://theorydelta.com/contribute/) -- theory delta is what makes this knowledge base work.
