modelcontextprotocol / modelcontextprotocol/python-sdk

Streamable HTTP server silently drops in-flight request when client reuses a JSON-RPC id

Đang mở
#2,655 3 bình luận 0 reaction 0 người được giao Xem trên GitHub

Chưa có ai nhận issue này.

bug fix proposed P2 ready for work
Ngôn ngữ chính
Python
Star
24.3k
Fork
4k
Merge trung bình
1 ngày 1 giờ
Pull request đã merge (30 ngày)
31

Mô tả

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:

  1. Surfaces the spec violation to the client immediately instead of timing out.
  2. Preserves the in-flight request that arrived first.
  3. 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.

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. 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.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Hướng nghiên cứu

Bắt đầu tại src/mcp/server/streamable_http.py ở các dòng 534-536, sau đó kiểm tra logic router ở các dòng 1004-1033. Theo dõi cách các ID yêu cầu trùng lặp thay thế các mục trong _request_streams và cách các phản hồi được phân phối. Được xem là hoàn tất khi yêu cầu đang thực hiện hiện tại vẫn còn nguyên, yêu cầu trùng lặp nhận JSON-RPC -32600 Invalid Request và hành vi vi phạm được ghi log.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
python
Lĩnh vực
api, backend
Loại issue
Lỗi
Độ khó
3/5
Thời gian dự kiến
1-2 ngày
Mức độ hoạt động
Ít trao đổi
Độ rõ ràng
Đặc tả rõ ràng
Mức phù hợp với người mới
68/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.