From e1cd2eabca76663cf316acb7307adbfeb8bac964 Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Mon, 9 Feb 2026 17:15:29 -0800 Subject: [PATCH] fix: atomic lock creation + validate JSON response shape - Use dict.setdefault() for atomic per-server lock creation - Add isinstance(body, dict) check before accessing token response fields Co-Authored-By: Claude Opus 4.6 --- .../_experimental/mcp_server/oauth2_token_cache.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/oauth2_token_cache.py b/litellm/proxy/_experimental/mcp_server/oauth2_token_cache.py index d43ce53a97e..f898447f24d 100644 --- a/litellm/proxy/_experimental/mcp_server/oauth2_token_cache.py +++ b/litellm/proxy/_experimental/mcp_server/oauth2_token_cache.py @@ -39,9 +39,7 @@ class MCPOAuth2TokenCache(InMemoryCache): self._locks: Dict[str, asyncio.Lock] = {} def _get_lock(self, server_id: str) -> asyncio.Lock: - if server_id not in self._locks: - self._locks[server_id] = asyncio.Lock() - return self._locks[server_id] + return self._locks.setdefault(server_id, asyncio.Lock()) async def async_get_token(self, server: "MCPServer") -> Optional[str]: """Return a valid access token, fetching or refreshing as needed. @@ -97,6 +95,12 @@ class MCPOAuth2TokenCache(InMemoryCache): response.raise_for_status() body = response.json() + if not isinstance(body, dict): + raise ValueError( + f"OAuth2 token response for MCP server '{server.server_id}' " + f"returned non-object JSON (got {type(body).__name__})" + ) + access_token = body.get("access_token") if not access_token: raise ValueError(