Skip to content

fix: reject unsupported HTTP methods early in session manager#2191

Open
Br1an67 wants to merge 2 commits intomodelcontextprotocol:mainfrom
Br1an67:fix/issue-1269-head-crash
Open

fix: reject unsupported HTTP methods early in session manager#2191
Br1an67 wants to merge 2 commits intomodelcontextprotocol:mainfrom
Br1an67:fix/issue-1269-head-crash

Conversation

@Br1an67
Copy link

@Br1an67 Br1an67 commented Mar 1, 2026

Summary

HEAD and other unsupported HTTP methods (PUT, PATCH, OPTIONS, etc.) sent to the StreamableHTTP endpoint now return 405 Method Not Allowed immediately in StreamableHTTPSessionManager.handle_request(), before any transport or background server task is created.

Fixes #1269

Problem

In stateless mode, a HEAD request to /mcp would:

  1. Create a new StreamableHTTPServerTransport
  2. Spawn a background run_stateless_server task (starting the message router)
  3. Delegate to handle_request()_handle_unsupported_request() → send 405
  4. Call terminate(), closing all streams
  5. The message router, still suspended in async for write_stream_reader, would resume on a closed stream → ClosedResourceError → server crash

Fix

Move the HTTP method check into StreamableHTTPSessionManager.handle_request() so unsupported methods are rejected before any transport is instantiated or background task is spawned. This is a single early-return guard that applies to both stateless and stateful modes.

Changes

  • src/mcp/server/streamable_http_manager.py: Added early method check before transport creation
  • tests/issues/test_1269_head_request_crash.py: Tests for HEAD, PUT, PATCH, OPTIONS in both stateless and stateful modes — verifies 405 response and no ClosedResourceError in logs

HEAD and other unsupported HTTP methods (PUT, PATCH, OPTIONS, etc.)
sent to the StreamableHTTP endpoint now return 405 Method Not Allowed
immediately in StreamableHTTPSessionManager.handle_request(), before
any transport or background server task is created.

Previously, in stateless mode, unsupported methods would flow through
the full transport lifecycle: a new StreamableHTTPServerTransport was
created, a background run_stateless_server task was spawned (starting
the message router), the 405 response was sent, and then terminate()
closed the streams while the message router was still running. This
caused a ClosedResourceError that crashed the server.

Fixes modelcontextprotocol#1269
Remove the unused lifespan context manager (httpx.ASGITransport does
not trigger ASGI lifespan events) and the caplog loop assertion (loop
body never executes when no errors are logged).  The early-return guard
rejects unsupported methods before checking _task_group, so the session
manager does not need to be running.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FastMCP server death on client HEAD calls

1 participant