AIGC标识 为什么 REA MCP 必须按项目安装:一次 30 万 token 的上下文税

为什么 REA MCP 必须按项目安装:一次 30 万 token 的上下文税

最近我在一个和逆向工程毫无关系的 Flutter 项目里新建 Claude Code 会话。还没开始干活,第一轮请求的 input_tokens 就是 300,574。

我一度以为是项目上下文太大。这个项目有 44 个会话,我抽查的首轮值普遍在 29.3 万–30.1 万;/context 里最大的一块也不是代码,而是 MCP 工具定义。罪魁祸首,是我为了图方便全局安装的 REA MCP。

这篇文章把账算清楚:REA 到底占了多少、为什么全局安装会让每个项目一起买单,以及怎么改成项目级安装。如果你也在 Claude Code 里装过重型 MCP server,这套方法同样适用。

一、先看现象:什么都没做,30 万 token 没了

Claude Code 会把每一轮请求的 usage 写进会话 JSONL。取第一条 assistant 消息:

jq -r 'select(.type=="assistant" and .message.usage) | .message.usage.input_tokens' \
  ~/.claude/projects/<project>/<session>.jsonl | head -1

我的结果是 300574。这不是估算,是请求实际携带的输入 token。

30 万意味着什么:

  • 对 200K 上下文窗口的模型,首轮就装不下;
  • 对 1M 窗口的模型,30% 的预算在开工前已经花掉;
  • 每一轮对话都会重复携带这批工具定义。Prompt caching 能降低重复计费,但不会减少窗口占用,也不会让首轮变小。

更麻烦的是,这个成本与项目内容无关。一个纯前端项目、一个数据分析脚本、一个只改文档的仓库,只要用了同一份全局配置,首轮都要付这笔钱。

二、根因:四个因素叠在一起

2.1 全局安装,等于所有项目共享

REA 的安装命令 rea mcp add 默认写入 user scope,也就是 ~/.claude.json 顶层的 mcpServers。它的帮助里写得很清楚:要项目级安装,需要加 --no-global。

user-scope server 会被所有项目加载,而且没有项目级豁免。disabledMcpjsonServers 只对项目 .mcp.json 里的 server 生效,管不到全局 server。你无法在某个项目里单独说“这次不要 REA”。

于是结果就是:项目里一行逆向代码都没有,207 个工具照样跟着启动。

2.2 REA 的工具面确实很大:207 个工具,3.09 MB schema

用 REA 自带的诊断命令看工具清单:

rea mcp doctor --json | jq '.checks[] | select(.name=="inventory:tools")'
# expected=207 observed=207 missing=none unexpected=none duplicates=none

207 个工具,默认 tools/list 序列化后是 3,087,952 字节。拆开看:

组成部分 字节数 占比
输出 schema(outputSchema) 2,264,286 73.3%
输入 schema(inputSchema) 688,973 22.3%
工具描述(description) 90,150 2.9%
其他(名称、注解等) 44,543 1.4%
合计 3,087,952 100%

体积最大的几个工具:

工具 序列化字节
compare_web_captures 169,765
inspect_analysis_view 145,262
compare_application_versions 60,096
capture_browser_scenario 51,876
reconcile_javascript_runtime 45,186
trace_application_feature 44,818

这不是说 REA 写得臃肿。它面对的是二进制、托管代码、Electron、浏览器抓包等复杂目标,必须把每个工具的输入输出契约写清楚,模型才能正确调用。在需要它的项目里,这是有价值的信息;在不需要它的项目里,这是纯成本。

2.3 Claude Code 默认会把 schema 内联进请求

MCP 工具定义不是在磁盘上放着不动的插件,它们会进入 API 请求的 tools 数组。Claude Code 提供了 tool search / deferred loading:当条件满足时,工具 schema 可以延迟加载,只在真正需要时进入上下文。server 侧也可以用 alwaysLoad: false 强制走 deferred。

但 tool search 有门控条件:需要 Anthropic first-party endpoint、受支持的模型,并且没有禁用相关实验性 beta。非 first-party endpoint 默认关闭。

我的 Claude Code 通过第三方兼容 endpoint 接入模型,ENABLE_TOOL_SEARCH 也没有显式设置,所以 tool search 没有生效,207 个 schema 全部内联。3 MB 的 JSON 经过 tokenizer,最终表现为首轮约 30 万 token 的输入。

这就是放大器:工具数量 × 每个工具的完整契约 × 每个会话。

2.4 每个会话还会各起一个 REA 进程

同一时间,我在机器上看到 10 多个 rea mcp 子进程,每个 Claude Code 会话一个。内存开销不是主因,但它说明这笔成本是乘法:会话越多,工具定义和进程一起翻倍。

三、所以,REA 应该按项目安装

把 MCP server 当成项目依赖,而不是全局常驻服务,理由很直接:

  1. 上下文窗口是每次会话的预算。工具定义是常驻开销,不是用完即走的磁盘文件。
  2. 大多数项目不需要逆向工程工具。让它们为 207 个工具付费,没有收益。
  3. 工具越多,选择噪声越大。不相干的工具会稀释模型对当前任务的注意力。
  4. 项目级 .mcp.json 可审查、可版本化。谁需要,谁在仓库里声明;Code Review 时也看得见。

这不是功能倒退,是把“安装”从一次性动作变成可管理的依赖。

四、具体怎么做

4.1 移除全局 REA

先确认它确实在 user scope:

claude mcp list
claude mcp get rea

然后移除:

claude mcp remove rea -s user

-s user 是明确作用域。删除后重启 Claude Code,新的会话才不会继续加载。

4.2 在真正需要的项目里安装

在项目根目录放一个 .mcp.json:

{
  "mcpServers": {
    "rea": {
      "type": "stdio",
      "command": "/path/to/rea",
      "args": ["mcp"]
    }
  }
}

或者用命令写入当前项目:

claude mcp add -s local rea -- /path/to/rea mcp

-s local 只对当前项目生效;-s project 会写入 .mcp.json,可以随仓库共享。首次打开项目时,Claude Code 会要求批准 .mcp.json 里的 server,批准后才会连接。

如果你在 REA 仓库里工作,项目级的 .mcp.json 本来就是最合适的位置;其他项目不再受影响。

4.3 如果确实需要全局:至少裁剪 schema

有些团队会希望 REA 在所有项目可用。那就别用默认的 full/full,REA 提供了两个裁剪开关:

{
  "mcpServers": {
    "rea": {
      "type": "stdio",
      "command": "/path/to/rea",
      "args": ["mcp"],
      "env": {
        "REA_MCP_OUTPUT_SCHEMA_PROFILE": "off",
        "REA_MCP_INPUT_SCHEMA_PROFILE": "compact"
      }
    }
  }
}

同一份 tools/list 的实测对比:

配置 JSON 字节 相对默认
默认 full/full 3,087,952 100%
REA_MCP_OUTPUT_SCHEMA_PROFILE=off 820,354 -73%
REA_MCP_INPUT_SCHEMA_PROFILE=compact 2,482,411 -20%
两者同时 214,813 -93%

两个开关同时开,工具定义从 3.09 MB 降到约 0.21 MB,减少 93%。

代价要说清楚:

  • output=off 不再向模型广告输出 schema。REA 仍会在服务端按契约校验结果并返回 structured content,但模型少了一份“返回长什么样”的说明,复杂工具的调用可能需要更多试错。
  • input=compact 去掉嵌套的注解性描述,保留类型、必填项和枚举等结构信息。对大多数调用够用,但对特别复杂的工具会损失一部分提示。

4.4 有条件的话,用 tool search 延迟加载

在支持的环境中,可以开启 tool search:

{
  "env": {
    "ENABLE_TOOL_SEARCH": "true"
  }
}

同时在 REA 的 server 配置里显式声明:

"alwaysLoad": false

然后重启会话,用 /context 确认出现 MCP tools (deferred),而不是把全部 schema 计入已用 token。

注意:这条路径依赖 Anthropic first-party endpoint 和受支持的模型。第三方兼容 endpoint 下可能默认不可用,显式开启也未必生效。以 /context 的实际结果为准,不要只看配置。

4.5 确定不需要 MCP 的会话

如果某个项目完全不需要任何 MCP server,可以启动时直接隔离:

claude --strict-mcp-config --mcp-config '{"mcpServers":{}}'

这会忽略用户、项目、本地所有 MCP 配置,只使用传入的空配置。适合 CI、脚本化任务或确定不需要 MCP 的会话。

4.6 另一种做法:全局安装 + 按项目禁用

如果你希望只维护一份全局配置,又不想让每个项目都加载 REA,Claude Code 提供了项目级开关:

/mcp disable rea   # 在当前项目禁用
/mcp enable rea    # 需要时恢复

在不需要 REA 的项目里执行一次 /mcp disable rea,Claude Code 会把 rea 写入该项目在 ~/.claude.json 中的 disabledMcpServers,并在该项目不再加载它。需要恢复时执行 /mcp enable rea。

这是 opt-out 模型:新项目默认启用,你必须记得在每个不需要它的项目里禁用。禁用状态保存在本机的 ~/.claude.json,不随仓库共享;换一台机器或换一个项目路径,需要重新设置。

三种“按项目生效”的方式对比:

方式 配置位置 默认行为 团队共享 适用场景
user scope + /mcp disable ~/.claude.json 顶层 + 项目条目的 disabledMcpServers 所有项目默认启用(opt-out) 否 一份全局配置,项目数量少且固定
local scope(claude mcp add -s local) ~/.claude.json 项目条目的 mcpServers 仅添加的项目启用(opt-in) 否 单机、多项目,按项目添加
project scope(.mcp.json) 项目仓库的 .mcp.json 仅该项目启用(opt-in) 是 团队协作、需要版本化

两个容易混淆的细节:

  • disabledMcpjsonServers 是给 .mcp.json server 用的,配置在 .claude/settings.local.json;disabledMcpServers 是给 user/local scope server 用的,配置在 ~/.claude.json 的项目条目中。两者不要混用。
  • 不要用 claude mcp remove 来“禁用”:它会永久删除 server 配置(env、headers)并清掉 OAuth token。/mcp disable 才是可逆的项目级开关。

如果你的目标是“默认不让其他项目付 30 万 token”,user scope + 手动 disable 仍然把责任放在每个项目的操作者身上;local/project scope 的 opt-in 模型更稳。全局安装更适合你愿意配合 /mcp disable,或者同时用 4.3 的 schema 裁剪来降低启用项目的成本。

五、验证:别猜,量一下

配置改动不会影响已经启动的会话。工具列表在会话启动时确定,必须重启才会释放。

重启后:

  1. 运行 /context,看 MCP tools 和 MCP tools (deferred) 两类各占多少。
  2. 取新会话第一条 assistant 消息的 usage.input_tokens,和之前的 300,574 对比。
  3. 用 rea mcp doctor --json 确认工具数量(当前是 207)。
  4. 如果保留了全局 REA,再用 4.3 的配置复测 tools/list 字节数。

一个容易踩的坑:MAX_MCP_OUTPUT_TOKENS 管的是工具输出的截断,不是工具定义注入。调它解决不了首轮 30 万 token 的问题。

六、结论

REA 是一个重型逆向工程工具箱。它在需要它的项目里很有价值,但不应该成为每个项目的默认背景。

这次排查让我把 MCP server 重新归类了:它不是“装一次、到处可用”的便利工具,而是每次请求都要携带的上下文依赖。依赖应该按项目声明、按需加载、定期测量。

上下文窗口是新的内存。不要把所有的工具都常驻。


文中字节数为 UTF-8 JSON 序列化后的长度;token 数来自 Claude Code 会话的 usage 记录。不同 Claude Code 版本、模型和 tokenizer 会得到不同数值,建议在自己的环境里用 /context 复测。

posted @ 2026-10-22 10:47  痴人指路  阅读(46)  评论(0)    收藏  举报