From 813168ad7940895ce74b9b3c84ea4097dfe613c3 Mon Sep 17 00:00:00 2001 From: Sacha <55644767+SoldierSacha@users.noreply.github.com> Date: Tue, 3 Jun 2025 16:33:31 -0700 Subject: [PATCH] Allow client credentials in dynamic registration --- src/mcp/server/auth/handlers/register.py | 14 ++++++++--- .../fastmcp/auth/test_auth_integration.py | 24 ++++++++++++++++++- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/mcp/server/auth/handlers/register.py b/src/mcp/server/auth/handlers/register.py index 2e25c779a3..78ad94af18 100644 --- a/src/mcp/server/auth/handlers/register.py +++ b/src/mcp/server/auth/handlers/register.py @@ -74,12 +74,20 @@ async def handle(self, request: Request) -> Response: ), status_code=400, ) - if set(client_metadata.grant_types) != {"authorization_code", "refresh_token"}: + grant_types_set = set(client_metadata.grant_types) + valid_sets = [ + {"authorization_code", "refresh_token"}, + {"client_credentials"}, + ] + + if grant_types_set not in valid_sets: return PydanticJSONResponse( content=RegistrationErrorResponse( error="invalid_client_metadata", - error_description="grant_types must be authorization_code " - "and refresh_token", + error_description=( + "grant_types must be authorization_code and refresh_token " + "or client_credentials" + ), ), status_code=400, ) diff --git a/tests/server/fastmcp/auth/test_auth_integration.py b/tests/server/fastmcp/auth/test_auth_integration.py index a226620456..907b6a8351 100644 --- a/tests/server/fastmcp/auth/test_auth_integration.py +++ b/tests/server/fastmcp/auth/test_auth_integration.py @@ -1001,9 +1001,31 @@ async def test_client_registration_invalid_grant_type( assert error_data["error"] == "invalid_client_metadata" assert ( error_data["error_description"] - == "grant_types must be authorization_code and refresh_token" + == ( + "grant_types must be authorization_code and " + "refresh_token or client_credentials" + ) + ) + + @pytest.mark.anyio + async def test_client_registration_client_credentials( + self, test_client: httpx.AsyncClient + ): + client_metadata = { + "redirect_uris": ["https://client.example.com/callback"], + "client_name": "CC Client", + "grant_types": ["client_credentials"], + } + + response = await test_client.post( + "/register", + json=client_metadata, ) + assert response.status_code == 201, response.content + client_info = response.json() + assert client_info["grant_types"] == ["client_credentials"] + class TestAuthorizeEndpointErrors: """Test error handling in the OAuth authorization endpoint."""