-
Notifications
You must be signed in to change notification settings - Fork 472
Description
Describe the bug
When the server returns HTTP 404 for an expired session, the rmcp streamable HTTP client:
- Surfaces the error to the caller (as
UnexpectedServerResponsesince fix(streamable-http): map stale session 401 to status-aware error #709) - Does not attempt to re-initialize the session
- All subsequent requests continue using the stale session ID and keep failing permanently
This violates MCP Spec 2025-03-26, Session Management, rule 4:
When a client receives HTTP 404 in response to a request containing an
Mcp-Session-Id, it MUST start a new session by sending a newInitializeRequestwithout a session ID attached.
To Reproduce
- Connect an rmcp streamable HTTP client to a stateful MCP server with session TTL
- Make tool calls (works fine)
- Wait for the server-side session to expire
- Make another tool call
- Client fails with
UnexpectedServerResponse("HTTP 404: ...")(orerror decoding response bodyon rmcp 0.15.0) - All subsequent requests keep failing with the same stale session ID. No recovery.
Expected behavior
On HTTP 404, the client should automatically start a new session by sending a new InitializeRequest without a session ID, then retry the failed request.
Logs
Server-side logs from a real MCP server (CodeX as client, rmcp 0.15.0):
12:45:09 INFO Session expired {"sessionId":"e52ece66-..."}
12:46:37 WARN Session not found {"sessionId":"e52ece66-...","bodyMethod":"tools/call"}
12:46:37 WARN Session not found {"sessionId":"e52ece66-...","bodyMethod":"tools/call"}
12:46:37 WARN Session not found {"sessionId":"e52ece66-...","bodyMethod":"tools/call"}
12:46:49 WARN Session not found {"sessionId":"e52ece66-...","bodyMethod":"tools/call"}
12:47:01 WARN Session not found {"sessionId":"e52ece66-...","bodyMethod":"resources/list"}
12:47:28 WARN Session not found {"sessionId":"e52ece66-...","bodyMethod":"tools/call"}
No initialize request was ever sent after the 404 responses. The client is stuck permanently until restarted.
CodeX issue: openai/codex#13969
Additional context
Code evidence: In StreamableHttpClientWorker::run() (crates/rmcp/src/transport/streamable_http_client.rs), the main event loop:
let send_result = match response {
Err(e) => Err(e), // ← error forwarded as-is, no re-init
Ok(StreamableHttpPostResponse::Accepted) => { ... }
Ok(StreamableHttpPostResponse::Json(message, ..)) => { ... }
Ok(StreamableHttpPostResponse::Sse(stream, ..)) => { ... }
};
let _ = responder.send(send_result);There is no code path that detects session expiry and triggers re-initialization. The session_id variable is immutable after the initial handshake.
The existing test test_streamable_http_stale_session.rs (added in #709) only asserts that stale sessions produce an UnexpectedServerResponse error. It does not test or expect re-initialization.
Related issues:
- streamable-http stale session returns UnexpectedContentType(None) instead of status-aware error #688 — stale session error was
UnexpectedContentType(fixed, error message improved) - Streamable HTTP: Session not found returns 401 instead of 404 (MCP spec violation) #689 — rmcp server returned 401 instead of 404 for stale sessions (fixed)
- fix(streamable-http): map stale session 401 to status-aware error #709 — non-success HTTP now returns
UnexpectedServerResponse(fixed, error message improved) - reqwest streamable HTTP client drops JSON-RPC error bodies on HTTP 4xx into TransportSend #724 — 4xx with JSON-RPC body should parse as
McpError(open)
None of these address the missing re-initialization behavior.