Skip to content

Commit 6b45c6c

Browse files
author
冯基魁
committed
fix: normalize oauth redirect uri url subtypes
1 parent a527142 commit 6b45c6c

2 files changed

Lines changed: 31 additions & 3 deletions

File tree

src/mcp/shared/auth.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Any, Literal
1+
from typing import Any, Literal, cast
22

33
from pydantic import AnyHttpUrl, AnyUrl, BaseModel, ConfigDict, Field, field_validator
44

@@ -84,6 +84,14 @@ class OAuthClientMetadata(BaseModel):
8484
software_id: str | None = None
8585
software_version: str | None = None
8686

87+
@field_validator("redirect_uris", mode="before")
88+
@classmethod
89+
def _normalize_redirect_uri_subtypes(cls, v: object) -> object:
90+
if isinstance(v, list):
91+
# Pydantic URL equality is type-strict, so store redirect URIs as the declared AnyUrl type.
92+
return [str(item) if isinstance(item, AnyUrl) else item for item in cast(list[object], v)]
93+
return v
94+
8795
@field_validator(
8896
"client_uri",
8997
"logo_uri",

tests/shared/test_auth.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
"""Tests for OAuth 2.0 shared code."""
22

33
import pytest
4-
from pydantic import ValidationError
4+
from pydantic import AnyHttpUrl, AnyUrl, ValidationError
55

6-
from mcp.shared.auth import OAuthClientInformationFull, OAuthClientMetadata, OAuthMetadata
6+
from mcp.shared.auth import (
7+
InvalidRedirectUriError,
8+
OAuthClientInformationFull,
9+
OAuthClientMetadata,
10+
OAuthMetadata,
11+
)
712

813

914
def test_oauth():
@@ -138,3 +143,18 @@ def test_invalid_non_empty_url_still_rejected():
138143
}
139144
with pytest.raises(ValidationError):
140145
OAuthClientMetadata.model_validate(data)
146+
147+
148+
def test_redirect_uri_subtypes_normalized_for_validation():
149+
client_info = OAuthClientInformationFull(
150+
client_id="test-client",
151+
redirect_uris=[AnyHttpUrl("https://example.com/callback")],
152+
)
153+
154+
incoming = AnyUrl("https://example.com/callback")
155+
156+
assert client_info.validate_redirect_uri(incoming) == incoming
157+
assert client_info.model_dump(mode="json")["redirect_uris"] == ["https://example.com/callback"]
158+
159+
with pytest.raises(InvalidRedirectUriError):
160+
client_info.validate_redirect_uri(AnyUrl("https://example.com/other"))

0 commit comments

Comments
 (0)