[v1.x] fix: return HTTP 404 for unknown session IDs instead of 400 (#1945)
Some checks failed
Main branch checks / checks (push) Has been cancelled
Some checks failed
Main branch checks / checks (push) Has been cancelled
Co-authored-by: Maxime <67350340+max-rousseau@users.noreply.github.com> Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
This commit is contained in:
parent
d891525958
commit
3d9d34552a
2 changed files with 67 additions and 4 deletions
|
|
@ -22,6 +22,7 @@ from mcp.server.streamable_http import (
|
|||
StreamableHTTPServerTransport,
|
||||
)
|
||||
from mcp.server.transport_security import TransportSecuritySettings
|
||||
from mcp.types import INVALID_REQUEST, ErrorData, JSONRPCError
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -276,10 +277,21 @@ class StreamableHTTPSessionManager:
|
|||
|
||||
# Handle the HTTP request and return the response
|
||||
await http_transport.handle_request(scope, receive, send)
|
||||
else: # pragma: no cover
|
||||
# Invalid session ID
|
||||
else:
|
||||
# Unknown or expired session ID - return 404 per MCP spec
|
||||
# TODO: Align error code once spec clarifies
|
||||
# See: https://github.com/modelcontextprotocol/python-sdk/issues/1821
|
||||
error_response = JSONRPCError(
|
||||
jsonrpc="2.0",
|
||||
id="server-error",
|
||||
error=ErrorData(
|
||||
code=INVALID_REQUEST,
|
||||
message="Session not found",
|
||||
),
|
||||
)
|
||||
response = Response(
|
||||
"Bad Request: No valid session ID provided",
|
||||
status_code=HTTPStatus.BAD_REQUEST,
|
||||
content=error_response.model_dump_json(by_alias=True, exclude_none=True),
|
||||
status_code=HTTPStatus.NOT_FOUND,
|
||||
media_type="application/json",
|
||||
)
|
||||
await response(scope, receive, send)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
"""Tests for StreamableHTTPSessionManager."""
|
||||
|
||||
import json
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
|
|
@ -11,6 +12,7 @@ from mcp.server import streamable_http_manager
|
|||
from mcp.server.lowlevel import Server
|
||||
from mcp.server.streamable_http import MCP_SESSION_ID_HEADER, StreamableHTTPServerTransport
|
||||
from mcp.server.streamable_http_manager import StreamableHTTPSessionManager
|
||||
from mcp.types import INVALID_REQUEST
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
|
@ -262,3 +264,52 @@ async def test_stateless_requests_memory_cleanup():
|
|||
|
||||
# Verify internal state is cleaned up
|
||||
assert len(transport._request_streams) == 0, "Transport should have no active request streams"
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_unknown_session_id_returns_404():
|
||||
"""Test that requests with unknown session IDs return HTTP 404 per MCP spec."""
|
||||
app = Server("test-unknown-session")
|
||||
manager = StreamableHTTPSessionManager(app=app)
|
||||
|
||||
async with manager.run():
|
||||
sent_messages: list[Message] = []
|
||||
response_body = b""
|
||||
|
||||
async def mock_send(message: Message):
|
||||
nonlocal response_body
|
||||
sent_messages.append(message)
|
||||
if message["type"] == "http.response.body":
|
||||
response_body += message.get("body", b"")
|
||||
|
||||
# Request with a non-existent session ID
|
||||
scope = {
|
||||
"type": "http",
|
||||
"method": "POST",
|
||||
"path": "/mcp",
|
||||
"headers": [
|
||||
(b"content-type", b"application/json"),
|
||||
(b"accept", b"application/json, text/event-stream"),
|
||||
(b"mcp-session-id", b"non-existent-session-id"),
|
||||
],
|
||||
}
|
||||
|
||||
async def mock_receive():
|
||||
return {"type": "http.request", "body": b"{}", "more_body": False} # pragma: no cover
|
||||
|
||||
await manager.handle_request(scope, mock_receive, mock_send)
|
||||
|
||||
# Find the response start message
|
||||
response_start = next(
|
||||
(msg for msg in sent_messages if msg["type"] == "http.response.start"),
|
||||
None,
|
||||
)
|
||||
assert response_start is not None, "Should have sent a response"
|
||||
assert response_start["status"] == 404, "Should return HTTP 404 for unknown session ID"
|
||||
|
||||
# Verify JSON-RPC error format
|
||||
error_data = json.loads(response_body)
|
||||
assert error_data["jsonrpc"] == "2.0"
|
||||
assert error_data["id"] == "server-error"
|
||||
assert error_data["error"]["code"] == INVALID_REQUEST
|
||||
assert error_data["error"]["message"] == "Session not found"
|
||||
|
|
|
|||
Loading…
Reference in a new issue