diff --git a/src/mcp/server/auth/provider.py b/src/mcp/server/auth/provider.py index 957082a85..89032ab0e 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 identifier (typically the "sub" JWT claim / user ID) RegistrationErrorCode = Literal[ diff --git a/tests/server/auth/middleware/test_auth_context.py b/tests/server/auth/middleware/test_auth_context.py index 66481bcf7..722863867 100644 --- a/tests/server/auth/middleware/test_auth_context.py +++ b/tests/server/auth/middleware/test_auth_context.py @@ -117,3 +117,23 @@ async def send(message: Message) -> None: # pragma: no cover # Verify context is still empty after middleware assert auth_context_var.get() is None assert get_access_token() is None + + +def test_access_token_subject_field(): + """Test that AccessToken supports the optional subject field.""" + # Without subject (backward compatible) + token_no_sub = AccessToken( + token="token1", + client_id="client1", + scopes=["read"], + ) + assert token_no_sub.subject is None + + # With subject + token_with_sub = AccessToken( + token="token2", + client_id="client2", + scopes=["read", "write"], + subject="user-123", + ) + assert token_with_sub.subject == "user-123"