首页
/ Caveman MCP Server 深度解析:stdio JSON-RPC 适配器的五个压缩工具、不可杀传输与 Anti-Storm 恢复机制

Caveman MCP Server 深度解析:stdio JSON-RPC 适配器的五个压缩工具、不可杀传输与 Anti-Storm 恢复机制

2026-09-04 13:54:24作者:姚月梅Lane

Caveman 的 MCP 服务(mcp/)是一个“薄层 stdio JSON-RPC 适配器”:它把压缩引擎以五个 MCP 工具暴露给任意 MCP 宿主(Claude Code、Cursor 等),自己只负责 MCP 协议封装(framing),所有压缩能力来自进程内直连的 Caveman Engine。读完本文,你将掌握这套服务端的完整目录结构、五个工具(caveman_compresscaveman_retrievecaveman_statscaveman_toon_encodecaveman_toon_decode)的精确语义与失败行为、共享 CCR 恢复存储的打开方式、工具注册带来的前缀 token 成本与 execute.mcp 开关、以及“不可杀传输”与 retrieve 反风暴两条核心工程不变量。

一、定位:本地-only、inferred-only 的协议薄层

mcp/CLAUDE.md 对该模块的定义非常明确:

  • 只拥有 MCP framinginitializetools/listtools/call、通知(notification)的封装与分发,属于适配器的职责;
  • 压缩全部属于引擎,且是 in-process 链接(无子进程、无行为漂移);
  • 本地-only:服务端不打开任何网络连接;
  • inferred-only:它报告的一切数据都是 inferred(推断值),从不出现 verified(已验证)。

这一“薄层”定位在 mcp/README.md 中同样被强调:五个工具、stdio 传输、任意 MCP 宿主可用、进程内链接引擎。这种分层让协议层(server.go)与压缩逻辑(engine/)可以独立演进和测试——Server 接收一个可注入的 Engine 接口,测试可以注入 mock,无需真实压缩器即可验证协议行为(见 mcp/engine_tools.go#L13-L23Engine 接口定义)。

二、模块布局:四个部分各管一段

文档给出的布局(Layout)如下,每一项都可以直接对应到仓库中的真实文件:

路径 职责
mcp/server.go Server 本体:JSON-RPC 循环、分发(dispatch)、五个工具处理器。接收可注入的 Engine 接口,使协议层可脱离真实压缩器测试
mcp/protocol.go JSON-RPC 与 MCP tool-result 类型、toolText/toolError 辅助函数、tools/list 的精确工具定义
mcp/cmd/caveman-mcp/ 二进制入口:打开共享文件 CCR store(CAVEMAN_CCR_DB,否则 CAVEMAN_HOME/ccr.db / ~/.caveman/ccr.db),然后服务 stdin↔stdout
bin/caveman-mcp.mjs + mcp/package.json npx caveman-mcp 启动器,exec 预编译好的 Go 二进制

2.1 二进制入口:先打开共享恢复存储,再进入协议循环

mcp/cmd/caveman-mcp/main.gomain() 流程是:处理 version --json 参数 → 用 slog 建一个写 stderr 的 logger → 打开恢复存储 → engine.New(store, nil)mcp.NewServerVersion("caveman", version, mcp.EngineTools(eng, logger), logger)srv.Serve(os.Stdin, os.Stdout)

存储打开逻辑(openRecoveryStore()mcp/cmd/caveman-mcp/main.go#L70-L87):

  1. CAVEMAN_MCP_EPHEMERAL=1 → 内存存储(ccr.OpenMemory()),不落盘,供测试与不需要跨进程恢复的会话使用;
  2. 否则取 CAVEMAN_CCR_DB 作为库路径;
  3. 否则取 CAVEMAN_HOME(默认 ~/.caveman)下的 ccr.db
  4. 若连主目录都取不到,回退为内存存储。

默认走共享文件存储的原因在源码注释中写得很直白:这与 Caveman 网关(proxy)写的是同一个 store,因此这里 caveman_retrieve 能解析出 proxy 披露过的 handle——这正是“被包装的 agent 能在流式请求中恢复 proxy 删减掉的细节”的机制基础。

version --json 输出(handleArgs)是一个带 schema: "caveman.mcp.version.v1" 的 JSON,capabilities 包含 mcp_recoverybuild_stamped_version。使用 NewServerVersion 而非 NewServer 的动机是:initialize 响应中的版本必须与构建时戳入的版本一致,避免兼容性信号漂移(见 mcp/server.go#L78-L81 的注释)。

2.2 npm 启动器与许可边界

npm 包 mcp/package.jsoncaveman-mcp v1.0.0,MIT,type: modulebin 指向 bin/caveman-mcp.mjs)是纯启动器:首次运行时下载与平台匹配的 BSL-1.1 二进制,校验密钥签名的 checksum 清单与制品 SHA-256,缓存到 ~/.caveman/bin。无需 Go 工具链,也无需全局安装 Caveman。若要使用已审查的本地二进制,可用 CAVEMAN_MCP_BIN=/path/to/caveman-mcp npx caveman-mcp。MIT 只适用于 npm 启动器部分;下载的二进制遵循 mcp/BINARY_LICENSE.md 中声明的 BSL-1.1 条款——这也是标题里“commercial Go core + MIT launcher”的含义。

三、五个工具:精确名称、精确语义、精确失败行为

工具名大小写敏感,恰好这五个(见 mcp/engine_tools.go#L25-L33 的常量定义):

工具 输入 返回
caveman_compress input(string) 压缩文本、推断 ratiorecovery_handle(直通时为 null)
caveman_retrieve recovery_handle(string) 字节级精确的原始内容;未知 handle 报错
caveman_stats 会话总量:前后 token、ratiobasis:"inferred"scope:"session"
caveman_toon_encode input(JSON 字符串) 显式 JSON→TOON 结果及两侧尺寸;不可编码时直通加说明
caveman_toon_decode input(TOON 字符串) 解码出的 JSON;非法 TOON 报错

3.1 caveman_compress:fail-open 的有损压缩

有损(S4)但可恢复;不可压缩、格式错误或压缩后不更小的输入原样返回ratio:0recovery_handle:null——永不报错

处理器实现(compressToolmcp/engine_tools.go#L118-L143)值得注意的细节:

  • 入参支持 content_typetype 两个字段(互为别名),缺省则交由引擎自行探测类型;
  • 引擎返回 error 时并不直接失败:若引擎已返回“完整记账的字节精确直通结果”,则保留该结果;只有当第三方 Engine 实现返回了不安全/空结果时才以 cave_compress_failed 大声失败,避免“非空内容看起来零成本”的假象。

返回结构 compressPayloadmcp/engine_tools.go#L94-L104)包含 compressedratiotokens_beforetokens_afterbasiscontent_typerecovery_handle(可空指针,直通时序列化为 null)、method 与可选的 lossless_to_model

3.2 caveman_retrieve:最后手段,不是分页 API

工具描述本身(mcp/engine_tools.go#L57)是一段写给模型的“行为引导”:删除标记(elision marker)陈述的是从被替换的精确单元计算出的事实(如 “… 340 rows elided (caveman): all state=charged; range amount=5.00..199.99 …”),且每个被丢单元都与仍可见的单元相似——因此计数、求和、按字段提问通常可以直接从可见视图加这些不变量回答,根本不需要调用。只有拿不到无法推导的精确字节时,才对每个 handle 做一次宽 query 调用。

实现侧(retrieveToolmcp/engine_tools.go#L168-L206):

  • handle 归一化normalizeRecoveryHandlemcp/engine_tools.go#L208-L233)接受该栈历史上向 agent 展示过的所有形态:裸 ccr_…、嵌入压缩内容的标记 <<ccr:ccr_…>>、半剥掉的 ccr:ccr_…、以及 native runtime 工具输出掩码 ccr://…。源码注释指出,缺了 ccr:// 这一形态曾导致某些基准任务无法回答——agent 看到了恢复工具拒绝解析的引用;
  • 空 query 是字节精确的全量恢复,非空 query 经 BM25 收窄到相关段落,且只返回完整记录、从不抽行,并在跳过的位置打印 “… [caveman: non-adjacent] …” 标记;
  • 未知 handle 返回 fail-closed 工具错误(cave_unknown_handle)并携带 cave_snake_code绝不伪造 payload

3.3 caveman_stats:字符串 "verified" 永不出现

statsToolmcp/engine_tools.go#L291-L306)固定填 Basis: engine.BasisInferredScope: "session",对应文档不变量:basis:"inferred"scope:"session",字符串 verified 永不出现。会话不可用时返回 cave_stats_unavailable 工具错误。

3.4 TOON 双向:显式请求才编码,失败要大声

  • caveman_toon_encodetoonEncodeToolmcp/engine_tools.go#L248-L272):显式 JSON→TOON 重编码,返回 outputencodedinput_bytesoutput_bytes。注意它不是 proxy 的 best-of 门:即使编码结果不更小也照样返回(两侧尺寸都给出,由 agent 自己决定)。无法编码的输入原样返回并带 note: "not encoded: …"——从不静默 no-op,也从不编造编码;
  • caveman_toon_decodetoonDecodeToolmcp/engine_tools.go#L277-L289):TOON→JSON;非法 TOON 返回 cave_invalid_toon 工具错误,绝不把原始输入当作 JSON 吐出(与 CLI decode 动词同一不变量)。

工具级错误的载体是 ToolErrormcp/protocol.go#L83-L88):isError: true 加一段 {"error": <cave_snake_code>, "message": …} 文本,保证宿主不会把它误认为成功 payload。

四、前缀成本:这五个工具是“按调用收费”的,并且被度量过

文档专门用一节量化了工具注册的代价:注册这个 server 会把五个工具的 schema 放进被包装 agent 每次调用的 prefix 里——agent 基准测试中为 11,060 tokens/call(其 201 次调用累计约 2.22M token)。因此 caveman wrap 把注入放在 execute.mcp 这个 surface 开关之后(取值 auto | marker-only | true | false,见 packages/cli/src/index.ts):

  • 非 auto 的 surface 下,wrap 抑制它的两处注入点——mcp install 写入,以及 openclaw 等配置文件型 agent 的 profile mcp.servers.caveman 覆盖层;
  • 绝不卸载用户自己安装的 server,所以已存在的安装会持续付出前缀成本,直到 caveman tools mcp uninstall <agent>
  • 该开关不触碰 wrap 门上的恢复诚实性:CAVEMAN_RECOVERY 依据“本次启动是否真的给了 agent caveman_retrieve”这一证据回答,proxy 永远不会被告知名下并无的检索工具存在;
  • 在此新增第六个工具,就会抬高每个被包装 agent 的每次调用税。

五、构建与运行约定

  • 构建/测试:make product-build PRODUCT=mcp / make product-test PRODUCT=mcp
  • stdout 是协议通道——日志只能写 stderr,且有一条专门测试守护这一点(NewServer 的注释明确要求 log 写 stderr、为 nil 时丢弃;入口 main.go 也确实把 logger 建在 os.Stderr 上)。

六、诚实性不变量(Gotchas)与源码印证

文档列出的“Gotchas (honesty invariants)”每一条都能在源码中找到对应实现:

6.1 不可杀的传输(un-killable transport,issue #139)

stdio server 除 EOF 外什么都杀不死。具体机制(mcp/server.go):

  • 逐行 framingreadLine(L146-L162)按 \n 分消息且有界读取——超过 maxInboundBytes 的行停止缓冲但继续排空到下一个换行,报 tooLong 后以 cave_payload_too_large 应答并重新同步到下一行(永不 return 退出循环);
  • 畸形行handleLine(L167-L181)解析失败应答 -32700(parse error),流继续;
  • 处理器 panicinvokeHandler(L348-L356)用 recover() 把 panic 收敛为 cave_tool_panicked 工具错误;分发层的 panic(dispatch 内部、工具之外)由 serveOne 的顶层 recover(L218-L230)收敛为 cave_internal_error
  • JSON-RPC 批量:以 [ 开头的行走 handleBatch(L187-L213),逐成员分发、收集非通知响应、按规范写回一个数组;全通知批次不回复;
  • 无 id / "id":null 请求是通知,永不回复(isNotificationmcp/protocol.go#L30-L35,对应 #139 的子问题 #4);
  • 双向尺寸上限:入站行与生成的工具输出(compress/toon)都受 cave_payload_too_large 约束,maxInboundBytes/maxResultBytes 默认各 16 MiBmcp/server.go#L29-L32)——但 caveman_retrieve 豁免Tool.ExemptResultCapmcp/server.go#L41-L48):恢复必须返回字节级精确的原始内容,且共享网关存储没有对应的尺寸上限,原始内容可以合法超过 cap,此时 fail-closed 会让被删减的内容永远无法恢复。“死掉的 server 比慢的 server 更糟——proxy 会继续删减已经没有 caveman_retrieve 可展开的内容”。

6.2 fail-open 与 fail-closed 的分工

  • fail-open:引擎错误或畸形输入 → 字节一致的直通,永不产生协议错误(见 compressTool 的回退路径);
  • fail-closed:未知工具/handle → isError + cave_snake_codehandleToolCall 对未知工具返回 cave_unknown_tool 工具错误,mcp/server.go#L319-L344);未知 JSON-RPC method → -32601dispatch 的 default 分支,mcp/server.go#L264-L285);
  • 完整错误码表定义在 mcp/protocol.go#L9-L15-32700 parse、-32600 invalid request、-32601 method not found、-32602 invalid params、-32603 internal。

6.3 零出站(zero-egress)

适配器不导入 net / net/http / os/exec,且有一条测试直接解析源码来强制这一点——TestZeroEgressNoNetworkImportsmcp/server_test.go#L530-L535)检查源码中不含这些导入,这是 PRD §11.6 出站保证的可执行形式。

6.4 版本边界

v1 仅 stdio仅字符串 payload(类型由引擎探测);HTTP 传输 + caveman mcp 子命令属于 v2 规划。

6.5 协议协商永不报错

适配器实现 2024-11-05 契约并回显该版本;客户端索要更新版本时,得到 initialize 结果里的 2024-11-05,由客户端自行决定(符合 MCP 生命周期)。handleInitializemcp/server.go#L287-L317)的注释完整记录了事故链:旧版对不支持的版本应答 -32602: unsupported protocol version,导致 Claude Code 等所有现行客户端直接丢弃 server——而 caveman wrap 是从安装期标记(而非活着的 agent)读取恢复可用性,proxy 于是继续压缩已无 caveman_retrieve 可展开的内容。“回显未实现的版本不对,拒绝开口更不对”:新代码保留保守内核(从不声称实现了并未实现的语义——它拒绝回显,而不是拒绝交谈),supportedProtocolVersions 显式只含 2024-11-05mcp/server.go#L50-L57)。

七、Retrieve 反风暴(anti-storm):每进程一份恢复台账

7.1 问题:每次 retrieve 花费一个完整的 agent 回合

caveman_retrieve 的代价是整个 agent turn:模型要重读整个会话前缀,且此前任何一次 retrieve 返回的内容从此都是该前缀的一部分——N 次 retrieve 花的是 N 个回合,而每次的回放脚本(transcript)都比上一次更长。

文档引用了 2026-08-10 一次对 229 个本地 CaveBench stdout 文件的只读扫描:34 个恢复会话、534 次恢复工具调用;按调用数分桶 1 / 2–5 / >5 分别含 3 / 16 / 15 个会话,p95 为 118,max 为 143。15 个 >5 次调用的会话中 8 个仍通过了精确任务 grader;534 次归一化 (handle, trimmed query) 对中仅 3 对完全重复,许多调用使用新 handle 或指针链。文档同时明确划出证据边界:批次混合了不同 arm、任务与重复,这是描述性的调用形态证据,不是同任务反事实、不是托管网关最终结果总体、也不构成对任何通用阈值的验证

7.2 两条规则:任何一条都不得扣住会话尚未拿到的内容

实现位于 mcp/engine_tools.go#L313-L397retrieveSession),由 EngineTools 为每个进程(=每个会话)创建一份台账:

  1. 重复指针:完全相同的 (handle, query) 返回一行指针,指向会话转录中已有的逐字答案,而不是再发一遍字节(repeatNote,L350)。key 的比较对 query 做与 RetrieveQuery 相同的 trim,所以 " """ 算同一请求(retrieveKey,L368-L370);
  2. 全量支付:超过 retrieveStormThreshold5,L348)个不同的 retrieve 后,下一次调用返回该 handle 的完整存储原文(而非 query 收窄视图)并明示(fullPayoutNote,L352)。文档强调这是保留的历史策略,意图是避免后续更窄的分页;没有任何成对实验验证过该阈值、token 效果或任务结果效果。retrieveStormThreshold 的注释进一步说明:更大的扫描取代了先前不完整的 11/25/46 数据,“描述性计数既不授权收紧也不授权移除”——改阈值需要先做那个实验。

*retrieveSession 为 nil 是安全的且同时禁用两条规则,因此任何没有会话概念的调用方保留旧语义(alreadyServed/shouldPayOutInFull/record 对 nil receiver 均短路返回,L372-L397)。

八、延伸阅读

适用前提:以上描述以当前仓库 v1 实现为准——stdio-only、字符串 payload、2024-11-05 协议版本、16 MiB 双向默认 cap、retrieveStormThreshold=5 为历史保留值。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
981
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384