-
Notifications
You must be signed in to change notification settings - Fork 2k
fix(client): scope transport headers to MCP requests and handle redirects explicitly #2465
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
felixweinberger
wants to merge
5
commits into
main
Choose a base branch
from
fweinberger/oauth-header-isolation
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,305
−80
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
78c714c
Do not apply transport request headers to OAuth requests
felixweinberger 5c8b73e
Treat redirected token and registration responses as terminal
felixweinberger 8ac6652
Handle data-plane redirects explicitly in the client transports
felixweinberger 359728a
Treat redirected cross-app token responses as terminal
felixweinberger 75b354b
Route transport requests through a single internal request door
felixweinberger File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| '@modelcontextprotocol/client': patch | ||
| --- | ||
|
|
||
| Token and client registration requests no longer follow HTTP redirects — including the cross-app access token exchanges. Token responses are terminal (RFC 6749 §5), so a 3xx answer from a token or registration endpoint now rejects with an error instead of being re-sent to the redirect target. |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| '@modelcontextprotocol/client': patch | ||
| --- | ||
|
|
||
| Transport `requestInit` headers now apply only to MCP requests, not to the OAuth requests issued by the transport's authorization flow (protected-resource metadata, authorization-server metadata, token, and client registration requests) — those may target a different origin than the MCP server, so connection-level headers do not carry over. A new `oauthRequestInit` transport option configures those requests explicitly. |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| --- | ||
| '@modelcontextprotocol/client': patch | ||
| '@modelcontextprotocol/core-internal': patch | ||
| --- | ||
|
|
||
| Handle redirect (3xx) responses on the client transports' MCP requests explicitly instead of delegating them to the `fetch` implementation. `StreamableHTTPClientTransport` and `SSEClientTransport` now issue their data-plane requests (`POST` message sends, the `GET` that opens or resumes an SSE stream, the session-terminating `DELETE`) with `redirect: 'manual'` and apply RFC 9110 redirect semantics themselves: `GET` redirects are followed for up to 3 hops — a same-origin `Location` target is requested with the original headers, while a hop that leaves the endpoint's origin is requested with only the headers that describe the request itself (`Accept`, `MCP-Protocol-Version`, and — on `StreamableHTTPClientTransport` stream resumption — `Last-Event-ID`); headers configured for the connection (`requestInit` headers, the auth provider's `Authorization`, the server-issued `Mcp-Session-Id`) are not applied across origins. `POST` and `DELETE` requests are never re-sent to a `Location` target: a redirect answer to those surfaces as an `SdkHttpError` with the new code `SdkErrorCode.ClientHttpRedirectNotFollowed` (MCP endpoints that answer `POST` with a redirect are not followed — point the transport at the new endpoint instead), and a redirect response can no longer supply the `mcp-session-id` the transport adopts. The new transport option `redirectPolicy: 'manual' | 'follow'` (default `'manual'`) is the escape hatch: `'follow'` restores the previous behavior — no `redirect` field is set, so `requestInit.redirect` or the platform default applies — for deployments behind redirecting infrastructure that requires the configured headers on the redirect target, and for browser runtimes: there `redirect: 'manual'` yields an opaque redirect (Fetch `opaqueredirect`: status 0, no readable `Location` header) whose target the transport cannot observe, so under the default `'manual'` policy any redirect answer — initial or on a followed hop — fails with `ClientHttpRedirectNotFollowed` and a message naming the remedies (set `redirectPolicy: 'follow'`, or serve the MCP endpoint without redirects) instead of an unexplained status-0 failure. See the "HTTP & headers" section of `docs/migration/upgrade-to-v2.md`. |
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
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
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
Oops, something went wrong.
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 The new '#### Redirect (3xx) responses on MCP requests' subsection is inserted mid-way through the 'HTTP & headers' section, so the pre-existing hostHeaderValidation/localhostHostValidation and Content-Type/415 paragraphs that follow it (before '### Errors') are now structurally nested under the redirect subsection in any rendered TOC/outline, despite being unrelated to redirects. Move the new subsection to the end of the 'HTTP & headers' section (after those paragraphs), or give the trailing paragraphs their own heading.
Extended reasoning...
What the issue is
The migration guide's
### HTTP & headerssection (line 782 ofdocs/migration/upgrade-to-v2.md) previously contained only flat paragraphs — no####subsections. This PR inserts the new#### Redirect (3xx) responses on MCP requestsheading at line 814, in the middle of that section. In Markdown's implicit outline model, a section extends until the next heading of the same or higher level — and the next heading after line 814 is### Errorsat line 880. Everything in between is therefore now a structural child of the redirect subsection.What gets misfiled
Two pre-existing, unrelated blocks fall inside that span:
hostHeaderValidation()/localhostHostValidation()moved-to-@modelcontextprotocol/expressparagraph (line 862).Neither has anything to do with redirects, but any hierarchical rendering — a generated table of contents, a docs-site outline/sidebar, section-scoped anchors or search extraction — now files them under "Redirect (3xx) responses on MCP requests".
Step-by-step proof
### HTTP & headers(782) →### Errors; the host-header and Content-Type paragraphs were direct children of### HTTP & headers.#### Redirect (3xx) responses on MCP requestsat line 814, above those paragraphs.HTTP & headers→Redirect (3xx) responses on MCP requests→ (host-header paragraph, Content-Type paragraph) →Errors.Why nothing prevents it
Markdown lint rules and the docs pipeline don't enforce topical grouping of heading spans, so the misfiled structure renders without any error.
How to fix
Either move the new
#### Redirect (3xx) responses on MCP requestsblock to the end of theHTTP & headerssection (immediately before### Errors, after line 878), or give the two trailing paragraphs their own####heading (e.g.#### Host validation & media types) so they are no longer implicit children of the redirect subsection.Impact
Cosmetic/structural only — no behavior, code, or example is affected. This should not block the merge; it's a one-move fix.