[Bug] Client sends empty _meta:{} on every request; strict servers (Meta Ads MCP) reject with HTTP 400

当使用基于 MCP Python SDK 构建的客户端连接严格校验 JSON-RPC 参数的服务器(如 Meta Ads MCP)时,SDK 会在每个请求的 params 里无条件附带空的 _meta:{} ,导致服务器返回 HTTP 400。优先排查客户端发出的请求体中是否携带了空的 _meta

快速结论:当使用基于 MCP Python SDK 构建的客户端连接严格校验 JSON-RPC 参数的服务器(如 Meta Ads MCP)时,SDK 会在每个请求的 params 里无条件附带空的 _meta:{},导致服务器返回 HTTP 400。优先排查客户端发出的请求体中是否携带了空的 _meta 字段。

适用环境:MCP Python SDK(current main,涉及 src/mcp/shared/jsonrpc_dispatcher.py);Python 3.12;受影响的服务端为 Meta hosted Ads MCP(https://mcp.facebook.com/ads)。

最快修复方案:升级到包含 PR #3628 的 SDK 版本,该版本已按报告中的建议修改为仅在 _meta 非空时才发送该字段。

注意事项:该修复未在真实 Meta Ads 端点上实际验证过,如果升级后仍无法连接,需要另开新 Issue。修复仅针对空 _meta 的发送逻辑,不涉及更广泛的重构。若自行打补丁,需确保在 trace context 注入之后再判断 _meta 是否为空,并保留调用方传入的非空 metadata 和注入的 progressToken。

问题场景

用户使用基于 MCP Python SDK 构建的客户端(下游报告来自 Hermes Agent)连接需要严格校验 JSON-RPC 参数的 MCP 服务器,例如 Meta hosted Ads MCP(https://mcp.facebook.com/ads)。在 initialize 阶段即被服务器拒绝,导致该服务器从所有基于此 SDK 构建的客户端都无法访问。

报错原文

HTTP 400
-32602 "_meta for Request must be a dict or null"

说明:服务端返回 HTTP 400,错误码 -32602,提示信息为 _meta for Request must be a dict or null。需要注意的是,_meta: null 同样会被拒绝,该字段必须完全不存在。

原因分析

最可能的原因是 JSONRPCDispatcher.send_raw_request 无条件地把 _meta 附加到 params 上。当没有设置 progress 回调、且 otel tracer 为 no-op(默认情况)时,out_meta 保持为空字典 {},于是每个请求都会携带 _meta: {} 上线。严格校验的服务器(如 Meta Ads MCP)会因此拒绝请求。相比之下,Meta 文档中提到的其他 MCP 客户端(Claude Code、ChatGPT)不会发送空的 _meta,因此可以正常连接。这是协议兼容性 bug,而非安全问题。

环境排查

  • 确认 MCP SDK 版本或所基于的 commit,重点检查 src/mcp/shared/jsonrpc_dispatcher.py 中 send_raw_request 的逻辑。
  • 确认 Python 版本(报告中为 3.12)。
  • 确认是否设置了 progress 回调,以及 otel tracer 是否为 no-op(默认)。
  • 确认目标服务端是否为严格校验的 MCP 服务器(如 Meta hosted Ads MCP)。
  • 用 curl 对真实服务端做最小复现:携带 _meta:{} 时返回 400,不带时返回 200。

解决步骤

  1. 升级到包含 PR #3628 的 SDK 版本。该 PR 采用了报告中的修复思路:_meta 仅在非空时才被发送。
  2. 如果暂无法升级,可优先尝试按报告中的建议在本地打补丁,将无条件赋值改为条件发送,例如仅在 out_meta 非空时写入 out_params["_meta"],否则从 out_params 中删除 _meta。
  3. 补丁位置应放在 inject_trace_context(out_meta) 之后,这样真实 trace context 仍会被发送。
  4. 确保补丁不修改调用方传入的原始 params,并保留非空的调用方 metadata 与注入的 progressToken。
  5. 若请求本身没有任何 params(如 ping、tools/list),修复后也不应再带出空的 params 成员,以保持 v1 发送的形态。

验证方法

检查客户端发出的 JSON-RPC 请求体:在无 progress 回调、tracer 为 no-op 的情况下,请求中不应再出现 _meta:{},也不应出现 _meta:null。可用 curl 对目标服务器复现:不带 _meta 时应返回 200,携带空 _meta 时应返回 400,以此确认差异。最终确认方式为客户端能否成功完成 initialize。由于该修复未在真实 Meta Ads 端点上验证过,若仍被拒绝,需另开新 Issue。

参考来源

modelcontextprotocol/python-sdk #3473

相关 PR:#3474(因 missing-issue-link 策略被自动关闭)、#3628(已合并修复)。

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 27228

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注