Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions src/mcp/shared/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,12 @@ 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(" ")
for scope in requested_scopes:
if scope not in allowed_scopes: # pragma: no branch
raise InvalidScopeError(f"Client was not registered with scope {scope}")
return requested_scopes # pragma: no cover
if self.scope is not None:
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}")
return requested_scopes

def validate_redirect_uri(self, redirect_uri: AnyUrl | None) -> AnyUrl:
if redirect_uri is not None:
Expand Down
50 changes: 50 additions & 0 deletions tests/shared/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,53 @@ def test_oauth_with_jarm():
"token_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post"],
}
)


def test_validate_scope_none_client_scope_allows_any_requested_scope():
"""When client.scope is None, any requested scope should be allowed."""
from mcp.shared.auth import OAuthClientMetadata

client = OAuthClientMetadata(
redirect_uris=["https://example.com/callback"],
scope=None,
)
result = client.validate_scope("read write admin")
assert result == ["read", "write", "admin"]


def test_validate_scope_with_client_scope_rejects_unregistered():
"""When client.scope is set, unregistered scopes should be rejected."""
import pytest

from mcp.shared.auth import InvalidScopeError, OAuthClientMetadata

client = OAuthClientMetadata(
redirect_uris=["https://example.com/callback"],
scope="read write",
)
with pytest.raises(InvalidScopeError):
client.validate_scope("admin")


def test_validate_scope_with_client_scope_allows_registered():
"""When client.scope is set, registered scopes should be allowed."""
from mcp.shared.auth import OAuthClientMetadata

client = OAuthClientMetadata(
redirect_uris=["https://example.com/callback"],
scope="read write",
)
result = client.validate_scope("read")
assert result == ["read"]


def test_validate_scope_none_requested_returns_none():
"""When requested_scope is None, should return None."""
from mcp.shared.auth import OAuthClientMetadata

client = OAuthClientMetadata(
redirect_uris=["https://example.com/callback"],
scope=None,
)
result = client.validate_scope(None)
assert result is None
Loading