fix(mcp): require explicit opt-in for OAuth2 M2M client_credentials flow

Auto-detecting M2M from client_id+secret+token_url presence broke existing
interactive OAuth setups (e.g. GitHub Enterprise). Add oauth2_flow field and
default has_client_credentials to False — M2M must be explicitly opted into
with oauth2_flow: client_credentials.
This commit is contained in:
Ishaan Jaffer 2026-03-09 14:43:16 -07:00
parent 1a5e215f08
commit 37eb63388a
2 changed files with 15 additions and 3 deletions

View file

@ -318,6 +318,7 @@ class MCPServerManager:
# oauth specific fields
client_id=server_config.get("client_id", None),
client_secret=server_config.get("client_secret", None),
oauth2_flow=server_config.get("oauth2_flow", None),
scopes=resolved_scopes,
authorization_url=resolved_authorization_url,
token_url=resolved_token_url,
@ -632,6 +633,7 @@ class MCPServerManager:
client_id=client_id_value or getattr(mcp_server, "client_id", None),
client_secret=client_secret_value
or getattr(mcp_server, "client_secret", None),
oauth2_flow=getattr(mcp_server, "oauth2_flow", None),
scopes=resolved_scopes,
authorization_url=mcp_server.authorization_url
or getattr(mcp_oauth_metadata, "authorization_url", None),

View file

@ -1,5 +1,5 @@
from datetime import datetime
from typing import Any, Dict, List, Optional
from typing import Any, Dict, List, Literal, Optional
from pydantic import BaseModel, ConfigDict
@ -60,12 +60,22 @@ class MCPServer(BaseModel):
byok_api_key_help_url: Optional[str] = None
created_at: Optional[datetime] = None
updated_at: Optional[datetime] = None
# OAuth2 flow type. Defaults to None (interactive / authorization_code).
# Set to "client_credentials" to enable M2M token fetching.
oauth2_flow: Optional[Literal["client_credentials", "authorization_code"]] = None
model_config = ConfigDict(arbitrary_types_allowed=True)
@property
def has_client_credentials(self) -> bool:
"""True if this server has OAuth2 client_credentials config (client_id, client_secret, token_url)."""
return bool(self.client_id and self.client_secret and self.token_url)
"""True if this server should use the OAuth2 client_credentials (M2M) flow.
M2M flow must be opted into explicitly via ``oauth2_flow: client_credentials``.
Having client_id / client_secret / token_url present is NOT sufficient —
those fields are also used for interactive (authorization_code) OAuth,
e.g. GitHub Enterprise. Auto-detecting M2M from field presence was a
breaking regression introduced with the M2M feature.
"""
return self.oauth2_flow == "client_credentials"
@property
def needs_user_oauth_token(self) -> bool: