Repository navigation
Conversation
Contributor
|
@bolinfest is on triage rotation this week and is the reviewer for this one. Labels come from the PR labeler. Please review the change. Automated triage. Labels are a first pass and the assigned maintainer has the final say. |
…treamable HTTP client
The default reqwest client used `Policy::none()`, so any redirect from an
MCP server failed with `UnexpectedServerResponse("HTTP 307: ...")`.
It now follows 307 and 308 when the target has the same origin as the
original request, up to 10 hops. Those keep the method and body, so the
JSON-RPC message and the session, auth and custom headers arrive intact.
301, 302 and 303 are still returned to the caller, because following them
turns the POST into a bodyless GET. Cross-origin redirects are still
returned too, which keeps the header-leak guard.
Refs modelcontextprotocol#936
Reported-by: Mo (@trickyfalcon)
Document on `from_uri` and `from_config` which redirects the default client follows, that a 308 does not change the stored URI, and that a client passed to `with_client` keeps its own redirect policy.
gocamille
force-pushed
the
fix/streamable-http-same-origin-redirects
branch
from
October 16, 2026 21:46
c818296 to
a821a5e
Compare
Contributor
Author
|
Dale signed off on the approach in #1203 (#1203 (comment)). I've added the redirect note he asked for to the |
gocamille
marked this pull request as ready for review
October 16, 2026 21:51
This branch has not been deployed
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.
Fixes #1203. Reported by Mo (@trickyfalcon).
Motivation and Context
The default client built by
StreamableHttpClientTransport::default_http_clientnow uses a custom reqwest redirect policy (same_origin_redirect_policy) instead ofPolicy::none():MAX_REDIRECTS(10) hops. reqwest keeps the method, the.json()body, and the session, auth and custom headers on these.UnexpectedServerResponse("HTTP 30x ...").StreamableHttpError::Client,is_redirect()), so a redirect loop can't run forever.Only the default client changes. A caller-supplied
reqwest::Clientkeeps its own policy. Thefrom_uriandfrom_configdocs now describe this redirect handling. GET and DELETE use the same rule; following 301/302 for a GET would be safe, but one rule for every method seemed simpler. Happy to loosen that.How Has This Been Tested?
Tests in
streamable_http_client.rs:default_http_client_follows_same_origin_body_preserving_redirect(307, 308): the target gets a POST with the same JSON-RPC body,Mcp-Session-Id,Authorization: Bearerand the custom header. Fails onmain.default_http_client_does_not_follow_method_rewriting_redirect(301, 302, 303): returns an error and the target is never hit. Passes onmaintoo; it's there so a broader policy can't slip in.default_http_client_stops_redirect_loop: a self-redirecting 307 ends in a redirect error after 11 requests.default_http_client_does_not_leak_custom_headers_to_redirect_target(existing) still passes; it's a cross-origin 307.Validation:
cargo test -p rmcp --all-features: passed.cargo clippy --all-targets --all-features -- -D warningsandcargo +nightly fmt --all -- --check: clean.Breaking Changes
No API changes. The default client now follows same-origin 307/308 redirects instead of returning
UnexpectedServerResponse.Types of changes
Checklist
Additional context
A 308 is followed on every request; the stored URI doesn't change. Updating it is tracked in #1373.