Skip to content

Commit ebff435

Browse files
committed
fix: skip eager token refresh when OAuth metadata is unavailable
When the auth server lives under a non-root path (e.g. /oauth2/api/v1/token), the eager refresh at the top of async_auth_flow used the fallback urljoin(get_authorization_base_url(server_url), '/token') which strips the path, hitting the wrong endpoint. Fix: only attempt the eager refresh when oauth_metadata is already populated (i.e. we know the real token_endpoint). Without metadata, let the request proceed with the stale token, receive a 401, and run full PRM/ASM discovery before retrying — which resolves the correct token endpoint. Fixes #3240
1 parent 1216c53 commit ebff435

1 file changed

Lines changed: 11 additions & 2 deletions

File tree

src/mcp/client/auth/oauth2.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -536,8 +536,17 @@ async def async_auth_flow(self, request: httpx.Request) -> AsyncGenerator[httpx.
536536
# Capture protocol version from request headers
537537
self.context.protocol_version = request.headers.get(MCP_PROTOCOL_VERSION_HEADER)
538538

539-
if not self.context.is_token_valid() and self.context.can_refresh_token():
540-
# Try to refresh token
539+
if (
540+
not self.context.is_token_valid()
541+
and self.context.can_refresh_token()
542+
and self.context.oauth_metadata is not None
543+
):
544+
# Try to refresh token — only when we already have OAuth metadata.
545+
# Without metadata the token endpoint is unknown; the fallback
546+
# urljoin(base_url, "/token") strips the path when the AS lives
547+
# under a non-root path (e.g. /oauth2/api/v1/token). Skipping
548+
# the refresh here lets the request proceed with the stale token,
549+
# receive a 401, and run full metadata discovery before retrying.
541550
refresh_request = await self._refresh_token()
542551
refresh_response = yield refresh_request
543552

0 commit comments

Comments
 (0)