Skip to content

fix: coerce redirect_uris AnyUrl subtypes to plain AnyUrl for correct equality#2942

Open
kaXianc2-gom wants to merge 1 commit into
modelcontextprotocol:mainfrom
kaXianc2-gom:fix/redirect-uris-anyurl-coercion
Open

fix: coerce redirect_uris AnyUrl subtypes to plain AnyUrl for correct equality#2942
kaXianc2-gom wants to merge 1 commit into
modelcontextprotocol:mainfrom
kaXianc2-gom:fix/redirect-uris-anyurl-coercion

Conversation

@kaXianc2-gom

Copy link
Copy Markdown

AI Assistance Disclosure

This contribution was developed with AI assistance (Claude / Anthropic Claude Code).

Summary

Fix #2687: pydantic v2 strict-type equality means AnyUrl(x) != AnyHttpUrl(x) even when the URLs are identical. This breaks validate_redirect_uri membership checks when callers pass AnyHttpUrl instances in redirect_uris.

Changes

Add a @field_validator(redirect_uris) to OAuthClientMetadata that converts each element to AnyUrl(str(...)), stripping the subtype while preserving the URL value.

****: +13 lines (validator + docstring) in OAuthClientMetadata
****: +62 lines (5 new test cases)

Verification Process

  1. Forked → cloned to local sandbox
  2. Applied fix: field_validator on redirect_uris
  3. Wrote 5 targeted tests
  4. Ran auth test suite: 17/17 passed (12 existing + 5 new)
  5. Ruff format + lint: all checks passed
  6. Zero new pragma/type:ignore/noqa annotations

Test Results

tests/shared/test_auth.py::test_redirect_uris_coerces_any_http_url_to_any_url PASSED [NEW]
tests/shared/test_auth.py::test_redirect_uris_validate_matches_coerced_url PASSED [NEW]
tests/shared/test_auth.py::test_redirect_uris_validate_rejects_mismatched_url PASSED [NEW]
tests/shared/test_auth.py::test_redirect_uris_none_untouched PASSED [NEW]
tests/shared/test_auth.py::test_redirect_uris_string_list_still_works PASSED [NEW]

Cross-Validation

Scenario Before After
AnyHttpUrl in redirect_uris, matching AnyUrl lookup ❌ type mismatch ✅ matches
AnyHttpUrl in redirect_uris, different URL lookup ❌ (correct reject) ❌ (correct reject)
redirect_uris=None
String list in redirect_uris ✅ (pydantic coerces) ✅ (unchanged)
AnyUrl subtype preserved in field ❌ stored as subtype ✅ stored as plain AnyUrl

Closes #2687

… equality

pydantic v2 strict-type equality means AnyUrl(x) != AnyHttpUrl(x) even
when the URLs are identical. This breaks validate_redirect_uri membership
checks when callers pass AnyHttpUrl instances in redirect_uris.

Add a field_validator to OAuthClientMetadata.redirect_uris that converts
each element to AnyUrl(str(...)), stripping the subtype while preserving
the URL value.

Closes modelcontextprotocol#2687

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OAuthClientInformationFull.redirect_uris: pydantic strict-type-equality breaks AnyUrl(x) != AnyHttpUrl(x) round-trip

1 participant