fix(mcp): use stored per-user OAuth token on tool calls - #31326
Conversation
Greptile SummaryThis PR fixes a bug where
Confidence Score: 5/5Safe to merge — the change is surgical, well-tested, and only affects per-user OAuth2 MCP servers; a Redis fast path limits the added lookup overhead. The one-line guard removal is straightforward and directly mirrors the already-working No files require special attention.
|
| Filename | Overview |
|---|---|
| litellm/proxy/_experimental/mcp_server/mcp_server_manager.py | Removes the or oauth2_headers short-circuit from _resolve_oauth2_headers_for_tool_call, so the stored per-user OAuth token is always preferred over the caller's Authorization header on OAuth2 per-user-token servers. Falls back to the caller's headers only when no stored token exists. |
| tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py | Adds TestResolveOauth2HeadersForToolCall with three mock-only tests covering the regression case (stored token overrides caller header), passthrough fallback (no stored token), and non-OAuth server early-return. Existing test formatting is also cleaned up (nested with → parenthesised with). |
Reviews (2): Last reviewed commit: "fix(mcp): use stored per-user OAuth toke..." | Re-trigger Greptile
call_tool resolved the per-user OAuth token only when the caller sent no Authorization header. The LiteLLM admission key always arrives in Authorization, so the stored-token lookup was skipped and the proxy key was forwarded upstream; every OAuth MCP tool call returned 401 while tool listing kept working. _resolve_oauth2_headers_for_tool_call now prefers the stored token whenever it exists and falls back to the caller header only for the passthrough cold-start, matching the list_tools path. Adds regression coverage for the override, the fallback, and the non-OAuth guard
36a1adc to
05eba23
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Relevant issues
Relates to #31222 (per-user MCP OAuth credential management).
Companion PR: #31327 — persist per-user OAuth
client_idso tokens auto-refresh.Type
🐛 Bug Fix
Changes
call_toolresolved the per-user OAuth token only when the caller sent noAuthorizationheader. On the standard auth path the caller'sAuthorizationis always the LiteLLM admission key, so the stored-token lookup was skipped and the proxy key was forwarded upstream — every per-user OAuth MCP tool call returned401, whilelist_tools(which already prefers the stored token) kept working._resolve_oauth2_headers_for_tool_callnow prefers the user's stored per-user OAuth token whenever one exists, and falls back to the caller'sAuthorizationonly for the passthrough cold-start (where the bearer genuinely is the upstream token). This mirrors thelist_toolspath.Tests
Adds
TestResolveOauth2HeadersForToolCall:Authorization(the regression)Pre-Submission checklist