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
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 3/5
- Thời gian dự kiến
- 1-2 ngày
- Mức phù hợp với người mới
- 58/100
- Loại issue
- Lỗi
- Độ rõ ràng
- Khá rõ ràng
- Mức độ hoạt động
- Ít trao đổi
- Công nghệ
- python
- Lĩnh vực
- api, backend, networking
Hướng nghiên cứu
Bắt đầu bằng cách tái hiện hai yêu cầu curl được mô tả trong issue, sau đó theo dõi các điểm vào của máy chủ HTTP streamable _handle_get_request, _check_accept_headers và _validate_session. Xác minh hành vi GET trước phiên và các trường hợp của tiêu đề Accept, rồi thêm hoặc cập nhật độ bao phủ để một GET không được hỗ trợ trả về 405 cùng tiêu đề Allow, đồng thời vẫn giữ nguyên cách xử lý SSE hợp lệ.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
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-streamin 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_headersstill 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.
- Ngôn ngữ chính
- Python
- Star
- 24.3k
- Fork
- 4k
- Merge trung bình
- 1 ngày 11 giờ
- Pull request đã merge (30 ngày)
- 30
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của modelcontextprotocol/python-sdk
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
modelcontextprotocol/python-sdk#3566 ·
-
Streamable HTTP client logs a WARNING for valid 202 Accepted on session termination (DELETE) Đang mởv1 v2
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 85/100
modelcontextprotocol/python-sdk#3546 · 5 bình luận ·
-
v1 v2
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
modelcontextprotocol/python-sdk#3545 · 1 bình luận ·
-
v1 v2
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 91/100
modelcontextprotocol/python-sdk#3508 · 2 bình luận ·
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 64/100
modelcontextprotocol/python-sdk#3504 ·
Tất cả issue của modelcontextprotocol/python-sdk
Issue tương tự
-
enhancement
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 70/100
canonical/paas-charm#368 · 1 bình luận ·
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
-
tech debt
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
-
addition to tracking list Đang mở
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 90/100
StevenBlack/hosts#3256 ·
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 90/100
qualcomm/qai-appbuilder#275 ·