Skip to content

Commit 482cb3e

Browse files
committed
relax validation
1 parent a2643d3 commit 482cb3e

2 files changed

Lines changed: 17 additions & 9 deletions

File tree

src/mcp/shared/auth.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from typing import Any, Literal
22

3-
from pydantic import AnyHttpUrl, BaseModel, Field
3+
from pydantic import AnyHttpUrl, BaseModel, Field, field_validator
44

55

66
class OAuthToken(BaseModel):
@@ -9,11 +9,20 @@ class OAuthToken(BaseModel):
99
"""
1010

1111
access_token: str
12-
token_type: Literal["bearer"] = "bearer"
12+
token_type: Literal["Bearer"] = "Bearer"
1313
expires_in: int | None = None
1414
scope: str | None = None
1515
refresh_token: str | None = None
1616

17+
@field_validator('token_type', mode='before')
18+
@classmethod
19+
def normalize_token_type(cls, v: str | None) -> str | None:
20+
if isinstance(v, str):
21+
# Bearer is title-cased in the spec, so we normalize it
22+
# https://datatracker.ietf.org/doc/html/rfc6750#section-4
23+
return v.title()
24+
return v
25+
1726

1827
class InvalidScopeError(Exception):
1928
def __init__(self, message: str):
@@ -111,13 +120,13 @@ class OAuthMetadata(BaseModel):
111120
token_endpoint: AnyHttpUrl
112121
registration_endpoint: AnyHttpUrl | None = None
113122
scopes_supported: list[str] | None = None
114-
response_types_supported: list[Literal["code"]] = ["code"]
123+
response_types_supported: list[Literal["code"] | str] = ["code"]
115124
response_modes_supported: list[Literal["query", "fragment"]] | None = None
116125
grant_types_supported: (
117-
list[Literal["authorization_code", "refresh_token"]] | None
126+
list[Literal["authorization_code", "refresh_token"] | str] | None
118127
) = None
119128
token_endpoint_auth_methods_supported: (
120-
list[Literal["none", "client_secret_post"]] | None
129+
list[Literal["none", "client_secret_post"] | str] | None
121130
) = None
122131
token_endpoint_auth_signing_alg_values_supported: None = None
123132
service_documentation: AnyHttpUrl | None = None
@@ -126,12 +135,12 @@ class OAuthMetadata(BaseModel):
126135
op_tos_uri: AnyHttpUrl | None = None
127136
revocation_endpoint: AnyHttpUrl | None = None
128137
revocation_endpoint_auth_methods_supported: (
129-
list[Literal["client_secret_post"]] | None
138+
list[Literal["client_secret_post"] | str] | None
130139
) = None
131140
revocation_endpoint_auth_signing_alg_values_supported: None = None
132141
introspection_endpoint: AnyHttpUrl | None = None
133142
introspection_endpoint_auth_methods_supported: (
134-
list[Literal["client_secret_post"]] | None
143+
list[Literal["client_secret_post"] | str] | None
135144
) = None
136145
introspection_endpoint_auth_signing_alg_values_supported: None = None
137-
code_challenge_methods_supported: list[Literal["S256"]] | None = None
146+
code_challenge_methods_supported: list[Literal["S256"] | str] | None = None

tests/client/test_auth.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -760,7 +760,6 @@ async def test_async_auth_flow_no_token(self, oauth_provider):
760760
# No Authorization header should be added if no token
761761
assert "Authorization" not in updated_request.headers
762762

763-
764763
@pytest.mark.anyio
765764
async def test_scope_priority_client_metadata_first(
766765
self, oauth_provider, oauth_client_info

0 commit comments

Comments
 (0)