From 5ca5e517e836a7d12af2e641a9b6af9f5b0dd264 Mon Sep 17 00:00:00 2001 From: Tin Chi Lo Date: Thu, 25 Jun 2026 15:05:32 -0700 Subject: [PATCH] feat(mcp): cut the tools/list connection over to v2 for authorization_code servers The listing connection's per-user OAuth header is no longer built by v1 for migrated servers; the v2 resolver drives it at connect time, ending the double-resolution where v1 built the token into extra_headers and the v2 graft then deferred to it. Safe because the preemptive 401 (in the streamable-http and SSE handlers) already challenges a missing token before the listing connection runs, so the connection is only reached with a token present. Non-migrated oauth2 (delegate) and the rest still build their header on v1. With this, resolve_credentials' result is honored on every authorization_code upstream path: tool calls and listing. --- .../proxy/_experimental/mcp_server/server.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/server.py b/litellm/proxy/_experimental/mcp_server/server.py index 2ce9054d4b9..61376067f23 100644 --- a/litellm/proxy/_experimental/mcp_server/server.py +++ b/litellm/proxy/_experimental/mcp_server/server.py @@ -1684,8 +1684,17 @@ if MCP_AVAILABLE: # Prefer server-stored per-user OAuth when configured, so a stale # Authorization header from the MCP client cannot override Redis/DB # (same issue as call_tool in mcp_server_manager: VS Code caches tokens). + from litellm.proxy._experimental.mcp_server.outbound_credentials.adapter import ( # noqa: PLC0415 + to_server_spec, + ) + + # A server migrated to the v2 resolver gets its token from the resolver at connect + # time; building it here would double-resolve and be shadowed by the v2 graft. The + # preemptive 401 already challenged a missing token, so one exists for the connect. + migrated_to_v2 = to_server_spec(server) is not None if ( - server.auth_type == MCPAuth.oauth2 + not migrated_to_v2 + and server.auth_type == MCPAuth.oauth2 and getattr(server, "needs_user_oauth_token", False) and user_api_key_auth is not None ): @@ -1698,7 +1707,11 @@ if MCP_AVAILABLE: extra_headers = db_headers # If still no OAuth2 token, fall back to pre-fetched creds (non-stale-client path) - elif extra_headers is None and server.auth_type == MCPAuth.oauth2: + elif ( + not migrated_to_v2 + and extra_headers is None + and server.auth_type == MCPAuth.oauth2 + ): extra_headers = await _get_user_oauth_extra_headers_from_db( server, user_api_key_auth,