news 2026/9/7 6:16:13

caveman-shrink:为 MCP 工具目录做「文字瘦身」的 stdio 代理,少烧 Token 且不改变工具语义

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
caveman-shrink:为 MCP 工具目录做「文字瘦身」的 stdio 代理,少烧 Token 且不改变工具语义

caveman-shrink:为 MCP 工具目录做「文字瘦身」的 stdio 代理,少烧 Token 且不改变工具语义

【免费下载链接】caveman🪨 why use many token when few token do trick — Claude Code skill that cuts 65% of tokens by talking like caveman项目地址: https://gitcode.com/GitHub_Trending/caveman1/caveman

caveman-shrink 是 caveman 项目中的一个 Model Context Protocol(MCP)中间件:它以 stdio 代理的形式站在 MCP 客户端(如 Claude Code)与任意上游 MCP 服务器之间,只对工具目录中的描述性文本(description等 prose 字段)做压缩,保留代码、URL、路径与标识符原样不动。读完本文,你将了解它的安装与接入方式、两个环境变量配置项、它明确「不碰」的安全边界,以及从源码层面验证其压缩规则、保护机制与进程管理细节的方法。

它解决什么问题

MCP 服务器在tools/listprompts/listresources/list等响应中会返回大量自然语言描述。模型每次读取工具目录都要为这些文字消耗上下文 Token,而其中相当一部分是冠词、填充词、客套话和犹豫式表达,对调用工具本身没有信息量。

caveman-shrink 的定位(见 README)可以概括为一句:

MCP middleware. Wrap any MCP server. Cut the prose. Keep the substance.

它是一个 stdio 代理:压缩的是「模型要读」的那一侧文本,目标是让工具目录被读得更省 Token,且工具的语义不发生任何变化。压缩规则与 caveman 主 skill 使用的边界一致——代码、URL、路径、标识符一律保留,只剥离冠词、填充词、模糊措辞与客套话。

安装与接入

安装

npm install -g caveman-shrink # 或者直接用 npx 运行 npx caveman-shrink <upstream-command> [...args]

在 MCP 客户端中包装任意上游服务器

以文件系统 MCP 服务器为例,在 Claude Code(或其他 MCP 客户端)的配置中把caveman-shrink作为第一个进程、上游服务器作为其参数:

{ "mcpServers": { "fs-shrunk": { "command": "npx", "args": [ "caveman-shrink", "npx", "@modelcontextprotocol/server-filesystem", "/path/to/dir" ] } } }

代理会以后台子进程方式启动上游服务器,拦截tools/listprompts/listresources/list三类响应,并重写其中的description字段(以及你在CAVEMAN_SHRINK_FIELDS中列出的其他字段)。

从 package.json 可以确认包的入口结构:bin指向 index.js(CLI 可执行入口),main指向 compress.js(纯 Node 压缩库,可被require复用),当前版本为0.1.1,MIT 协议。

它明确「不碰」什么

README 强调 v1 是保守设计,以下三类内容一律不改:

  • 发往上游的请求体:客户端 → 上游方向原样透传。
  • tools/call的调用结果:不压缩上游返回给模型的数据,避免悄悄改变数据内容而破坏下游解析。
  • prose 中的代码外观 token:标识符、URL、路径、类代码片段在任意文本中都精确保留,边界与父级 caveman skill 相同。

这一保守策略在 index.js 中可以直接印证:Client → us → upstream. Pass through unchanged for v1.,客户端输入只被forwardInput原字节转发,不经过任何JSON.parse或改写。

配置项

环境变量默认值作用
CAVEMAN_SHRINK_FIELDSdescription需要压缩的字段名,逗号分隔
CAVEMAN_SHRINK_DEBUG0设为1时向 stderr 输出每个字段的压缩前后长度

两个变量在 index.js 中解析:

const debug = process.env.CAVEMAN_SHRINK_DEBUG === '1'; const fields = (process.env.CAVEMAN_SHRINK_FIELDS || 'description') .split(',').map(s => s.trim()).filter(Boolean);

即字段列表按逗号切分、去空白、去空项,只接受非空字符串字段名。开启调试后,代理会为每一处实际发生变化的字段打印一条形如[caveman-shrink] tools.<tool名>.description: 142→97 bytes的日志(见 index.js),便于核对压缩是否如预期生效。

代理主流程:源码级走读

双向行缓冲的 JSON-RPC 透传

MCP 的 stdio 传输按行分隔 JSON-RPC 消息。index.js 中的makeLineBufferStringDecoder做 UTF-8 安全的行缓冲(避免多字节字符被 chunk 边界截断),两个方向各建一个:

  • 上游 → 客户端:每行尝试JSON.parse,解析失败则原样透传(见 index.js);解析成功则交给transformResponse
  • 客户端 → 上游:直接透传,不做解析。

transformResponse的匹配策略值得注意(index.js):它不依赖请求-响应对应关系,而是按响应形状识别——只要msg.result中存在toolspromptsresourcesresourceTemplates数组之一,就对数组内每项的指定字段做压缩。这意味着即使上游在响应里携带了请求方法之外的额外列表字段,也能被覆盖到。

压缩分两层:

  1. 顶层字段:遍历数组项,对fields列表中的字符串字段调用compress
  2. 嵌套 schema:对每项的inputSchema调用compressDescriptionsInPlace,递归遍历对象/数组,压缩所有同名字符串字段——这覆盖了工具参数级(JSON Schema 内嵌套的)description,是顶层压缩管不到的区域。

背压与退出码

代理对背压做了显式处理:写客户端方向,若process.stdout.write返回 false 就暂停上游 stdout,等drain再恢复(index.js);客户端 → 上游方向同理(index.js)。上游进程close时先暂停 stdin、摘除监听器,让已转换的字节全部排空后按退出码/信号自然退出(index.js),信号退出时还原为128 + signal_number的 shell 惯例。

压缩器:保护优先的文本规则

compress.js 是纯 Node 实现(文件头注释说明它是对 caveman-compress 工具边界的 Node 重实现,使代理保持单运行时)。API 为compress(text) → { compressed, before, after }

永远不动的 token(保护模式)

PROTECTED_PATTERNS(compress.js)列出了即使在 prose 内部也绝不触碰的 8 类模式:

模式正则覆盖
围栏代码块```…```整块代码
行内代码`…`反引号片段
URLhttps?://\S+链接
路径/\的词文件系统路径
CONST_CASEAPI_KEY_VALUE常量标识符
点分调用pkg.fn()模块/方法引用
函数调用name(...)代码外观 token
版本号1.2.3语义化版本

实际删除的内容

compressProse(compress.js)对剩余文本依次应用五组正则:

  • LEADERS:句首的I'll/I will/you can/we will/let me等(多行模式);
  • PLEASANTRIESpleasekindlythank yousurecertainlyof coursehappy to等客套;
  • HEDGESperhapsmaybemightcould potentiallyi thinkit seems等模糊措辞;
  • FILLERSjustreallybasicallyactuallysimplyquiteveryessentiallyliterally
  • ARTICLESa/an/the(仅当后接小写字母,避免误伤缩略词)。

随后折叠多余空白、清理标点前的空格、压缩三连以上换行,并把可能被误伤成小写的句首重新大写。

哨兵替换与嵌套恢复

保护机制的实现是「哨兵替换」:先把每个受保护片段替换为N占位符,只压缩剩余文本,再迭代把占位符还原回原文(withProtectedSegments,compress.js)。这里的坑在于模式嵌套——路径规则先吞下STARTER/BUSINESS,随后函数调用规则又可能把还原产物里的哨兵一起匹配进新的哨兵。因此恢复是迭代的,上限MAX_RESTORE_PASSES = 8次;注释明确说明:轮数随嵌套深度增长而不随输入长度增长,8 次已远超真实深度,同时为病态输入兜底。

这个机制有对应的回归测试(tests/test_mcp_shrink.js,标注为 #444):对plan type (STARTER/BUSINESS)user role (ADMIN/MEMBER/GUEST)等输入断言枚举值完整保留、且输出中不残留N形式的哨兵。

上游进程启动:跨平台与 Windows shim 安全

spawn-options.js 单独抽出了「如何安全启动上游进程」的逻辑,文件头注释解释了动机:Windows 上 npm 工具以.cmdshim 形式存在,必须直接把参数数组交给目标 Node 脚本,绝不能把上游参数拼接进 cmd.exe 字符串

从源码结构看其处理链:

  1. win32平台直接透传{ command, args }
  2. Windows 上按PATHEXT语义解析无扩展名命令(resolveWindowsCommand);
  3. 目标是.cmd/.bat时,限制 shim 文件 ≤256KB,解析出其中引用的.js/.cjs/.mjs目标脚本,最终改写为process.execPath(当前 Node 可执行文件)直接启动该脚本;
  4. 非 Node 的 Windows shim 直接抛错拒绝启动,避免「猜」。

getSpawnOptions在所有平台都保持shell未开启、stdio: ['pipe', 'pipe', 'inherit']windowsHide: true(spawn-options.js),测试 tests/test_mcp_shrink.js 分别对 win32/linux/darwin 断言了「shell 关闭 + 三管道 stdio」。

测试如何验证它

压缩单元行为的回归测试集中在 tests/test_mcp_shrink.js,每条断言都对应 README 宣称的一条边界:

  • 删冠词、删填充词/客套、删模糊措辞与I will句首(compress直接测);
  • 围栏代码、行内代码、URL、路径、CONST_CASE、点分调用逐条断言原样保留;
  • 真实 MCP 风格描述(天气工具示例)断言压缩率超过 15% 下限,且weather/Fahrenheit/city name等实质内容保留;
  • compressDescriptionsInPlace对嵌套tools数组的遍历、对非字符串字段跳过不抛错。

代理本体的运行时测试在 tests/installer/mcp-shrink.runtime.test.mjs:

  • 上游一次性吐出 1.5MB 的无行尾最终响应,断言代理能完整排空、payload 长度不丢失(对应responses.end()的 flush 逻辑);
  • 一个🙂emoji 的 UTF-8 字节被切到两个 chunk,断言StringDecoder行缓冲不会产生乱码。

此外 tests/test_mcp_shrink.js 中还有一个打包回归(#597):静态遍历binmain入口可达的全部相对require,断言每个模块都列在package.jsonfiles中——因为files漏项会导致发布包在启动时MODULE_NOT_FOUND(历史上spawn-options.js就出过这种事故)。

当前状态与适用范围

README 的 Status 一节明确:caveman-shrink 处于Pre-1.0,压缩规则与字段集合可能变化;它是 caveman 生态(cavemancavememcavekitcavecrewcaveman-statscaveman-init等 skill 套件)的组成部分,许可为 MIT。

适用前提与限制小结:

  • 仅作用于 MCPstdio 传输、按行分隔的 JSON-RPC 消息;非行分隔或多消息粘连的传输不在当前实现覆盖范围内;
  • v1 只压缩tools/list/prompts/list/resources/list(含resourceTemplates)响应中的描述性字符串字段,tools/call结果与请求体一律原样透传;
  • CAVEMAN_SHRINK_FIELDS只能列出字符串字段名,对象/数组值会被跳过(测试有明确断言)。

对想进一步了解的读者,可直接阅读 压缩核心、代理主入口 与 启动选项 三个文件,配合 README 与上文引用的测试文件,即可完整重建并验证这一中间件的行为边界。

【免费下载链接】caveman🪨 why use many token when few token do trick — Claude Code skill that cuts 65% of tokens by talking like caveman项目地址: https://gitcode.com/GitHub_Trending/caveman1/caveman

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 6:14:53

Unity游戏开发:宝可梦机甲变身盲盒系统完整实现指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 6:14:50

CUDA统一内存深度解析:从原理到性能优化实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 6:12:10

STK中文教程详解:从卫星轨道设计到覆盖分析的航天仿真实践

简介&#xff1a;STK中文教程.zip是一份面向航天工程、遥感及军事仿真初学者的系统教程合集&#xff0c;围绕STK软件从基础概念到任务规划的典型学习路径做了完整整理。压缩包共48个文件&#xff0c;以14个PDF教程、2个PPT演示、8个GIF操作演示及多个场景工程文件&#xff08;M…

作者头像 李华
网站建设 2026/9/7 6:11:08

Project: My Awesome TypeScript Library

Project: My Awesome TypeScript Library 【免费下载链接】gemini-cli An open-source AI agent that brings the power of Gemini directly into your terminal. 项目地址: https://gitcode.com/GitHub_Trending/gemi/gemini-cli General Instructions: When generati…

作者头像 李华
网站建设 2026/9/7 6:10:42

WinDbg实战指南:从崩溃转储到内核蓝屏分析

简介&#xff1a;Windbg是微软出品的老牌Windows调试工具&#xff0c;在系统蓝屏分析、内核驱动排错、用户态程序崩溃转储等场景中无可替代。这份资源把Windbg的常用功能整理成一份可查可用的资料包&#xff0c;既包含符号路径设置、进程附加等入门操作&#xff0c;也覆盖内存读…

作者头像 李华
网站建设 2026/9/7 6:09:21

从零构建Model Eon:轻量级模型版本管理系统的实践指南

在实际机器学习项目中&#xff0c;模型文件的管理往往比训练本身更容易埋下隐患。训练好的模型散落在各台机器的磁盘目录里&#xff0c;文件名可能是model_v2_final_v3_really_final.pkl&#xff0c;实验记录写在聊天记录或本地 Excel 里&#xff0c;线上服务用的模型版本靠人工…

作者头像 李华