Transparent proxy cannot serve stateless streamable-http backends (no client-facing Mcp-Session-Id when the backend issues none)
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 2.2k
- Forks
- 300
- Avg merge
- 1d 15h
- Merged PRs (30d)
- 184
Description
Description
The transparent proxy (used by both MCPServer and MCPRemoteProxy with transport: streamable-http) cannot serve a stateless streamable-http backend — one that, as the spec allows, never issues an Mcp-Session-Id. The proxy runs stateful session semantics unconditionally, but its client-facing session id comes only from capturing the backend's initialize response header. When the backend issues none:
- the proxy's
initializeresponse to the client carries noMcp-Session-Idheader (body passes through fine); - a subsequent
tools/listwithout a session id returns HTTP 200 with an empty body; - a client-generated
Mcp-Session-Idis rejected with{"error":{"code":-32001,"message":"Session not found"},"id":null,"jsonrpc":"2.0"}; GET /mcpreturns405withAllow: DELETE, POST, confirming the proxy is in stateful mode with the session lifecycle active.
So a spec-compliant stateless backend (per the streamable-http transport spec, a server MAY assign a session ID at initialization time — session management is optional) is unusable behind the proxy: clients can initialize but never reach tools/list/tools/call.
Session pass-through itself works correctly: with a session-issuing backend (googleapis/mcp-toolbox 1.6.0, which issues Mcp-Session-Id when the client initializes with protocolVersion: 2025-03-26) the proxy forwards the backend's session id to the client and the full flow (initialize → notifications/initialized → tools/list → tools/call) succeeds end to end. The gap is only the stateless-backend case.
Steps to reproduce
- Deploy the operator (v0.34.0) and an
MCPRemoteProxy(orMCPServer) withtransport: streamable-httppointing at any stateless streamable-http MCP server. Two easy backends that reproduce it:- a FastMCP (Python) server with
stateless_http=True; googleapis/mcp-toolboxwhen the client initializes withprotocolVersion: 2025-06-18(it issues session ids only for2025-03-26).
- a FastMCP (Python) server with
POST /mcpinitializethrough the proxy → 200, valid body, noMcp-Session-Idresponse header (verified withcurl -vdirectly against the proxy pod via port-forward, so no ingress is involved).POST /mcptools/list→ HTTP 200,Content-Typeper backend, empty body (0 bytes). Directly against the backend the same request returns the full tool list.POST /mcptools/listwith a client-generatedMcp-Session-Id→-32001 Session not found.
Expected behavior
Either the proxy synthesizes and manages a client-facing session id when the backend issues none (bridging stateful clients to stateless backends), or an explicit stateless mode is exposed in the operator CRDs. The RunConfig stateless flag exists internally (thv run --stateless) but is not exposed on MCPRemoteProxy/MCPServer (kubectl explain mcpremoteproxy.spec.stateless → field does not exist, v0.34.0), and it appears to gate only HTTP methods and health checks rather than session handling.
Actual behavior
Stateless backends are unreachable through the proxy beyond initialize (details above).
Environment
- ToolHive operator v0.34.0 (operator mode, Kubernetes v1.26.6); code paths look unchanged as of v0.36.0
- Backends verified: FastMCP with
stateless_http=True;googleapis/mcp-toolbox1.6.0 atprotocolVersion: 2025-06-18
Additional context
Not all MCP servers need sessions; for a server that uses no stateful features they are pure protocol overhead, which is presumably why the upcoming 2026-07-28 revision removes them — I see #5743 / #5755 already design for that. This report is about the current stable revisions, where stateless servers are already legal and already break behind the proxy today; an interim fix (or folding "current-spec stateless backends" explicitly into that workstream) would unblock gateways fronting such servers now. Related art in another MCP gateway: agentgateway/agentgateway#221.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by tracing streamable-http handling for MCPRemoteProxy and MCPServer, including the RunConfig stateless flag and the thv run --stateless entry point. Reproduce the initialize and tools/list requests against a backend that omits Mcp-Session-Id, then inspect the operator CRD fields. Done means a compliant stateless backend can complete tools/list and tools/call through the proxy, or an explicit supported stateless mode is exposed.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go, kubernetes
- Domain
- backend-api-design, cloud
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 50/100