DEBUG.WIKI
京东云;让 AI 进入真实业务;零售 · 物流 · 客服 · 硬件;查看方案 →
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国
Evoxt;最高 6.0GHz CPU;高频云服务器 · 免费每周备份;查看配置 →

MCP OAuth 授权地址多一个问号

MCP 授权地址已有 query,旧 Python 客户端又加问号,导致 response_type 丢失。保留租户或环境参数,核对 URL 合并、版本与回调清理。

MCP 的浏览器授权页报 invalid_request,或登录后停在普通欢迎页,回调一直收不到授权码。如果授权服务器公布的 authorization_endpoint 已经带查询参数,先看客户端实际打开的地址是否多拼了一个 ?。

例如,服务端公布:

https://auth.example.com/authorize?tenant=acme

旧 Python MCP 客户端可能拼成:

https://auth.example.com/authorize?tenant=acme?response_type=code&client_id=...

授权服务器读到的 tenant 就变成 acme?response_type=code,并没有得到独立的 response_type。这足以让授权流程失败,不能直接认定是账号密码或本机回调端口有问题。

核对公布地址与生成地址

已有 SDK 1.30.0、2.2.0 的近期反馈;已核对的正式版 2.3.0 授权 URL 构造仍使用“端点加问号,再加编码参数”的路径。这不是升级到 2.3.0 就已经消失的问题。

比较可信公开 metadata 的 authorization_endpoint 与客户端生成的授权地址,保留原有 tenant、env、policy 或其他选择参数。它们可能决定授权环境和租户,删除查询串让 URL 看起来整齐,会改变授权目标。

记录时应脱敏 state、授权码和其他会话信息,不能把完整真实授权 URL 贴进公开日志。这篇检查的是浏览器授权地址的 query 合并;metadata 的资源身份不一致则按公开资源端点检查处理。

修复在 URL 构造处合并 query

处理方向是在原授权端点的 query 中加入本次 OAuth 参数,保留已有参数,并继续使用 SDK 生成的 PKCE、state、回调地址和后续 issuer 校验。正常形状应让原来的 tenant=acme 与 response_type=code 分别成为参数。

不能对整条 URL 把第二个问号简单全局替换成 &,也不能自己另造一组 state 或 PKCE 值。若维护本地补丁,应针对所用版本的 _perform_authorization_code_grant 审查改动,用 URL 解析和编码处理 query;修复后仍需完整保留原授权与回调验证。

已有真实部署反馈表明,改正连接分隔符可让不含参数冲突的受影响端点完成授权,但这不等于任意端点都适合字符替换。原端点若已经带 resource、prompt 或其他本次流程也要生成的键,还要明确处理冲突,不能靠服务端“取第一个”或“取最后一个”碰运气。

授权端点不应带 fragment,同一请求参数也不应重复。自建 query 合并逻辑应明确拒绝无效或有歧义的输入;不能把允许重复键的通用 URL 工具行为,直接当成 OAuth 参数冲突已经解决。

目前不能给这处问题指定已核证的正式修复发行版。选择后续版本或下游修复时,要检查实际 URL 构造代码与兼容性;未合入补丁、重复问题关闭或版本号增大,都不足以证明修复已经进入所装的软件。

回调端口错误可能是后续症状

授权立即失败后,应用若反复发起登录,而上一轮回调监听器还没关闭,可能接着报 Address already in use。应先结束旧的授权尝试、按应用生命周期清理自己拥有的监听器,再检查生成地址。

这不表示所有端口占用都由 OAuth URL 造成。不要为隐藏症状结束未知进程,或不断换端口启动更多监听器。

在获准的测试环境中,先按参数解析确认原 query 未变、response_type=code 独立存在,再走授权与回调,确认 state、issuer 和 PKCE 校验仍有效。还要检查没有 query 的普通端点,以及取消或失败后的监听器清理。仅看到登录页出现,不能证明授权码与令牌流程已经完成。