跳到正文
Hacker News· stag·· 8 天前AI 评分66

Claude Code 与 Codex 在十个 MCP 特性上各挂一半

Claude Code and Codex break on different MCP features

AI 导读

作者用开源工具 M3 在 2026 年 10 月 9 日对 Claude Code 2.1.287(claude-sonnet-5-5)和 Codex 0.162.0(gpt-5.6-sol)各跑三轮十个 MCP 特性,结果五个特性只在其中一个智能体上失败,且两者失败的特性完全不重叠。

正文 · AI 翻译

我们让 Claude Code 和 Codex 分别对接同样的十个 MCP 服务器。十个功能中有五个在一个 agent 上正常、在另一个上失败,而且这两个 agent 从未在同一个功能上失败。

如果你发布了一个 MCP 服务器,你大概见过这个问题:“工具在 Claude Code 里不显示”或者“Codex 发送了错误的参数”。我们刚开始时,各个 agent 仓库里大约有 28 个这类未解决的 issue。它们都没有说明是哪个 agent、在哪个版本上、在哪个 MCP 功能上出问题。

所以我们做了一张能说明这些的表。我们称之为 Harness Quirks Matrix,它运行在 M3 上,这是我们开源的、用于测试 MCP 服务器及使用它们的 agent 的工具。

结果:10 个 MCP 功能中有 5 个让两个 agent 表现分化

2026 年 10 月 9 日,我们针对 Claude Code 2.1.287(claude-sonnet-5-5)和 Codex 0.162.0(gpt-5.6-sol)运行了十个 MCP 功能,每个三次试验。Claude Code 在三个功能上失败,Codex 在另外两个上失败。

MCP 功能Claude Code 2.1.287Codex 0.162.0
名为 ids[] 的工具参数被丢弃0/3正常3/3
12,000 字符工具错误中间的标记结果丢失0/3正常3/3
同时包含文本和 structuredContent 的结果结果丢失0/3正常3/3
工具列表变更后新增的工具正常3/3被丢弃0/3
14 KB 输入 schema 中的必需嵌套字段正常3/3被丢弃0/3
inputSchema 中的根级 anyOf正常3/3正常3/3
outputSchema 声明 draft-07正常3/3正常3/3
省略有默认值的可选参数正常3/3正常3/3
全名长度为 65 个字符的工具正常3/3正常3/3
结果中的 resource_link 块正常3/3正常3/3
2026 年 10 月 9 日用 M3 测量。每个单元格三次试验;只有当调用以预期参数到达服务器、且结果到达答案时,该单元格才算通过。

如果你维护一个 MCP 服务器,以下是这五个失败的含义。

Claude Code 从不调用输入属性名为 ids[] 的工具。它不显示任何错误,工具就是不存在。OpenAPI 生成的服务器经常产生这样的名称。

Claude Code 还只保留工具错误的大约前 5,000 个和后 5,000 个字符,所以中间的任何内容都到不了模型。把代码、原因和下一步放在错误消息的开头。而且当结果同时带有 content 和 structuredContent 时,模型报告了结构化值,丢失了文本值。

在服务器宣布新工具后,Codex 仍继续使用旧目录,所以它从未调用新工具。它还会压缩大型输入 schema 以符合预算,在我们 14 KB 的 schema 中,必需的嵌套字段随之被压缩掉了。把 Codex 的每服务器 schema 预算提高到 20,000 后,调用恢复了。

每个结果覆盖固定版本下的一个客户端和模型组合,使用明确的提示词。失败的单元格告诉你数据没有从服务器到达模型的答案;它不告诉你哪一层丢弃了它。每个单元格都随附其运行清单、二进制 SHA-256 和线上追踪记录。

直接探测发现的另外八个失败

我们还直接探测了 Claude Code 和 Codex 的二进制文件(未使用 M3),又发现了八个功能,它们至少在其中一个上会失败。这些功能都尚未经过 M3 验证,因此在验证之前只能视为线索。

MCP 功能Claude Code 2.1.295Codex 0.162.0
根 oneOf 紧邻 properties正常属性名丢失
单个服务器上有 2,049 个工具正常超过 2,048 的工具被丢弃
一个工具没有根 type整个服务器被丢弃正常
int64 参数 1234567890123456789被舍入转为字符串或被舍入
anyOf 内的枚举,schema 超过 5 KB正常枚举被抹除
prefixItems 元组正常被声明为 Array<string>
同一服务器上的 fetch.page 和 fetch_page一个被错误路由正常
声明了 outputSchema,但结果仅为文本成功被转为错误正常
直接探测各 agent 二进制文件,未使用 M3。尚未在 M3 下测量。

这些探测也排除了一些旧嫌疑。Claude Code 的根 anyOf 和 65 字符名称问题在当前版本下不再复现,Codex 的 resource_link 处理也不再复现,因此服务器作者可以去掉针对这些问题的变通方案。

M3 如何运行每个单元格

每个单元格都是一个普通的 pytest 测试,M3 针对真实 agent 二进制文件、在精确版本下运行它,同时记录线上传输的内容。以下是 ids[] 用例的完整测试:

@pytest.mark.m3(suite_name="quirks")
def test_q05_args_property_name_brackets(agent) -> None:
    state = run_eval(agent, Path(__file__).resolve().parent)
    assert state == "works", state

run_eval 调用 agent.run(prompt, servers=(control, quirk)),然后用 M3 的 expect(result).to_have_tool_call(...) 对照线上证据检查结果。agent fixture 来自命令行:

m3 test --runtime=managed \
  --harness [email protected]=claude-sonnet-5-5 \
  --harness [email protected]=gpt-5.6-sol \
  --trials 3 -- quirks/

有几点保证了单元格的可靠性:

  1. M3 将每个 agent 版本下载到缓存中,并记录其版本和 SHA-256,因此新版本会有自己的列。
  2. M3 分别记录 agent 是否报告了工具调用,以及服务器是否收到了该调用。上述大多数失败都发生在这两点之间。
  3. 每次运行都会在待测服务器旁边放一个普通的 echo 服务器。如果 echo 失败,该单元格会被标记为未测量,这样损坏的环境就不会被误报为 agent 的 bug。
  4. 服务器为每次试验生成一个随机标记,因此模型无法从提示中得出答案。
  5. 我们会针对同一二进制文件、在不使用 M3 的情况下重新运行每个失败用例,以检查 M3 本身不是原因。
  6. 表格仅从存储的文件渲染,重新渲染会得到相同的字节。

完整运行共执行 66 次,耗时约 16 分钟。Claude Code 平均每次执行 9.8 秒;Codex 平均 18.2 秒。

全部二十个测试都在 M3 仓库的 benchmarks/harness-quirks 下,并附有自行运行它们的命令。

让我们意外的地方

我们的第一版什么都没发现。我们从公开的 bug 报告中选取了五个功能开始,36 次执行在两个 agent 上都通过了。那些 bug 要么已被修复,要么只出现在我们未测试的环境中,这就是我们回过头来直接探测二进制文件的原因。

提示中的一句话让每次 Codex 运行都失败。我们最初的提示以“如果某个工具不可用,请说明是哪一个并停止”结尾。Codex 将此视为放弃的理由,每个单元格都返回 control failed。如果没有对照服务器,我们就会把它当作 Codex 的 bug 发布出去。

使用 gpt-5.6-sol 时,Codex 以代码模式运行,因此模型永远看不到你的 JSON Schema。它得到的是由此生成的压缩 TypeScript 声明,本文中若干 Codex 失败就发生在这个转换过程中。

在每个 agent 上测试你自己的 MCP 服务器

这个矩阵只是 M3 测试,你可以用大约十行为自己的服务器编写同样的测试。安装 CLI 并搭建一个项目:

uv tool install sf-m3-cli
m3 init      # a skipped starter test and an .env.example
m3 setup

把你关心的行为写成一个普通的 pytest 测试:

import pytest
from m3 import expect

@pytest.mark.m3
def test_shipping(agent, shipping_server):
    result = agent.run("Get a local shipping quote", server=shipping_server)
    expect(result).to_have_tool_call("shipping_quote", server=shipping_server.name,
                                     status="success")

然后针对任意数量的 agent 和版本运行它:

m3 test --runtime=managed \
  --harness [email protected]=claude-sonnet-5-5 \
  --harness [email protected]=gpt-5.6-sol \
  --trials 3 -- tests/test_shipping.py

M3 内置了 Claude Code、Codex、OpenCode 和 Pi,任何支持 ACP 的 agent 都可以通过一个小型 manifest 添加。运行结果会保存在本地,并可与保存的基线进行比较,因此当新的 agent 版本破坏你的服务器时,你会在用户看到 bug 之前先看到一个失败的测试。要将其变成 pull request 检查,请参阅在 CI 中为 MCP 服务器设置门禁。

M3 是基于 Apache 2.0 的开源项目:github.com/sineframe/m3。请从文档开始。如果你的服务器有某个特性需要我们加入矩阵,请在那里提交 issue。

来源:Hacker News · m3.sineframe.com