-
Notifications
You must be signed in to change notification settings - Fork 469
Description
Summary
When a client sends a request with an Mcp-Session-Id header for a session that no longer exists (expired, server restarted, etc.), the StreamableHttpService returns HTTP 401 Unauthorized instead of HTTP 404 Not Found.
The MCP specification (2025-03-26) requires 404:
"The server MAY terminate the session at any time, after which it MUST respond to requests containing that session ID with HTTP 404 Not Found."
"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."
Location
crates/rmcp/src/transport/streamable_http_server/tower.rs
handle_get (lines ~203-208):
if !has_session {
// unauthorized
return Ok(Response::builder()
.status(http::StatusCode::UNAUTHORIZED)
.body(Full::new(Bytes::from("Unauthorized: Session not found")).boxed())
.expect("valid response"));
}handle_post (lines ~315-320):
if !has_session {
// unauthorized
return Ok(Response::builder()
.status(http::StatusCode::UNAUTHORIZED)
.body(Full::new(Bytes::from("Unauthorized: Session not found")).boxed())
.expect("valid response"));
}resume errors (line ~222):
internal_error_response("resume session") returns HTTP 500 when SessionManager::resume() fails. If resume fails because the session is gone, this should also be 404.
Expected Behavior
When has_session() returns false for a session ID that was previously valid:
- Return HTTP 404 Not Found (not 401)
- This tells the client to start a new session with a fresh
InitializeRequest
When resume() returns an error because the session is not resumable:
- Return HTTP 404 Not Found (not 500)
- Same rationale: the session is gone, client should create a new one
Impact
Clients that strictly follow the MCP spec only check for 404 to trigger session re-creation. Receiving 401 or 500 instead may cause:
- Retry loops (client retries the same dead session instead of creating a new one)
- Connection storms (multiple reconnection attempts all failing with 500)
Suggested Fix
// In handle_get and handle_post, when has_session returns false:
if !has_session {
return Ok(Response::builder()
.status(http::StatusCode::NOT_FOUND)
.body(Full::new(Bytes::from("Session not found")).boxed())
.expect("valid response"));
}For resume failures, consider distinguishing "session not found" errors from true internal errors, mapping the former to 404.