Allow `OAuthClientProvider` to accept a pre-configured auth server URL

Abierto
#2,121 0 comentarios 0 reacciones 1 asignado Ver en GitHub

@pcarleton ya está trabajando en esto.

Desde el 5/3/2026.

Evaluación

Este issue todavía no se ha evaluado.

Descripción

auth enhancement needs confirmation
Description

Problem

OAuthClientProvider.async_auth_flow in mcp/client/auth/oauth2.py only triggers OAuth discovery when it receives a 401 response. There's no way to tell the client "I already know where the auth server is — skip straight to OASM discovery and token exchange."

This forces an unnecessary round-trip in environments where the authorization server URL is already known (config file, env var, service registry, etc.).

Current behavior

In OAuthClientProvider.__init__:

OAuthClientProvider(
    server_url="http://mcp-server:3002/mcp",
    client_metadata=OAuthClientMetadata(...),
    storage=my_storage,
    redirect_handler=...,
    callback_handler=...,
)

Then inside async_auth_flow (line ~500):

response = yield request  # unauthenticated

if response.status_code == 401:
    # Only NOW does it start discovery:
    # 1. Extract WWW-Authenticate
    # 2. Discover PRM (/.well-known/oauth-protected-resource)
    # 3. Set self.context.auth_server_url from PRM
    # 4. Discover OASM (/.well-known/oauth-authorization-server)
    # 5. Register client (DCR or CIMD)
    # 6. Authorization code + PKCE
    # 7. Retry with token

Steps 1–3 exist purely to resolve auth_server_url. When the caller already has that URL, they're wasted work.

Proposed change

Add an optional authorization_server_url parameter to OAuthClientProvider.__init__:

OAuthClientProvider(
    server_url="http://mcp-server:3002/mcp",
    client_metadata=OAuthClientMetadata(...),
    storage=my_storage,
    redirect_handler=...,
    callback_handler=...,
    authorization_server_url="http://auth-server:9000",  # NEW
)

When provided:

  • Set self.context.auth_server_url at init time
  • In async_auth_flow, skip the initial unauthenticated request and jump directly to OASM fetch → client registration → token exchange → send authenticated request

When omitted, behavior stays identical to today.

Rough implementation sketch

In OAuthContext:

@dataclass
class OAuthContext:
    # ... existing fields ...
    auth_server_url: str | None = None  # already exists, just needs to be settable at init

In OAuthClientProvider.__init__:

def __init__(self, ..., authorization_server_url: str | None = None):
    self.context = OAuthContext(...)
    if authorization_server_url:
        self.context.auth_server_url = authorization_server_url

In async_auth_flow, before response = yield request:

if self.context.auth_server_url and not self.context.is_token_valid():
    # Auth server already known — go straight to OASM + token exchange
    # (skip sending unauthenticated request and waiting for 401)
    ...

Why this matters

  • Latency — One fewer round-trip before the client can do real work.
  • Controlled environments — Enterprise setups always know their auth server. Discovery via 401 adds no value there.
  • Complements existing featuresclient_metadata_url (CIMD) already lets you skip DCR. This is the same idea applied one step earlier in the chain.

Alternatives considered

Approach Limitation
CIMD (client_metadata_url) Skips DCR (step 5), but the 401 trigger + PRM discovery still happens
Pre-populating TokenStorage We don't necessarily manage the TokenStorage
Using httpx.Auth with a static Bearer token Works, but loses the full OAuth lifecycle (refresh, re-auth, etc.)
References
  • OAuthClientProvidermcp/client/auth/oauth2.py line 217
  • OAuthContext.auth_server_url — already exists as a field (line ~107), just populated from PRM today
  • async_auth_flow — the 401 branch starts at line ~504
  • MCP spec — Authorization
  • RFC 9728 — Protected Resource Metadata
  • RFC 8414 — OAuth Authorization Server Metadata
Lenguaje dominante
Python
Estrellas
24.3k
Forks
4k
Merge medio
1 d 1 h
PR fusionados (30 d)
31

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de modelcontextprotocol/python-sdk

Todos los issues de modelcontextprotocol/python-sdk

Issues similares

Más issues de Python

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.