Skip to content

Commit ffea4ae

Browse files
committed
fix: normalize OAuth redirect URI URL subtypes
1 parent 616476f commit ffea4ae

2 files changed

Lines changed: 22 additions & 2 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, Field, field_validator
44

@@ -85,6 +85,14 @@ def _empty_string_optional_url_to_none(cls, v: object) -> object:
8585
return None
8686
return v
8787

88+
@field_validator("redirect_uris", mode="before")
89+
@classmethod
90+
def _normalize_redirect_uris(cls, v: object) -> object:
91+
if isinstance(v, list):
92+
redirect_uris = cast(list[Any], v)
93+
return [str(uri) for uri in redirect_uris]
94+
return v
95+
8896
def validate_scope(self, requested_scope: str | None) -> list[str] | None:
8997
if requested_scope is None:
9098
return None

tests/shared/test_auth.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
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

66
from mcp.shared.auth import OAuthClientInformationFull, OAuthClientMetadata, OAuthMetadata
77

@@ -109,6 +109,18 @@ def test_valid_url_passes_through_unchanged():
109109
assert str(metadata.client_uri) == "https://udemy.com/"
110110

111111

112+
def test_redirect_uri_url_subtypes_are_normalized():
113+
info = OAuthClientInformationFull(
114+
client_id="abc123",
115+
redirect_uris=[AnyHttpUrl("https://example.com/callback")],
116+
)
117+
118+
incoming = AnyUrl("https://example.com/callback")
119+
120+
assert info.validate_redirect_uri(incoming) == incoming
121+
assert info.model_dump(mode="json")["redirect_uris"] == ["https://example.com/callback"]
122+
123+
112124
def test_information_full_inherits_coercion():
113125
"""OAuthClientInformationFull subclasses OAuthClientMetadata, so the
114126
same coercion applies to DCR responses parsed via the full model."""

0 commit comments

Comments
 (0)