modelcontextprotocol / modelcontextprotocol/python-sdk
Streamable HTTP server silently drops in-flight request when client reuses a JSON-RPC id
還沒有人認領這個 Issue。
- 主要語言
- Python
- 星號
- 24.3k
- 分支
- 4k
- 平均合併
- 1 天 1 小時
- 30 天內合併 PR
- 31
描述
Summary
When a client sends two JSON-RPC requests with the same id on the same Streamable HTTP session before the first response is delivered, StreamableHTTPServerTransport overwrites the per-id response stream slot without warning. The first request's caller hangs until its read times out, and the second response is delivered to whichever caller wins the race. The server logs nothing.
The MCP base protocol requires that "the request ID MUST NOT have been previously used by the requestor within the same session," so a duplicate id is a protocol violation by the client. But the server has no way to tell the client (or an operator) that the violation happened, which makes this very hard to diagnose downstream.
Where it happens
In src/mcp/server/streamable_http.py lines 534-536 at v1.27.1:
request_id = str(message.root.id)
self._request_streams[request_id] = anyio.create_memory_object_stream[EventMessage](0)
request_stream_reader = self._request_streams[request_id][1]
The assignment is unconditional. If request_id is already a key in _request_streams, the prior (send, receive) pair is dropped on the floor. The earlier request's writer eventually tries to push a response into a stream nobody is reading, and the earlier request's caller is reading a stream nobody will ever write to.
The router side at lines 1004-1033 uses the same str(id) keying to dispatch responses, so once the slot has been overwritten, response routing is undefined.
Reproduction signature
We hit this through an in-house client that was sending id: 1 for every concurrent tool call on a shared session. Under concurrent load, calls timed out and at least one caller received the wrong tool's response payload.
Proposed Behavior
When a request arrives with an id that already has an in-flight stream registered, the server should reject it with JSON-RPC -32600 Invalid Request and leave the existing in-flight request untouched. This:
- Surfaces the spec violation to the client immediately instead of timing out.
- Preserves the in-flight request that arrived first.
- Gives operators a log line to diagnose against.
A weaker variant (log-only, no error response) would still be a meaningful improvement over silent data loss.
貢獻指南
從這裡開始
- 先讀完整個 Issue,再讀專案的貢獻指南。
- 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
- Fork 儲存庫,在一個分支上完成修改。
- 送出 Pull Request,並在描述裡引用這個 Issue 編號。
研究方向
從 src/mcp/server/streamable_http.py 的第534-536行開始,接著檢查第1004-1033行的路由器邏輯。追蹤重複的請求 ID 如何取代 _request_streams 中的項目,以及回應如何被分派。完成的標準是:現有的進行中請求維持不變,重複的請求收到 JSON-RPC -32600 Invalid Request,且該違規行為會被記錄。
由索引模型根據 Issue 內容生成。
評估
- 技術堆疊
- python
- 領域
- api, backend
- Issue 類型
- 缺陷
- 難度
- 3/5
- 預估耗時
- 1-2 天
- 活躍度
- 冷清
- 描述清晰度
- 描述清楚
- 新手友好度
- 68/100