diff --git a/src/mcp/server/auth/provider.py b/src/mcp/server/auth/provider.py index 957082a85..1a4c17527 100644 --- a/src/mcp/server/auth/provider.py +++ b/src/mcp/server/auth/provider.py @@ -40,6 +40,7 @@ class AccessToken(BaseModel): scopes: list[str] expires_at: int | None = None resource: str | None = None # RFC 8707 resource indicator + subject: str | None = None # Subject (user ID, "sub" claim) RegistrationErrorCode = Literal[ diff --git a/tests/server/auth/test_provider.py b/tests/server/auth/test_provider.py index aaaeb413a..2487d502a 100644 --- a/tests/server/auth/test_provider.py +++ b/tests/server/auth/test_provider.py @@ -77,3 +77,27 @@ def test_construct_redirect_uri_encoded_values(): # urlencode uses + for spaces by default assert "state=test+state+with+spaces" in result + + +def test_access_token_subject_field(): + """Test AccessToken supports optional subject (user ID) claim.""" + from mcp.server.auth.provider import AccessToken + + token = AccessToken( + token="token123", + client_id="client1", + scopes=["read", "write"], + subject="user_456" + ) + assert token.subject == "user_456" + assert token.token == "token123" + assert token.client_id == "client1" + assert token.scopes == ["read", "write"] + + # subject is optional + token2 = AccessToken( + token="token2", + client_id="client2", + scopes=["read"] + ) + assert token2.subject is None