MCP 经中转站:哪些能通、哪些一定不通,以及怎么在十分钟内测出来
先把问题问对
「你们家支持 MCP 吗?」——这是我在选型群里见过最多、也最容易得到错误答案的问题。
原因很简单:MCP(Model Context Protocol)是「客户端 ↔ 工具服务器」之间的协议,它不经过模型 API。
一次带工具的对话,实际发生的是三件事:
- 客户端把「有哪些工具可用」写进请求的
tools字段,发给模型 API; - 模型返回
tool_calls,说明它想调用哪个工具、参数是什么; - 客户端自己去执行这个工具(可能是本地子进程,也可能是远程的 MCP 服务器),把结果作为一条
role: tool的消息再发回模型。
MCP 规范里的两种传输层 —— stdio 和 Streamable HTTP —— 描述的都是第 3 步中「客户端怎么和工具服务器说话」,模型 API 全程不参与。
所以:中转站无法「支持」或「不支持」MCP,它只能支持或不支持 tool use。 一家站说自己「支持 MCP」,通常只是说「我们的模型能返回格式正确的 tool_calls」。这句话本身没有信息量,因为格式正确是及格线,不是能力。
真正会卡住你的三件事
把「支持 MCP」翻译成可验证的问题,只剩三条:
一、多轮往返
工具调用的本质是多轮:模型发起调用 → 客户端执行 → 结果回传 → 模型继续。很多网关只保证第一轮正确。
典型失效:第二轮请求带着 role: tool 的消息回去,网关的协议转换层不认识这个 role,直接 400 或静默丢弃,模型于是「忘记自己调用过工具」,开始编造结果。
这一类在 Anthropic 与 OpenAI 之间做协议转换的网关上最常见,因为两家的工具消息结构不同:OpenAI 是 assistant.tool_calls 加独立的 role: tool 消息,Anthropic 是 assistant 内容块里的 tool_use 加 user 内容块里的 tool_result。转换器漏掉一个 tool_use_id 的映射,整条链路就断在这里。
二、流式下的参数分片
tool_calls[].function.arguments 是一个 JSON 字符串,而在流式响应里它是分片下发的 —— 你会收到一串 arguments 的片段,需要自己按 index 拼起来再解析。
问题在于:有些网关会「帮你聚合」,把分片拼成一个完整的 arguments 再下发,看起来更友好。但只要聚合逻辑有 bug(比如把多个并行调用的分片混进同一个 index),客户端拿到的就是一个语法非法的 JSON,报错信息往往只是 Expecting ',' delimiter,和网络毫无关系,非常难查。
判据:流式下发起一次工具调用,把收到的每个 chunk 原样打印出来。你要看到的是「多个 delta 片段」或「一个完整字符串」,不能是「拼了一半的字符串」。后者说明聚合器有截断。
三、并行工具调用
用户问「北京和上海今天多少度」,模型应该一次返回两个 tool_calls。这要求网关支持 parallel_tool_calls,并且能在下一轮把两个 tool 结果都正确配对回去。
很多网关只处理 tool_calls[0]。表现是:模型明明要查两个城市,你只收到一个调用;或者两个结果都回传了,模型只认了其中一个。
十分钟测法
不用装任何 MCP 服务器,两个假工具就够了:
tools = [
{"type": "function", "function": {
"name": "get_weather",
"description": "Get current weather for a city",
"parameters": {"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"]}}},
{"type": "function", "function": {
"name": "get_time",
"description": "Get current time for a city",
"parameters": {"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"]}}}
]
三轮测试,每轮记一个结论:
| 轮次 | 你发什么 | 看什么 | 不通过说明 |
|---|---|---|---|
| 1 | 「北京天气怎么样」,带上面 2 个工具 | tool_calls 里是否有 get_weather,arguments 能否 json.loads | 模型不发工具,说明工具能力没接上 |
| 2 | 把 role: tool 的结果回传 | 模型是否基于结果作答,而不是重发调用 | 400 或重复调用,说明多轮往返断了 |
| 3 | 「北京和上海分别多少度」 | 是否一次返回两个 tool_calls | 只回一个,说明并行调用不支持 |
三轮全过,MCP 就能正常跑。任何一轮不过,换站比调代码快。
报错对照
看到下面这几条,基本可以确定是网关的问题,不是你的代码:
tool_use ids were found without tool_result blocks—— 结果回传时 id 没对上,Anthropic 风格报错,多数是网关改写 id 后没同步;messages.1.content: unexpected tool_use_id—— 同上,OpenAI 风格;Invalid schema for function—— 网关对parameters做了二次校验,且只认 JSON Schema 的一个子集。把$ref、oneOf去掉再试,能过就是它的问题;- 工具调用完全正常,但流式下报
Expecting ',' delimiter—— 参数分片聚合 bug,见上文第二条。
一个容易忽略的边界
如果你的工具是远程 MCP 服务器(Streamable HTTP),那么客户端需要同时连两个地方:模型 API(中转站)和 MCP 服务器(多半是你自己的云主机或第三方服务)。
这意味着:
- 中转站挂了不影响 MCP 服务器,但整个链路一样不可用;
- 反过来,MCP 服务器在你的内网、不可公网访问时,只能走 stdio 模式(本地起进程),这时候根本没有网络协议的问题;
- 客户端所在机器的出网策略会同时影响这两条连接,排查时必须分开测。
先把两条连接分开验证:先确认模型 API 能返回合法 tool_calls,再确认 MCP 服务器能被客户端连上。混在一起测,永远说不清是谁的问题。
小结
- MCP 不经过模型 API,所以「中转站支不支持 MCP」是伪问题,真正要问的是 tool use 的三项能力;
- 三项能力按重要性排序:多轮往返 > 参数分片 > 并行调用;
- 用两个假工具、三轮对话、十分钟即可测完,不需要真的搭一套 MCP;
- 报错里出现
tool_use_id、tool_result、Expecting ',' delimiter,先怀疑网关的协议转换层; - 远程 MCP 的两条连接必须分开验证,否则排查方向一定是错的。
这篇文章对你有帮助吗?如果你在实际使用中发现了文中没提到的情况,欢迎 提交纠错或补充,我们会核验后更新到正文里。
相关文章
顺着看下去:本站收录的部分中转站
本文没有指向特定模型系列,下面是本站收录站点中综合评分靠前的几家,供你顺着看下去。排序依据是评分与可用率,不构成推荐。