modelcontextprotocol / modelcontextprotocol/python-sdk

Streamable HTTP server never returns the spec-mandated 405 for GET requests it won't serve as SSE (406/400 instead) — breaks client SSE-probe fallback

Offen
#3,102 5 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

bug needs confirmation P2 v1 v2
Vorherrschende Sprache
Python
Sterne
24.3k
Forks
4k
Ø Merge
1 T. 1 Std.
Gemergte PRs (30 T.)
31

Beschreibung

Summary

The streamable-HTTP server transport never answers a GET it won't serve with 405 Method Not Allowed, even though the spec makes 405 the required signal:

The server MUST either return Content-Type: text/event-stream in response to this HTTP GET, or else return HTTP 405 Method Not Allowed, indicating that the server does not offer an SSE stream at this endpoint.
Streamable HTTP § Listening for Messages from the Server

Instead, a GET that can't be served yields:

Pre-session GET /mcp with… Server response
Accept: */*, Accept: application/json, or no Accept 406 Not Acceptable (_handle_get_request_check_accept_headers, which does literal prefix matching so */* is not honored)
Accept: text/event-stream (stateful server, no mcp-session-id) 400 Bad Request: Missing session ID (_validate_session)

So in stateful mode there is no request shape for which a pre-session GET returns 405. (405 is used elsewhere: DELETE without a session and unsupported methods.)

Why it matters — real interop break

Several client transports probe with exactly this GET before initialize to ask "do you offer a standalone SSE stream?", and treat only 405 as the graceful "no SSE — fall through to POST" signal (e.g. the TypeScript SDK's _startOrAuthSse explicitly special-cases 405). Any other status is a transport error, so against a stock python-SDK server the connection aborts before initialize is ever sent.

We hit this in production between two independently-built agents: the client authenticated successfully, then its GET probe got 406 (its relay sent Accept: */*), the transport threw, and the handshake never reached initialize — surfacing as "server doesn't respond to MCP protocol" while the server looked perfectly healthy to every python-SDK client (which only GETs after initialize, with a session id). We've deployed a workaround (a small ASGI shim returning 405 + Allow: POST for session-less GETs on the MCP path), but every stock python-SDK deployment presumably reproduces this.

Repro (mcp 1.28.1, stateful StreamableHTTP server)

curl -i -X GET http://localhost:8000/mcp -H 'Accept: */*'                 # → 406, expected 405
curl -i -X GET http://localhost:8000/mcp -H 'Accept: text/event-stream'  # → 400, expected 405 (no session yet)

Suggested behavior

For a GET the server cannot serve as an SSE stream, respond 405 with an Allow header per the spec quote above — at minimum for the pre-session case (no mcp-session-id), where returning 400 makes the spec'd client probe impossible to satisfy. Separately (or as part of this), _check_accept_headers honoring */* / text/* per RFC 9110 §12.5.1 would remove the 406 arm for wildcard clients.

Related

  • #2349 covers the POST-side strict dual-Accept requirement; this issue is about the GET/405 contract, which is distinct.
  • #1641 raised wildcard-Accept non-compliance and is closed, but on 1.28.1 _check_accept_headers still does literal prefix matching, and the GET path 406s wildcard clients.

Happy to provide full header traces or a PR if the maintainers agree on the intended shape.

Beitragsleitfaden

Beitragsleitfaden öffnen

Erste Schritte

  1. Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
  3. Forke das Repository und arbeite in einem Branch.
  4. Öffne einen Pull Request, der die Issue-Nummer nennt.

Rechercherichtung

Beginne damit, die beiden im Issue beschriebenen curl-Anfragen zu reproduzieren, und verfolge dann die Einstiegspunkte des streamable HTTP-Servers _handle_get_request, _check_accept_headers und _validate_session. Überprüfe das GET-Verhalten vor der Sitzung und die Fälle für den Accept-Header. Füge anschließend Tests hinzu oder aktualisiere die Testabdeckung so, dass ein nicht unterstützter GET 405 mit einem Allow-Header zurückgibt, während die gültige SSE-Verarbeitung erhalten bleibt.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
python
Bereich
api, backend, networking
Issue-Typ
Bug
Schwierigkeit
3/5
Geschätzter Aufwand
1-2 Tage
Aktivitätsstatus
Ruhig
Klarheit
Größtenteils klar
Anfängerfreundlichkeit
58/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.