modelcontextprotocol / modelcontextprotocol/python-sdk

Streamable HTTP clean EOF reconnects can exceed the request retry budget

Đang mở Phù hợp với người mới
#3,307 5 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 needs confirmation P2 v1 v2
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ả

Description

The streamable HTTP client can exceed its per-request SSE reconnection budget when each reconnect opens successfully, emits only an id-bearing priming event, and then reaches EOF without a JSON-RPC response.

In that case _handle_reconnection() currently recurses with attempt=0 after the clean EOF path. The exception path increments the counter, but the normal EOF-without-response path resets it, so a no-timeout request such as subscriptions/listen can keep reconnecting instead of resolving the waiter with CONNECTION_CLOSED after MAX_RECONNECTION_ATTEMPTS.

Reproduction

Drive StreamableHTTPTransport._handle_reconnection() with a mock HTTP transport that returns these per-request SSE responses:

  1. Reconnect 1: id: evt-1 with empty data, then EOF.
  2. Reconnect 2: id: evt-2 with empty data, then EOF.
  3. Reconnect 3: a JSON-RPC success response.

Starting from Last-Event-ID: evt-0 and retry_interval_ms=0, current main makes the third HTTP request and delivers the success response. I expected the client to stop after the two configured reconnect attempts, emit a JSONRPCError for the original request with CONNECTION_CLOSED, and only send Last-Event-ID: evt-0 and Last-Event-ID: evt-1.

Expected Behavior

Each per-request reconnect that reaches EOF without delivering a JSON-RPC response should consume the reconnect budget. That keeps request-scoped SSE drops consistent whether they end by transport exception or by clean EOF, and prevents no-timeout callers from staying parked forever when a server repeatedly closes resumable streams without producing the response.

Local Verification

I have a local regression test that fails on current main because the third reconnect response is accepted, then passes when the clean EOF path recurses with attempt + 1.

Commands run locally:

  • uv run --frozen pytest tests/client/test_streamable_http.py::test_empty_resumable_sse_reconnects_count_toward_the_request_budget -q
  • uv run --frozen pytest tests/client/test_streamable_http.py -q
  • uv run --frozen ruff check src/mcp/client/streamable_http.py tests/client/test_streamable_http.py
  • uv run --frozen ruff format --check src/mcp/client/streamable_http.py tests/client/test_streamable_http.py
  • uv run --frozen pyright src/mcp/client/streamable_http.py tests/client/test_streamable_http.py
  • UV_FROZEN=1 uv run --frozen strict-no-cover

Disclosure: I used AI assistance to help prepare this report and a local patch; I reviewed the reproduction, root cause, and test results.

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 với StreamableHTTPTransport._handle_reconnection() trong src/mcp/client/streamable_http.py, sau đó đọc test_empty_resumable_sse_reconnects_count_toward_the_request_budget trong tests/client/test_streamable_http.py. Trước tiên, hãy chạy bài kiểm thử hồi quy đó và toàn bộ tệp kiểm thử streamable HTTP. Hoàn tất khi các EOF sạch không có phản hồi JSON-RPC tiêu tốn ngân sách kết nối lại của request và resolve waiter với CONNECTION_CLOSED.

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, networking
Loại issue
Lỗi
Độ khó
2/5
Thời gian dự kiến
1-3 giờ
Mức độ hoạt động
Sôi nổi
Độ rõ ràng
Đặc tả rõ ràng
Mức phù hợp với người mới
88/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.