fix(client): stop retrying GET stream when server answers 405 - #3420
Closed
lccstc wants to merge 2 commits into
Closed
fix(client): stop retrying GET stream when server answers 405#3420lccstc wants to merge 2 commits into
lccstc wants to merge 2 commits into
Conversation
Per the Streamable HTTP spec, a 405 response to GET is the server's definitive signal that it does not offer a server-initiated SSE stream. The transport currently treats it like any other disconnect and retries (MAX_RECONNECTION_ATTEMPTS times per session), producing pointless requests, reconnect backoff sleeps, and "GET stream disconnected, reconnecting in ...ms" log noise on every session creation. Treat HTTP 405 as terminal: log once and disable the GET stream for the session. POST-based request/response flows are unaffected.
- 405 on GET must stop the reconnect loop immediately (one attempt, no backoff sleep) since the server has definitively signaled that it does not offer a server-initiated SSE stream. - Other HTTP errors keep the existing bounded-retry behavior.
Contributor
|
This PR has been closed automatically. This repo only keeps pull requests open when they come from a maintainer, or from a contributor a maintainer has assigned to the linked issue, and this PR doesn't link an open issue yet.
You're welcome to keep pushing commits here (just avoid force-pushing, since GitHub can't reopen a rewritten branch), but that on its own won't get the PR reviewed or the issue assigned, and realistically most auto-closed PRs stay closed. There's no need to open a new PR either way. CONTRIBUTING.md has the full reasoning, but in short:
Maintainers: reopen, remove |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
handle_get_streamtreats an HTTP 405 response to the GET stream like any other transport error and retries. Per the Streamable HTTP spec, a405 Method Not Allowedon GET is the server's definitive signal that it does not offer a server-initiated SSE stream — the client should accept that and proceed without the GET stream, not retry.Real-world impact
Observed against a production MCP endpoint that does not implement GET SSE (GitHub Copilot's
https://api.githubcopilot.com/mcp/, which is used by several MCP clients, including self-hosted agent platforms such as TencentCloud/Octop). Every new streamable-HTTP session there produces:That is 2 pointless GET requests + 2 retry backoff sleeps per session, plus misleading "disconnected" log noise suggesting a network problem when the server is behaving exactly per spec. In SDK versions with an unbounded reconnect loop this manifests as an endless ~1s reconnect cycle per session.
Fix
In
handle_get_stream, catchhttpx2.HTTPStatusErrorfirst: if the status is 405, log once ("GET stream disabled: server does not support server-initiated SSE (405)") and return — the session keeps working normally over POST, which is the entire point of the Streamable HTTP transport's optional GET stream. All other errors keep the existing bounded-retry behavior unchanged.Testing
tests/client/test_streamable_http_405.py:test_get_stream_405_disables_retry— a 405 produces exactly one GET attempt and no backoff sleep.test_get_stream_other_http_errors_still_retry— a 500 still consumes the bounded retry budget (MAX_RECONNECTION_ATTEMPTS), proving no behavior change for non-405 errors.