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

Offen
#2,687 3 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

Bewertung

Schwierigkeit
3/5
Geschätzter Aufwand
1-2 Tage
Anfängerfreundlichkeit
72/100
Issue-Typ
Bug
Klarheit
Klar beschrieben
Aktivitätsstatus
Ruhig
Tech-Stack
python

Rechercherichtung

Beginne in mcp/server/auth/provider.py bei OAuthClientInformationFull und verfolge die Validierung von redirect_uris. Untersuche anschließend die Membership-Prüfung in mcp/server/auth/handlers/authorize.py. Reproduziere den AnyUrl/AnyHttpUrl-Mismatch mit dem Beispiel aus dem Issue und ergänze eine Regressionstestabdeckung für Eingaben von Subtypen. Als erledigt gilt die Aufgabe, wenn äquivalente URLs während des authorize-to-token-Flows erfolgreich verglichen werden, ohne dass Aufrufer rohe Strings übergeben müssen.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Beschreibung

auth bug fix proposed P2 ready for work

Summary
When implementing a custom OAuth provider against the MCP Python SDK, callers must construct OAuthClientInformationFull instances. The SDK declares redirect_uris: list[AnyUrl] (where AnyUrl is pydantic's base URL type). Passing pydantic's stricter subtype AnyHttpUrl (or any other AnyUrl subtype) causes silent equality failures downstream: AnyUrl("https://...") == AnyHttpUrl("https://...") returns False in pydantic v2, even when the two URLs serialize identically. This breaks redirect_uri matching during the /authorize/token exchange.
Reproducer

from pydantic import AnyUrl, AnyHttpUrl
from mcp.server.auth.provider import OAuthClientInformationFull

# pydantic v2 strict-type equality
u1 = AnyUrl("https://example.com/callback")
u2 = AnyHttpUrl("https://example.com/callback")
assert str(u1) == str(u2)   # True (both render the same)
assert u1 == u2              # FAILS in pydantic v2 — different runtime types

# Concrete impact in OAuth flow:
client_info = OAuthClientInformationFull(
    client_id="test",
    redirect_uris=[AnyHttpUrl("https://example.com/cb")],
    # ...other required fields
)
# When the /authorize request arrives with redirect_uri parameter, the SDK
# constructs an AnyUrl from the query string and checks membership:
incoming = AnyUrl("https://example.com/cb")
assert incoming in client_info.redirect_uris   # FAILS — type mismatch

Expected behavior
OAuthClientInformationFull.redirect_uris should accept and compare-equal across AnyUrl and AnyUrl subtypes (AnyHttpUrl, AnyHttpsUrl, etc.) when the underlying URL is identical.
Actual behavior
Strict-type equality causes the membership check to fail. The OAuth flow returns a generic redirect-mismatch error to the client; the underlying cause (type vs URL mismatch) is invisible without instrumenting the SDK.
Suggested fix
Two options:
Coerce on assignment. Have OAuthClientInformationFull.redirect_uris field validator coerce all values to AnyUrl (the declared base type), regardless of what the caller passes. This is the cleanest fix and matches the field declaration.
Compare-by-string. Override __eq__ on the AnyUrl chain to compare-by-str() rather than by runtime type. Broader-impact change; probably not desirable.
Option 1 is preferred. A short field_validator with mode="before" converting to AnyUrl strings before pydantic instantiates would do it.
Workaround (current PolyBot mitigation)
Pass redirect_uris as raw list[str]; pydantic coerces to AnyUrl per the field declaration. This avoids the type mismatch:

client_info = OAuthClientInformationFull(
    client_id="test",
    redirect_uris=["https://example.com/cb"],   # raw strings, not AnyHttpUrl
    # ...
)

Works at runtime; loses some IDE type hints in the caller code.
Environment
mcp Python SDK version: 1.27.1
pydantic version: 2.x
Python: 3.11+
Related code locations
In the MCP SDK:
mcp/server/auth/provider.pyOAuthClientInformationFull definition with redirect_uris: list[AnyUrl]
mcp/server/auth/handlers/authorize.py — where the membership check happens
Severity
Medium — silently breaks OAuth flows in custom-provider setups; reproducer is simple; workaround is trivial once known but the failure mode is hard to diagnose from the user-facing error.

Vorherrschende Sprache
Python
Sterne
24.3k
Forks
4k
Ø Merge
1 T. 19 Min.
Gemergte PRs (30 T.)
29

Beitragsleitfaden

Beitragsleitfaden öffnen

Erste Schritte

  1. Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
  3. Forken Sie das Repository und arbeiten Sie in einem Branch.
  4. Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.

Mehr aus modelcontextprotocol/python-sdk

Alle Issues in modelcontextprotocol/python-sdk

Ähnliche Issues

Weitere Issues zu Python

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.