Streamable HTTP client hangs on a null-id JSON-RPC error in a 200 JSON response
Maintainer thường phản hồi trong vòng 1 ngày
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 2/5
- Thời gian dự kiến
- 1-3 giờ
- Mức phù hợp với người mới
- 75/100
Hướng nghiên cứu
Bắt đầu trong src/mcp/client/streamable_http.py tại _handle_json_response, sau đó so sánh cách xử lý của nó với _handle_sse_event và _handle_post_request. Thêm bài kiểm thử hồi quy cạnh bài kiểm thử null-id non-2xx hiện có trong tests/client/test_notification_response.py; hoàn tất khi một lỗi JSON-RPC 200 với id null gây ra MCPError thay vì để yêu cầu tiếp tục chờ.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Initial Checks
- I confirm that I'm using the newest release of my line (the latest 2.x, or the latest 1.x if I'm still on v1)
- I confirm that I searched for my issue in https://github.com/modelcontextprotocol/python-sdk/issues before opening this issue
Release line
2.x (current stable)
Description
On current main (2118f14f), when a server answers a request POST with a 200 application/json body holding a JSON-RPC error with "id": null, the streamable HTTP client never resolves the call. session.list_tools() (or any request) waits forever, or until a read timeout if one is set.
JSON-RPC 2.0 allows id: null on an error when the server could not determine the request id. Gateways and non-SDK servers can send one with status 200.
_handle_json_response in src/mcp/client/streamable_http.py forwards the parsed message unchanged. The dispatcher then drops it as a response to an unknown id (jsonrpc_dispatcher.py, "unknown/late request id").
The other two response paths in the same transport already handle this:
- SSE (
_handle_sse_event): replaces a response's id with the original request id. - Non-2xx JSON body (
_handle_post_request): rebuilds the error under the request's id. The comment there says: "The server may have setid: null(request rejected before its id was parsed); use this request's id so correlation works."
So the same error body resolves the call over SSE or with a 4xx status, but hangs with a 200.
Expected: the call raises MCPError with the server's error, the same as the SSE and non-2xx paths.
Proposed fix: in _handle_json_response, when the parsed message is a JSONRPCError, set its id to the POST's request id. A 2xx JSON body answers exactly that POST, so nothing else can be waiting on it. That is a 4-line change plus a regression test next to the existing non-2xx null-id test in tests/client/test_notification_response.py.
I'd like to fix this and have a branch ready: https://github.com/hyeonsang010716/python-sdk/tree/fix/streamable-http-json-null-id
I found and prepared this with AI assistance. I've reviewed the change, checked the repro below fails on main and passes with the fix, and can explain it.
Example Code
import json
import anyio
import httpx2
from starlette.applications import Starlette
from starlette.requests import Request
from starlette.responses import JSONResponse, Response
from starlette.routing import Route
from mcp import ClientSession, MCPError
from mcp.client.streamable_http import streamable_http_client
async def handle_mcp(request: Request) -> Response:
data = json.loads(await request.body())
if data.get("method") == "initialize":
result = {"protocolVersion": "2025-06-18", "capabilities": {}, "serverInfo": {"name": "s", "version": "1"}}
return JSONResponse({"jsonrpc": "2.0", "id": data["id"], "result": result})
if "id" not in data:
return Response(status_code=202)
# e.g. a gateway that fails before it has parsed the request id
return JSONResponse({"jsonrpc": "2.0", "id": None, "error": {"code": -32603, "message": "upstream failed"}})
async def main() -> None:
app = Starlette(routes=[Route("/mcp", handle_mcp, methods=["POST"])])
async with httpx2.AsyncClient(transport=httpx2.ASGITransport(app=app)) as http:
async with streamable_http_client("http://localhost/mcp", http_client=http) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
try:
with anyio.fail_after(3):
await session.list_tools()
except MCPError as exc:
print("raised MCPError:", exc.error.message)
except TimeoutError:
print("list_tools() never returned")
anyio.run(main)
Output on main:
list_tools() never returned
Output with the fix:
raised MCPError: upstream failed
Python & MCP Python SDK
Python 3.14.7
mcp main @ 2118f14f (also present in v2.3.0)
- Ngôn ngữ chính
- Python
- Star
- 24.5k
- Fork
- 4k
- Merge trung bình
- 1 ngày 4 giờ
- Pull request đã merge (30 ngày)
- 33
Chuẩn bị môi trường
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Đọc 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
-
documentation v2
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
modelcontextprotocol/python-sdk#3662 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Audio(data=b"") raises "Either path or data can be provided", while Image(data=b"") worksCó thể đã có người làm @KaiyiQuan đã nhận 1 ngày trước. Đang mởbug v1 v2
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 85/100
modelcontextprotocol/python-sdk#3656 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
enhancement
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 66/100
modelcontextprotocol/python-sdk#3655 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
enhancement
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 86/100
modelcontextprotocol/python-sdk#3654 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
CORSMiddleware on /register and /token forwards any non-preflight OPTIONS request straight to the body-reading handlerCó thể làm lại được Pull request cho issue này đã bị đóng mà không được merge. Đang mởbug v1 v2
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 84/100
modelcontextprotocol/python-sdk#3652 · 3 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của modelcontextprotocol/python-sdk
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 86/100
UKGovernmentBEIS/inspect_ai#5802 ·
Maintainer thường phản hồi trong vòng 2 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 74/100
no-human-ai/no_human#660 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Add a Security Insights v2 fileĐang mởdocumentation good first issue
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
Maintainer thường phản hồi trong vòng 1 ngày
-
documentation need help question
Độ khó 1/5 1-3 giờ Mức phù hợp với người mới 66/100
phonology024/babelscribe#26 ·
-
bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 62/100