diff --git a/src/mcp/shared/auth.py b/src/mcp/shared/auth.py index 2bbf7a715a..5ce5faa7b2 100644 --- a/src/mcp/shared/auth.py +++ b/src/mcp/shared/auth.py @@ -109,7 +109,9 @@ def validate_scope(self, requested_scope: str | None) -> list[str] | None: if requested_scope is None: return None requested_scopes = requested_scope.split(" ") - allowed_scopes = [] if self.scope is None else self.scope.split(" ") + if self.scope is None: + return requested_scopes + allowed_scopes = self.scope.split(" ") for scope in requested_scopes: if scope not in allowed_scopes: raise InvalidScopeError(f"Client was not registered with scope {scope}") diff --git a/tests/shared/test_auth.py b/tests/shared/test_auth.py index 7463bc5a8a..f40459366f 100644 --- a/tests/shared/test_auth.py +++ b/tests/shared/test_auth.py @@ -3,7 +3,7 @@ import pytest from pydantic import ValidationError -from mcp.shared.auth import OAuthClientInformationFull, OAuthClientMetadata, OAuthMetadata +from mcp.shared.auth import InvalidScopeError, OAuthClientInformationFull, OAuthClientMetadata, OAuthMetadata def test_oauth(): @@ -138,3 +138,43 @@ def test_invalid_non_empty_url_still_rejected(): } with pytest.raises(ValidationError): OAuthClientMetadata.model_validate(data) + + +def test_validate_scope_returns_none_when_no_scope_requested(): + client = OAuthClientInformationFull( + client_id="client", + redirect_uris=["https://example.com/callback"], + scope="read write", + ) + + assert client.validate_scope(None) is None + + +def test_validate_scope_allows_registered_scopes(): + client = OAuthClientInformationFull( + client_id="client", + redirect_uris=["https://example.com/callback"], + scope="read write", + ) + + assert client.validate_scope("read write") == ["read", "write"] + + +def test_validate_scope_rejects_unregistered_scopes(): + client = OAuthClientInformationFull( + client_id="client", + redirect_uris=["https://example.com/callback"], + scope="read", + ) + + with pytest.raises(InvalidScopeError, match="Client was not registered with scope write"): + client.validate_scope("write") + + +def test_validate_scope_allows_requested_scopes_when_registered_scope_omitted(): + client = OAuthClientInformationFull( + client_id="client", + redirect_uris=["https://example.com/callback"], + ) + + assert client.validate_scope("read write") == ["read", "write"]