From 12880bf760a13d7dc4a53cd42978e76012b4e3bb Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Sat, 7 Mar 2026 09:26:06 -0800 Subject: [PATCH] fix(mcp-oauth2): apply _extract_access_token in _check_byok_credential _check_byok_credential wrote the raw credential from DB directly to _byok_cred_cache without calling _extract_access_token. When the OAuth2 callback stores a JSON blob {"access_token": "...", "refresh_token": "..."}, a subsequent call that went through _check_byok_credential first (rather than _get_byok_credential) would populate the cache with the blob. The next _get_byok_credential call would return the blob as the Bearer token, causing silent HTTP 401s on every upstream API call. Fix: extract the access_token before writing to cache, consistent with how _get_byok_credential already handles it. --- litellm/proxy/_experimental/mcp_server/server.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/_experimental/mcp_server/server.py b/litellm/proxy/_experimental/mcp_server/server.py index 20131d3a261..9e76a6aea22 100644 --- a/litellm/proxy/_experimental/mcp_server/server.py +++ b/litellm/proxy/_experimental/mcp_server/server.py @@ -1651,11 +1651,16 @@ if MCP_AVAILABLE: if prisma_client is None: return - credential = await get_user_credential( + raw_credential = await get_user_credential( prisma_client=prisma_client, user_id=user_id, server_id=mcp_server.server_id, ) + # Apply _extract_access_token so the cache always stores a plain token + # string. Without this, a JSON blob {"access_token": ..., "refresh_token": ...} + # stored by the OAuth2 callback would be returned as-is by _get_byok_credential + # on the next request, causing Bearer-header corruption and silent 401s. + credential = _extract_access_token(raw_credential) _write_byok_cred_cache(user_id, mcp_server.server_id, credential) if credential is None: raise HTTPException(