Skip to content

Streamable HTTP client retries the standalone GET after a 401, 403, or 405 #3592

Open

Description

@Jmales

On mcp 1.28.1 through 2.2.0, StreamableHTTPTransport.handle_get_stream treats a 401, 403, or 405 on the standalone server-push GET like a dropped stream. raise_for_status() raises, the except Exception increments the attempt, and the loop sleeps DEFAULT_RECONNECTION_DELAY_MS before trying again, up to MAX_RECONNECTION_ATTEMPTS. Those statuses will not change within the session, so the second GET repeats the first.

Since 1.30.0 the same method already returns immediately when a redirect leaves the endpoint's origin, because retrying that GET cannot help. A status rejection is the same kind of case.

Can we either stop retrying when the server rejects that GET, or add a flag on streamable_http_client to skip subscribing to the server-push stream, the way terminate_on_close=False skips the session DELETE.

Activity

  1. added
    v2Affects the v2 line (2.x on main)
    v1Affects the v1.x maintenance line
    on Sep 28, 2026
  2. avinash-git-24 commented on Sep 28, 2026

    @avinash-git-24

    Hey team, handle_get_stream in src/mcp/client/streamable_http.py catches 401/403/405 HTTPStatusErrors in the broad except Exception: retry loop instead of returning early. Happy to open a PR with a fix and test!

  3. junknewera commented on Oct 13, 2026

    @junknewera

    @avinash-git-24 are you still planning to send a PR? If so, I'm happy to step back and review it; otherwise I have a fix with tests ready

  4. avinash-git-24 commented on Oct 13, 2026

    @avinash-git-24

    Hi @junknewera, please feel free to go ahead and open your PR with the tests. Appreciate you checking in!

  5. junknewera commented on Oct 13, 2026

    @junknewera

    Reproduced on main: a 401/403/405 on the standalone GET falls into the except Exception retry loop, so the client waits 1s and sends the same GET again. That can't help: 405 means there's no GET stream, and 401/403 come after the OAuth provider already tried to refresh.

    I have a fix locally (option a): return before raise_for_status() for these statuses, the same way handle_get_stream already returns on a cross-origin redirect. No new API, parametrized test included, full suite plus ruff and pyright pass.

    One question: debug or warning log for 401/403?

    @avinash-git-24 is fine with me taking it (see above), so could a maintainer assign it to me?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingv1Affects the v1.x maintenance linev2Affects the v2 line (2.x on main)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions