fix(mcp): derive streamable HTTP retry backoff from backoffs taken - #4174
Merged
Conversation
MCPServerStreamableHttp.call_tool used retries_used, a retry-budget counter, as its backoff exponent. That counter is deliberately frozen while max_retry_attempts is -1, and one isolated-session retry charges it twice, so the delay was constant in the first case and grew 4x per sleep in the second instead of following the configured schedule. Count backoffs in a dedicated local and use that for the exponent. Budget accounting, attempt counts, and isolated-session retry eligibility are unchanged.
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
MCPServerStreamableHttp.call_toolreplaces the shared retry loop with its own, and takes the backoff exponent fromretries_used— a retry budget counter rather than a count of backoffs already taken. The two disagree in both directions:retries_usedis only advanced at the top of the loop whenmax_retry_attempts != -1. So withmax_retry_attempts = -1("retry indefinitely") the genericexcept Exceptionpath evaluatesretry_backoff_seconds_base * 2**0on every pass and the delay never grows — an unreachable server is retried at a flat interval forever.retries_usedtwice, once at the top of the loop and once in the_IsolatedSessionRetryFailedhandler. With a finite budget the delay then quadruples per sleep:1s, 4s, 16sinstead of1s, 2s, 4s..agents/references/local-mcp-server-lifecycle.mdstates that genericlist_tools()/call_tool()retries "use the configured attempt count and backoff", and_run_with_retries— the schedule every other retry path in this file follows — isbase * 2 ** (attempts - 1)over actual attempts. This change makes the streamable HTTP override agree with it by counting backoffs in a dedicated local and using that as the exponent. Budget accounting, attempt counts, and isolated-session retry eligibility are unchanged.Test plan
Two tests added to
tests/mcp/test_client_session_retries.py, both patchingasyncio.sleepto record the delay sequence:test_streamable_http_backoff_grows_with_unlimited_retries—max_retry_attempts = -1, expects[1.0, 2.0, 4.0]. On unmodifiedmainit records[1.0, 1.0, 1.0].test_streamable_http_backoff_matches_generic_schedule_on_isolated_retry— finite budget with an isolated-session retry, expects[1.0, 2.0, 4.0]. On unmodifiedmainit records[1.0, 4.0, 16.0].Both were confirmed failing against unmodified
mainbefore the fix and passing after it..agents/skills/code-change-verification/scripts/run.sh:make format,make lintandmake typecheckpass.make testsreports a single failure,tests/test_run_step_execution.py::test_multiple_tool_calls_raise_late_fatal_sibling_exception_after_cancellation[20], which is pre-existing and unrelated to this change: it reproduces identically on unmodifiedmainat 19e364c under the samepytest -n auto --dist worksteal -m "not serial"invocation, and passes when that test is run on its own. I have left the "all verification steps pass" box unchecked for that reason.Issue number
N/A
Checks
.agents/skills/code-change-verification/scripts/run.sh/reviewbefore submitting this PR