Return 405 for listen-mode GET on stateless streamable HTTP transport - #3574
SammyTourani wants to merge 1 commit into
Conversation
Stateless mode never sends server-initiated messages, so the standalone GET stream previously opened a 200 text/event-stream response and held it open with zero bytes, hanging clients. Add a stateless flag to StreamableHTTPServerTransport and answer 405 for the listen-mode GET when set, per the 2025-03-26 spec. Fixes modelcontextprotocol#3492.
|
This PR has been closed automatically. This repo only keeps pull requests open when they come from a maintainer, or from a contributor a maintainer has assigned to the linked issue, and you aren't currently assigned to #3492. If a maintainer assigns you to #3492, this PR reopens on its own and there's nothing more you need to do here. Assignment is a maintainer call based on capacity; comments that only ask to be assigned don't factor in. What does help is engaging on the issue itself by confirming the repro, explaining why it matters for your use case, or describing the approach you'd take. You're welcome to keep pushing commits here (just avoid force-pushing, since GitHub can't reopen a rewritten branch), but that on its own won't get the PR reviewed or the issue assigned, and realistically most auto-closed PRs stay closed. There's no need to open a new PR either way. CONTRIBUTING.md has the full reasoning, but in short:
Maintainers: reopen, remove |
Fixes #3492
StreamableHTTPServerTransport.__init__gets a newstateless: bool = Falseparameter, stored asself._stateless.mcp_session_id is None, eventhough that's true for every stateless transport today. An existing test
(
test_standalone_stream_teardown_between_dequeues_is_not_an_error)constructs a transport directly with
mcp_session_id=Nonespecifically toexercise the standalone-GET-stream teardown path, so
Nonesession id isnot a reliable stand-in for "stateless" at the transport level — it's
already used as "no session tracking, but the standalone stream still
works" in that test. An explicit flag avoids overloading that meaning,
which is also what the issue's first commenter (arpankernel) suggested.
_handle_get_requestnow returns 405 immediately whenself._statelessis set, before any Accept-header check or stream creation.
streamable_http_manager.py::_handle_stateless_requestpassesstateless=Truewhen constructing the transport (the one and only placethat builds a stateless transport).
stateless_appfixture (via a newstateless=Falsekwarg onthe existing
running_apphelper) andtest_get_sse_stream_returns_405_when_stateless, which asserts the GETreturns 405 within a 5s
anyio.fail_after(so if the fix ever regresses,the test fails fast instead of hanging CI).
Verification