`OAuthClientInformationFull.redirect_uris`: pydantic strict-type-equality breaks `AnyUrl(x) != AnyHttpUrl(x)` round-trip
維護者通常 1 天內回覆
@syf2211 已經在處理了。
開始於 2026年6月26日。
評估
- 難度
- 3/5
- 預估耗時
- 1-2 天
- 新手友好度
- 72/100
- Issue 類型
- 缺陷
- 描述清晰度
- 描述清楚
- 活躍度
- 冷清
- 技術堆疊
- python
研究方向
從 mcp/server/auth/provider.py 中的 OAuthClientInformationFull 開始,追蹤 redirect_uris 的驗證,然後檢查 mcp/server/auth/handlers/authorize.py 中的 membership check。使用 issue 中的範例重現 AnyUrl/AnyHttpUrl 不相符,並為子型別輸入新增回歸測試涵蓋。完成的標準是:在 authorize-to-token 流程中,等價的 URL 能夠成功比較,而不要求呼叫端傳入原始字串。
由索引模型根據 Issue 內容生成。
描述
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.py — OAuthClientInformationFull 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.
- 主要語言
- Python
- 星號
- 24.5k
- 分支
- 4k
- 平均合併
- 1 天 4 小時
- 30 天內合併 PR
- 33
環境準備
- 沒有 Dockerfile 或 Docker Compose 檔案
- 有 Pull Request 範本
- 閱讀貢獻指南
從這裡開始
- 先讀完整個 Issue,再讀專案的貢獻指南。
- 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
- Fork 儲存庫,在一個分支上完成修改。
- 送出 Pull Request,並在描述裡引用這個 Issue 編號。
modelcontextprotocol/python-sdk 的其他 Issue
-
bug v1 v2
難度 2/5 1-3 小時 新手友好度 75/100
modelcontextprotocol/python-sdk#3670 · 1 則留言 ·
維護者通常 1 天內回覆
-
documentation v2
難度 2/5 1-3 小時 新手友好度 82/100
modelcontextprotocol/python-sdk#3662 ·
維護者通常 1 天內回覆
-
Audio(data=b"") raises "Either path or data can be provided", while Image(data=b"") works可能已有人在做 @KaiyiQuan 於 2 天前認領。 未關閉bug v1 v2
難度 1/5 1 小時以內 新手友好度 85/100
modelcontextprotocol/python-sdk#3656 · 1 則留言 ·
維護者通常 1 天內回覆
-
enhancement
難度 2/5 1-3 小時 新手友好度 66/100
modelcontextprotocol/python-sdk#3655 ·
維護者通常 1 天內回覆
-
enhancement
難度 1/5 1 小時以內 新手友好度 86/100
modelcontextprotocol/python-sdk#3654 ·
維護者通常 1 天內回覆
查看 modelcontextprotocol/python-sdk 的全部 Issue
相似的 Issue
-
area:space-accuracy good first issue track:data
難度 2/5 1-3 小時 新手友好度 85/100
Sara-Managed-Projects/space-radar#904 ·
維護者通常 1 天內回覆