Skip to content

fix: preserve per-user OAuth Authorization over MCP signer JWT on tools/calls - #32251

Closed
RajanChavada wants to merge 1 commit into
BerriAI:litellm_internal_stagingfrom
RajanChavada:fix/mcp-jwt-preserve-oauth-authorization
Closed

RajanChavada wants to merge 1 commit into
BerriAI:litellm_internal_stagingfrom
RajanChavada:fix/mcp-jwt-preserve-oauth-authorization

Conversation

@RajanChavada

Copy link
Copy Markdown

Relevant issues

Fixes #31977

Linear ticket

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have added meaningful tests
  • My PR passes all CI/CD checks (e.g., lint, format, unit tests)
  • [ x My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

Delays in PR merge?

If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).

Screenshots / Proof of Fix

pytest tests/.../test_mcp_hook_extra_headers.py → 29 passed. Added a
regression test on the v1 delegate_auth_to_upstream oauth2 path (the scenario
#31977 reports) asserting the resolved OAuth Authorization survives the signer
merge. Mutation-checked: reverting the fix re-fails the test.

Screenshot of error in the code:

image

Screenshot after the fix

image

Type

🐛 Bug Fix

Changes

On the MCP tools/call path, the signer's JWT unconditionally overwrote the
Authorization header via extra_headers.update(hook_extra_headers), even when a
per-user OAuth token had already been resolved into extra_headers. The
tools/list path already guards against this; this applies the same guard to
tools/call — the hook's non-Authorization headers still merge in.

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@greptile-apps

greptile-apps Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a header-priority bug on the MCP tools/call path where the guardrail signer's JWT unconditionally overwrote a per-user OAuth token (or any previously resolved Authorization) via extra_headers.update(hook_extra_headers). The fix replaces the blanket update with a case-insensitive per-header loop that skips the hook's Authorization when one already exists in extra_headers, matching the guard that was already in place on the tools/list path.

  • Core fix (mcp_server_manager.py, ~line 3352): replaces extra_headers.update(hook_extra_headers) with a loop that preserves any pre-existing Authorization header while still merging all other hook headers.
  • Tests (test_mcp_hook_extra_headers.py): two tests corrected to assert the new precedence order; a new regression test covers the delegate_auth_to_upstream / v1-path scenario from [Bug]: MCP JWT signer overwrites OAuth Authorization header during tools/call #31977, mutation-checked by the author.

Confidence Score: 5/5

Safe to merge — the change is narrowly scoped to the hook-header merge step and cannot affect callers that have no existing Authorization in extra_headers.

The fix correctly mirrors the tools/list guard onto the tools/call path, uses case-insensitive header comparison, preserves all non-Authorization hook headers, and is covered by a mutation-verified regression test. The only gap is a missing debug log when the JWT is silently skipped — a minor observability concern with no correctness impact.

No files require special attention; both changed files are straightforward.

Important Files Changed

Filename Overview
litellm/proxy/_experimental/mcp_server/mcp_server_manager.py Replaces the blanket extra_headers.update(hook_extra_headers) with a case-insensitive per-header loop that skips the hook's Authorization when one is already present in extra_headers, matching the existing tools/list guard logic.
tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_hook_extra_headers.py Two existing tests updated to assert the correct fixed priority (existing Authorization wins); a new regression test for the delegate_auth_to_upstream / v1-path scenario from issue #31977 is added. All tests use mocks only — no real network calls.

Reviews (1): Last reviewed commit: "fix: preserve per-user OAuth Authorizati..." | Re-trigger Greptile

Comment on lines +3363 to +3365
if isinstance(header, str) and header.lower() == "authorization":
if has_existing_authorization:
continue

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 When the hook's Authorization is silently skipped because an existing one was already in extra_headers, nothing is logged. A debug-level message here would help operators understand why the signer JWT was not forwarded, matching the level of observability that the old warning provided for the inverse scenario.

Suggested change
if isinstance(header, str) and header.lower() == "authorization":
if has_existing_authorization:
continue
if isinstance(header, str) and header.lower() == "authorization":
if has_existing_authorization:
verbose_logger.debug(
"MCPServerManager: hook_extra_headers 'Authorization' skipped — "
"an existing Authorization header (per-user OAuth or static) takes precedence."
)
continue

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@codecov

codecov Bot commented Jul 6, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.71429% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...oxy/_experimental/mcp_server/mcp_server_manager.py 85.71% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@yuneng-berri
yuneng-berri deleted the branch BerriAI:litellm_internal_staging September 13, 2026 04:28
@yuneng-berri yuneng-berri reopened this Sep 13, 2026
@github-actions

Copy link
Copy Markdown
Contributor

#31977 was fixed by #38555 on main, so this pull request is closed. Reopen it if something was missed.

@github-actions github-actions Bot closed this Sep 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: MCP JWT signer overwrites OAuth Authorization header during tools/call

3 participants