MCP 工具被丢弃
MCP 工具已注册却被客户端列表过滤,检查 x-mcp-header 的参数类型与 schema。核对 2.3.0 注册检查、可选字符串写法和手工 Tool 的边界。
MCP 服务已经启动,工具也注册了,客户端的 tools/list 却少了它。若客户端警告中出现 invalid x-mcp-header 和 dropping tool,应核对工具参数 schema 的 HTTP 头标注,而不是先怀疑工具分页。
这个问题已有 Python SDK 2.1.1、2.2.0 的反馈。旧 MCPServer 接受了无效标注,客户端按 2026-07-28 协议处理时却会丢弃工具。已核对的正式版 2.3.0 把相应检查提前到由函数创建工具的注册路径。
标注的是参数,不是任意 header
x-mcp-header 放在工具输入 schema 的参数上。支持它的 Streamable HTTP 客户端会把参数值同时放进 Mcp-Param-* HTTP 头,服务端再核对 header 与正文是否一致;工具函数仍接收普通参数。
只有 schema 中明确的 string、integer 和 boolean 类型可作这类标注。给 list[str]、对象或 float 加标注,不能靠字符串化让它自动符合规则。header 名也须符合 token 形状,不同参数不能使用仅大小写不同的重复名字。
排查时比较服务端实际提供的 inputSchema 与客户端保留的工具列表,查看生成后的 schema,不能只看 Python 注解。str | None、Enum 或嵌套模型可能生成 anyOf、$ref 等形状,不能默认它们等同于一层明确的 string 参数。
先决定参数是否真的需要路由头
数组或复杂对象若只是业务输入,应保留在正文,移除不适用的 x-mcp-header。不要为了让工具出现就把业务对象随意改成字符串,也不要让客户端跳过无效标注检查。
确实需要给可选字符串参数标注 header 时,2.3.0 文档提供了明确 schema 的写法:
from typing import Annotated
from pydantic import WithJsonSchema
region: Annotated[
str | None,
WithJsonSchema({"type": "string", "x-mcp-header": "Region"}),
] = None
这里只展示处理函数的参数声明,应放进已有工具函数签名;它不是完整服务程序。明确 schema 后,仍要核对实际业务值与网关的路由契约,不能把允许省略参数理解为允许伪造任意 region。
新版可能在启动时就拒绝注册
2.3.0 的 Tool.from_function 对生成的 schema 运行同一套标注检查。无效时抛 InvalidSignature,包括工具名和具体原因。这覆盖 @server.tool()、MCPServer.add_tool() 以及经过 Tool.from_function 构造的工具。
升级后从“客户端看不到工具”变成“注册时启动失败”,可能正是检查提前了,应修正参数声明,而不是恢复旧版的静默行为。它没有新增 McpHeader(...) 这样的正式标记接口。
手工创建 Tool(parameters=...) 或由低层 Server 返回工具列表,会绕过这条函数构造路径,仍需自己核对 schema。不能仅凭安装了 2.3.0,就认定每种注册入口都会拒绝无效标注。
核对列表,也核对真正的调用
在获准的测试环境中,确认修正后的工具进入实际客户端列表,header 与正文中的参数相符时调用成功,不相符时仍被拒绝。需要兼容旧协议或其他 transport 时,也应按它们自己的行为检查;这类标注在其他连接上不会自动带来同样的 HTTP 头传输。
如果 schema 中没有无效标注,客户端仍只看到第一页,可另按nextCursor 的分页检查处理。工具被 schema 过滤与成功拿到一页后没有继续请求,是两条不同路径。


