Skip to content

fix(client): bound empty GET stream retries - #3258

Open
daleselaji-dev wants to merge 3 commits into
modelcontextprotocol:mainfrom
daleselaji-dev:agent/bound-empty-get-stream-retries
Open

fix(client): bound empty GET stream retries#3258
daleselaji-dev wants to merge 3 commits into
modelcontextprotocol:mainfrom
daleselaji-dev:agent/bound-empty-get-stream-retries

Conversation

@daleselaji-dev

Copy link
Copy Markdown

Problem

StreamableHTTPTransport.handle_get_stream() can reconnect forever when a server returns a valid empty SSE stream and closes it cleanly. The loop's MAX_RECONNECTION_ATTEMPTS bound is never reached because every empty close resets the attempt counter.

Root Cause

The transport treated every normal SSE iterator completion as a productive stream, even when no SSE event had been received.

Solution

Track whether the stream delivered an event. Reset the retry counter only after a productive stream; count an empty clean close against the existing retry budget.

Changes

  • Bound empty GET/SSE stream retries at MAX_RECONNECTION_ATTEMPTS.
  • Preserve existing retry reset behavior for streams that deliver events.
  • Add a regression test with an empty 200 text/event-stream response.

Testing

  • Baseline: existing header and exhausted-reconnection tests ? 9 passed.
  • PYTHONPATH=src;. py -3 -m pytest --import-mode=importlib tests/client/test_streamable_http.py -q ? 29 passed.
  • py -3 -m ruff format --check src/mcp/client/streamable_http.py tests/client/test_streamable_http.py ? passed.
  • py -3 -m ruff check src/mcp/client/streamable_http.py tests/client/test_streamable_http.py ? passed.
  • py -3 -m pyright --pythonpath D:\Python310\python.exe src/mcp/client/streamable_http.py tests/client/test_streamable_http.py ? passed.
  • git diff --check ? passed.

Compatibility / Risk

This only changes behavior for a cleanly closed stream that delivered zero events. Productive SSE streams continue to reset the retry budget and reconnect as before. The change prevents unbounded background polling against endpoints that terminate without sending data.

Notes for Reviewer

The regression uses the real transport with httpx2.MockTransport, a valid empty SSE response, and a patched zero-delay sleep; it asserts exactly the configured retry budget is consumed.

Linked Issue

Fixes #3257

@daleselaji-dev
daleselaji-dev marked this pull request as ready for review August 6, 2026 02:49

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 2 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread tests/client/test_streamable_http.py Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

handle_get_stream reconnects indefinitely when the server ends the GET stream normally (attempt = 0 defeats MAX_RECONNECTION_ATTEMPTS)

1 participant