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

发布时间:2026/9/7 6:16:42
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/cavemancaveman-shrink 是 caveman 项目中的一个 Model Context ProtocolMCP中间件它以 stdio 代理的形式站在 MCP 客户端如 Claude Code与任意上游 MCP 服务器之间只对工具目录中的描述性文本description等 prose 字段做压缩保留代码、URL、路径与标识符原样不动。读完本文你将了解它的安装与接入方式、两个环境变量配置项、它明确「不碰」的安全边界以及从源码层面验证其压缩规则、保护机制与进程管理细节的方法。它解决什么问题MCP 服务器在tools/list、prompts/list、resources/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/list、prompts/list、resources/list三类响应并重写其中的description字段以及你在CAVEMAN_SHRINK_FIELDS中列出的其他字段。从 package.json 可以确认包的入口结构bin指向 index.jsCLI 可执行入口main指向 compress.js纯 Node 压缩库可被require复用当前版本为0.1.1MIT 协议。它明确「不碰」什么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 中的makeLineBuffer用StringDecoder做 UTF-8 安全的行缓冲避免多字节字符被 chunk 边界截断两个方向各建一个上游 → 客户端每行尝试JSON.parse解析失败则原样透传见 index.js解析成功则交给transformResponse。客户端 → 上游直接透传不做解析。transformResponse的匹配策略值得注意index.js它不依赖请求-响应对应关系而是按响应形状识别——只要msg.result中存在tools、prompts、resources、resourceTemplates数组之一就对数组内每项的指定字段做压缩。这意味着即使上游在响应里携带了请求方法之外的额外列表字段也能被覆盖到。压缩分两层顶层字段遍历数组项对fields列表中的字符串字段调用compress嵌套 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_PATTERNScompress.js列出了即使在 prose 内部也绝不触碰的 8 类模式模式正则覆盖围栏代码块…整块代码行内代码…反引号片段URLhttps?://\S链接路径含/或\的词文件系统路径CONST_CASEAPI_KEY_VALUE式常量标识符点分调用pkg.fn()式模块/方法引用函数调用name(...)式代码外观 token版本号1.2.3式语义化版本实际删除的内容compressProsecompress.js对剩余文本依次应用五组正则LEADERS句首的Ill/I will/you can/we will/let me等多行模式PLEASANTRIESplease、kindly、thank you、sure、certainly、of course、happy to等客套HEDGESperhaps、maybe、might、could potentially、i think、it seems等模糊措辞FILLERSjust、really、basically、actually、simply、quite、very、essentially、literallyARTICLESa/an/the仅当后接小写字母避免误伤缩略词。随后折叠多余空白、清理标点前的空格、压缩三连以上换行并把可能被误伤成小写的句首重新大写。哨兵替换与嵌套恢复保护机制的实现是「哨兵替换」先把每个受保护片段替换为N占位符只压缩剩余文本再迭代把占位符还原回原文withProtectedSegmentscompress.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 字符串。从源码结构看其处理链非win32平台直接透传{ command, args }Windows 上按PATHEXT语义解析无扩展名命令resolveWindowsCommand目标是.cmd/.bat时限制 shim 文件 ≤256KB解析出其中引用的.js/.cjs/.mjs目标脚本最终改写为process.execPath当前 Node 可执行文件直接启动该脚本非 Node 的 Windows shim 直接抛错拒绝启动避免「猜」。getSpawnOptions在所有平台都保持shell未开启、stdio: [pipe, pipe, inherit]、windowsHide: truespawn-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静态遍历bin与main入口可达的全部相对require断言每个模块都列在package.json的files中——因为files漏项会导致发布包在启动时MODULE_NOT_FOUND历史上spawn-options.js就出过这种事故。当前状态与适用范围README 的 Status 一节明确caveman-shrink 处于Pre-1.0压缩规则与字段集合可能变化它是 caveman 生态caveman、cavemem、cavekit、cavecrew、caveman-stats、caveman-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),仅供参考