diff --git a/src/mcp/server/auth/handlers/register.py b/src/mcp/server/auth/handlers/register.py index b34f893f3..120b1cf09 100644 --- a/src/mcp/server/auth/handlers/register.py +++ b/src/mcp/server/auth/handlers/register.py @@ -69,20 +69,20 @@ async def handle(self, request: Request) -> Response: status_code=400, ) grant_types_set: set[str] = set(client_metadata.grant_types) - valid_sets = [ + required_sets = [ {"authorization_code", "refresh_token"}, {"client_credentials"}, {"token_exchange"}, {"client_credentials", "token_exchange"}, ] - if grant_types_set not in valid_sets: + if not any(required_set.issubset(grant_types_set) for required_set in required_sets): return PydanticJSONResponse( content=RegistrationErrorResponse( error="invalid_client_metadata", error_description=( - "grant_types must be authorization_code and refresh_token " - "or client_credentials or token exchange or client_credentials and token_exchange" + "grant_types must include authorization_code and refresh_token, " + "client_credentials, token_exchange, or client_credentials and token_exchange" ), ), status_code=400, diff --git a/src/mcp/shared/auth.py b/src/mcp/shared/auth.py index bf37a7b57..c7b273d29 100644 --- a/src/mcp/shared/auth.py +++ b/src/mcp/shared/auth.py @@ -47,13 +47,15 @@ class OAuthClientMetadata(BaseModel): # client_secret_post; # ie: we do not support client_secret_basic token_endpoint_auth_method: Literal["none", "client_secret_post"] = "client_secret_post" - # grant_types: this implementation supports authorization_code, refresh_token, client_credentials, & token_exchange + # grant_types: this implementation supports authorization_code, refresh_token, client_credentials, token_exchange, + # and allows additional grant types provided by the client (e.g. device code) grant_types: list[ Literal[ "authorization_code", "refresh_token", "client_credentials", "token_exchange", + "urn:ietf:params:oauth:grant-type:device_code", ] ] = [ "authorization_code",