fix(mcp): resolve OAuth2 'Capabilities: none' bug for upstream MCP servers#20602
Conversation
…rvers - process_mcp_request() now falls back to OAuth2 passthrough when Authorization header contains a non-LiteLLM token (catches HTTPException and ProxyException 401/403) - MCPClient._get_auth_headers() adds missing MCPAuth.oauth2 case
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile OverviewGreptile SummaryThis PR fixes the "Capabilities: none" bug where OAuth2 tokens from upstream MCP providers (e.g., Atlassian) were incorrectly rejected as invalid LiteLLM API keys, preventing tool discovery. Root Causes FixedBug 1 (Primary): Bug 2: Key Changes
TestingAll scenarios covered: OAuth2 passthrough (HTTPException & ProxyException), explicit LiteLLM key with OAuth2, backward compatibility, and non-auth exceptions still propagate correctly. Confidence Score: 5/5
|
| Filename | Overview |
|---|---|
| litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py | Added OAuth2 passthrough logic with proper exception handling for both HTTPException and ProxyException (401/403), preserving backward compatibility for LiteLLM keys in Authorization header |
| litellm/experimental_mcp_client/client.py | Added missing MCPAuth.oauth2 case to _get_auth_headers() method, correctly formatting Bearer token for OAuth2 authentication |
| tests/test_litellm/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py | Added comprehensive test suite (6 tests) covering OAuth2 fallback scenarios, backward compatibility, and exception handling for both HTTPException and ProxyException |
| tests/mcp_tests/test_mcp_client_unit.py | Added unit tests for OAuth2 auth header generation and extra_headers override behavior in MCPClient |
Sequence Diagram
sequenceDiagram
participant C as Client
participant G as Gateway
participant A as Auth
participant V as Validator
participant U as Upstream
C->>G: MCP Request
G->>A: Authenticate
A->>V: Verify
V-->>A: Rejected
A->>A: Passthrough
A-->>G: Proceed
G->>U: Forward
U-->>C: Tools
Relevant issues
Related to MCP Gateway showing "Capabilities: none" after successful OAuth2 authentication with upstream MCP servers (e.g. Atlassian).
Pre-Submission checklist
tests/litellm/directory, Adding at least 1 test is a hard requirement - see detailsmake test-unitCI (LiteLLM team)
Branch creation CI run
Link:
CI run for the last commit
Link:
Merge / cherry-pick CI run
Links:
Type
🐛 Bug Fix
Changes
Root Cause
Two bugs caused the MCP Gateway to return zero tools ("Capabilities: none") after a successful OAuth2 flow with upstream MCP providers like Atlassian:
Bug 1 (primary): In
process_mcp_request(), when nox-litellm-api-keyheader is present, the code falls back to theAuthorizationheader to find a LiteLLM API key. After OAuth2 completes, the client sendsAuthorization: Bearer <atlassian_token>— this Atlassian token gets passed touser_api_key_auth()which rejects it (not a valid LiteLLM key), killing the entire request.Bug 2:
MCPClient._get_auth_headers()had no case forMCPAuth.oauth2, so OAuth2 tokens stored inself._mcp_auth_valuewere silently dropped and never sent to the upstream server.Fix
litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py:has_explicit_litellm_keycheck to distinguish between explicit LiteLLM keys and OAuth2 tokensx-litellm-api-keyis present butAuthorizationheader exists: try LiteLLM auth first, on 401/403 failure fall back to permissiveUserAPIKeyAuth()(OAuth2 passthrough)HTTPExceptionandProxyException(which is whatuser_api_key_authactually raises in production)Authorizationheader still work (try succeeds)litellm/experimental_mcp_client/client.py:MCPAuth.oauth2case to_get_auth_headers()→ generatesAuthorization: Bearer <token>Tests Added
tests/test_litellm/proxy/_experimental/mcp_server/auth/test_user_api_key_auth_mcp.py— 6 new tests inTestMCPOAuth2AuthFlow:tests/mcp_tests/test_mcp_client_unit.py— OAuth2 auth header generation + extra_headers overrideE2E Verification
Before the fix — OAuth2 token in
Authorizationheader causes 401/500:After the fix — OAuth2 token passes through, MCP server initializes correctly: