From 3920bf42c5082f3cdc946600515bbb226b9338ee Mon Sep 17 00:00:00 2001 From: internet-dot <207546839+internet-dot@users.noreply.github.com> Date: Wed, 6 May 2026 10:10:02 +0000 Subject: [PATCH] auth: add optional subject field to AccessToken (fixes #1038) Adds a `subject` field to the AccessToken pydantic model to hold the "sub" claim from JWT tokens. This allows token verifiers to include the user ID in the access token, which can then be retrieved via `get_access_token().subject` in request handlers. The field is optional (str | None) to maintain backward compatibility. --- src/mcp/server/auth/provider.py | 1 + tests/server/auth/test_provider.py | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+) 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