diff --git a/src/fastmcp/server/auth/oauth_proxy.py b/src/fastmcp/server/auth/oauth_proxy.py index a75f8b690..c70b12edc 100644 --- a/src/fastmcp/server/auth/oauth_proxy.py +++ b/src/fastmcp/server/auth/oauth_proxy.py @@ -242,6 +242,7 @@ class OAuthProxy(OAuthProvider): service_documentation_url: AnyHttpUrl | str | None = None, # Client redirect URI validation allowed_client_redirect_uris: list[str] | None = None, + valid_scopes: list[str] | None = None, ): """Initialize the OAuth proxy provider. @@ -262,9 +263,14 @@ class OAuthProxy(OAuthProvider): If None (default), only localhost redirect URIs are allowed. If empty list, all redirect URIs are allowed (not recommended for production). These are for MCP clients performing loopback redirects, NOT for the upstream OAuth app. + valid_scopes: List of all the possible valid scopes for a client. + These are advertised to clients through the `/.well-known` endpoints. Defaults to `reuqired_scopes` if not provided. """ # Always enable DCR since we implement it locally for MCP clients - client_registration_options = ClientRegistrationOptions(enabled=True) + client_registration_options = ClientRegistrationOptions( + enabled=True, + valid_scopes=valid_scopes or token_verifier.required_scopes, + ) # Enable revocation only if upstream endpoint provided revocation_options = ( diff --git a/tests/server/auth/test_oauth_proxy.py b/tests/server/auth/test_oauth_proxy.py index 6892ef8c2..8f9c90d7a 100644 --- a/tests/server/auth/test_oauth_proxy.py +++ b/tests/server/auth/test_oauth_proxy.py @@ -55,6 +55,10 @@ class TestOAuthProxyComprehensive: assert str(proxy.base_url) == "https://api.example.com/" assert str(proxy.issuer_url) == "https://issuer.example.com/" assert str(proxy.service_documentation_url) == "https://docs.example.com/" + assert ( + proxy.client_registration_options is not None + and proxy.client_registration_options.valid_scopes == ["read", "write"] + ) def test_initialization_with_all_parameters(self, jwt_verifier): """Test OAuthProxy initialization with all optional parameters.""" @@ -69,6 +73,7 @@ class TestOAuthProxyComprehensive: redirect_path="/auth/callback", issuer_url="https://issuer.example.com", service_documentation_url="https://docs.example.com", + valid_scopes=["open", "close"], ) # Verify all parameters are set correctly @@ -83,6 +88,10 @@ class TestOAuthProxyComprehensive: assert proxy._redirect_path == "/auth/callback" assert str(proxy.issuer_url) == "https://issuer.example.com/" assert str(proxy.service_documentation_url) == "https://docs.example.com/" + assert ( + proxy.client_registration_options is not None + and proxy.client_registration_options.valid_scopes == ["open", "close"] + ) def test_redirect_path_normalization(self, jwt_verifier): """Test that redirect_path is normalized to start with /."""