diff --git a/src/mcp/shared/auth.py b/src/mcp/shared/auth.py index ca5b7b45a..583acc9b2 100644 --- a/src/mcp/shared/auth.py +++ b/src/mcp/shared/auth.py @@ -71,7 +71,10 @@ 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(" ") + # When no scope is required (None), allow all requested scopes + if self.scope is None: + return requested_scopes + allowed_scopes = self.scope.split(" ") for scope in requested_scopes: if scope not in allowed_scopes: # pragma: no branch raise InvalidScopeError(f"Client was not registered with scope {scope}") diff --git a/test_validate_scope_fix.py b/test_validate_scope_fix.py new file mode 100644 index 000000000..6dd1c0189 --- /dev/null +++ b/test_validate_scope_fix.py @@ -0,0 +1,48 @@ +"""Test for validate_scope fix when self.scope is None""" +import pytest +from mcp.shared.auth import ClientRegistration, InvalidScopeError + + +def test_validate_scope_with_none_scope_allows_all(): + """When client has no scope restriction (None), all requested scopes should be allowed.""" + client = ClientRegistration( + client_id="test-client", + client_secret="secret", + scope=None, # No scope restriction + redirect_uris=["http://localhost/callback"], + ) + + # Should not raise - all scopes allowed when no restriction + result = client.validate_scope("read write admin") + assert result == ["read", "write", "admin"] + + +def test_validate_scope_with_empty_requested_returns_none(): + """When requested_scope is None, return None.""" + client = ClientRegistration( + client_id="test-client", + client_secret="secret", + scope="read write", + redirect_uris=["http://localhost/callback"], + ) + + result = client.validate_scope(None) + assert result is None + + +def test_validate_scope_with_restrictions_enforced(): + """When client has scope restrictions, only allowed scopes pass.""" + client = ClientRegistration( + client_id="test-client", + client_secret="secret", + scope="read write", + redirect_uris=["http://localhost/callback"], + ) + + # Allowed scope + result = client.validate_scope("read") + assert result == ["read"] + + # Disallowed scope should raise + with pytest.raises(InvalidScopeError): + client.validate_scope("admin")