内容
# savage-mcp
<p align="center">
<img src="docs/assets/savage-cave-painting.png" width="520" alt="Savage cave painting mark">
</p>
<p align="center">
<strong>为 MCP 提供更简洁的封装:更少的文字,更小的图像负载,图形化的上下文。</strong>
</p>
<p align="center">
<a href="#前后对比">前后对比</a> •
<a href="#安装">安装</a> •
<a href="#您将获得">您将获得</a> •
<a href="#MCP 工具">MCP 工具</a> •
<a href="#基准测试">基准测试</a> •
<a href="#链接">链接</a>
</p>
---
一个以 Codex 为优先的 MCP 代理和技能/插件,它压缩代理人阅读的内容。文本变得更加紧凑,类似机器的散文。图像要么变成原始的洞穴绘画图形,要么变成区块图场景图。目标很简单:保留意义,减少 token 消耗。
## 前后对比
| 正常回答 | Savage 回答 |
| --- | --- |
| **React 渲染循环**<br><br>"您的 React 组件重新渲染的原因可能是因为您在每次渲染时都创建了一个新的对象引用。当这个内联对象作为道具传递时,React 浅比较看到的是一个不同的对象,因此子组件会再次渲染。使用 useMemo 来保持引用稳定。"<br><br>**73 估计 token** | `tg[React-cmp rerender<-obj-ref/render;inline-obj-prop->diff-ref->child-rerender;fix:useMemo stable ref]`<br><br>**26 估计 token,节省 64%** |
| **身份验证过期 bug**<br><br>"确实,问题很可能是由身份验证中间件不一致地验证 token 过期引起的。比较应该在 exp 小于或等于当前时间戳时拒绝 token,而错误路径应该返回 401 而不是继续执行。"<br><br>**67 估计 token** | `tg[bug:auth-mw tok-expiry invalid;reject exp<=now err-path->401/no-fallthrough]`<br><br>**20 估计 token,节省 70%** |
相同的修复。更少的散文。代码、URL、路径、标识符、API 名称和确切的错误保持不变。
| 信号 | 含义 |
| --- | --- |
| 文本图保存:平均 62% | 在十个短散文样本上,通过本地文本到图形压缩器实现平均减少。这是 MCP 上下文中有意义的文本模式。 |
| 图形图像保存:样本 85% | Base64 图像被小型场景图图形替换。这是 MCP 上下文节省大量空间的地方。 |
| 技术 token | 当它们看起来像代码、路径、URL、API 名称或错误时,精确保留。 |
| 形状 | 图形节点/边,而不是父/子树。 |
> [!IMPORTANT]
> 文本数字是使用 `format: "graph"` 从 `examples/run_examples.js` 进行的本地估计,而不是 API 计费记录。图像数字将 base64 字符与生成的图形有效负载进行比较。
## 安装
```bash
npm install -g savage-mcp
```
包装任何 stdio MCP 服务器:
```json
{
"mcpServers": {
"fs-savage": {
"command": "npx",
"args": [
"savage-mcp",
"--mode",
"auto",
"--image-mode",
"diagram",
"--",
"npx",
"@modelcontextprotocol/server-filesystem",
"/tmp"
]
}
}
}
```
命令行界面:
```bash
savage-mcp [--mode auto|text|image|off] \
[--text-level lite|balanced|ultra] \
[--text-format prose|graph] \
[--image-mode graph|svg|diagram|pass] \
[--max-primitives 64] \
[--min-image-bytes 4096] \
-- <upstream> [...args]
```
## 您将获得
| 功能 | 描述 |
| --- | --- |
| 文本图压缩 | 对于安全散文字段和文本内容,使用类似 `tg[subject->action;fix:step]` 的 KISS 帧。 |
| 图像基元 | `image/png`、`jpeg`、`webp` 等 -> `savage_graph_v1` 节点/边/颜色。 |
| 场景图 | 实体框 + 动词箭头 -> `savage_diagram_v1`。 |
| Codex 技能 | 用于压缩 Codex 回复的 `plugins/savage` 和 `skills/savage`。 |
| MCP 实用工具 | 注入到上游 `tools/list` 中的本地工具。 |
| 统计信息 | 文本/图像节省的进程级字节计数器。 |
## MCP 工具
`savage-mcp` 添加了以下本地工具:
| 工具 | 描述 |
| --- | --- |
| `savage.text` | 将散文压缩为紧凑的 KISS 散文或文本图帧。保留代码、URL、路径、标识符、API 名称和确切的错误。 |
| `savage.image` | 将 base64 图像数据转换为紧凑的视觉基元、SVG 或图块。 |
| `savage.diagram` | 从标记的实体和动词关系中构建紧凑的场景/区块图。 |
| `savage.payload` | 压缩 JSON 有效负载中的安全文本字段和图像块。 |
| `savage.stats` | 返回此代理进程节省的字节数。 |
工具调用变体:
```json
{ "name": "savage.text", "arguments": { "text": "数据库配置响应非常重要。", "level": "ultra", "format": "graph" } }
```
```json
{ "name": "savage.image", "arguments": { "data": "<base64>", "mimeType": "image/png", "format": "graph", "maxPrimitives": 24 } }
```
```json
{
"name": "savage.diagram",
"arguments": {
"items": [
{ "id": "person", "label": "person" },
{ "id": "mountain", "label": "mountain" }
],
"relations": [
{ "from": "person", "to": "mountain", "verb": "climbs" }
]
}
}
```
## 图像模式
| 模式 | 输出 | 最适合 |
| --- | --- | --- |
| `graph` | `savage_graph_v1` 矩形、颜色、邻接边 | 确定性视觉简化;洞穴绘画原始感觉。 |
| `svg` | 由原始单元格生成的紧凑 SVG | 客户端可以渲染 SVG 时的视觉预览。 |
| `diagram` | `savage_diagram_v1` 标记框 + 动词箭头 | 语义区块方案:`person --climbs--> mountain`。 |
| `pass` | 原始图像块 | 当二进制保真度比上下文大小更重要时。 |
从生成的样本场景接收的示例图像:
<p align="center">
<img src="examples/generated/sample-scene.png" width="220" alt="生成的样本场景在 savage 图像压缩之前">
<img src="examples/generated/diagram-preview.png" width="300" alt="带有 person climbs mountain 的图预览">
</p>
| 有效负载 | 字符 | 模式 | 节省 |
| --- | ---: | --- | ---: |
| Base64 PNG | 3752 | 原始 | - |
| 原始图 | 3347 | `savage_graph_v1` | 11% |
| 场景图 | 562 | `savage_diagram_v1` | 85% |
场景图输出:
```text
person --climbs--> mountain
sun --lights--> mountain
```
> [!NOTE]
> `diagram` 模式是确定性的。如果您想要 `person`、`mountain` 和 `climbs` 这样的标签,请在工具调用中传递场景标签/关系。没有标签,包装器只能发出视觉区域,而不能声称对象识别。
## 基准测试
通过本地示例运行器和当前的压缩器以及简单的 UTF-8 字节/4 token 估计进行衡量。
| 任务 | 正常估计 token | Savage 图估计 token | 节省 |
| --- | ---: | ---: | ---: |
| 解释 React 重新渲染 bug | 73 | 26 | 64% |
| 修复 token 过期中间件 | 67 | 20 | 70% |
| 设置 PostgreSQL 连接池 | 62 | 13 | 79% |
| 解释 MCP stdio 包装器 | 54 | 18 | 67% |
| 重构回调 API | 49 | 23 | 53% |
| 比较整体架构和服务 | 52 | 22 | 58% |
| 审查 PR 安全风险 | 51 | 27 | 47% |
| Docker 构建优化 | 52 | 17 | 67% |
| 调试竞争条件 | 51 | 28 | 45% |
| 实现错误边界 | 49 | 15 | 69% |
| **平均值** | **56** | **21** | **62%** |
### Savage 收据
内存样式文件,相同的运行器:
| 文件 | 原始估计 token | Savage 图估计 token | 节省 |
| --- | ---: | ---: | ---: |
| `agent-instructions.md` | 50 | 17 | 66% |
| `mcp-server-notes.md` | 46 | 21 | 54% |
| `image-policy.md` | 46 | 9 | 80% |
| `release-checklist.md` | 51 | 11 | 78% |
| `codex-style.md` | 43 | 18 | 58% |
| **平均值** | **47** | **15** | **67%** |
> [!TIP]
> 图形文本模式比散文模式更具攻击性。当可读性比节省更重要时,请使用 `--text-format prose`;对于 MCP 上下文大小,请使用 `graph`。
## 图形形状
文本图帧:
```text
tg[React-cmp rerender<-obj-ref/render;inline-obj-prop->diff-ref->child-rerender;fix:useMemo stable ref]
```
原始图像图:
```json
{
"v": 1,
"kind": "savage_graph_v1",
"nodes": [{ "id": "n0", "t": "rect", "bbox": [0, 0, 0.1, 0.1], "color": "#8ecae6", "score": 0.9 }],
"edges": [{ "a": "n0", "b": "n1", "r": "adj", "w": 0.42 }],
"meta": { "mimeType": "image/png", "width": 192, "height": 128 }
}
```
场景图:
```json
{
"v": 1,
"kind": "savage_diagram_v1",
"nodes": [{ "id": "person", "t": "box", "label": "person" }],
"edges": [{ "a": "person", "b": "mountain", "r": "verb", "label": "climbs" }]
}
```
## 链接
- [README 基准测试源](examples/run_examples.js)
- [生成的示例结果](examples/generated/results.json)
- [Codex 插件清单](plugins/savage/.codex-plugin/plugin.json)
- [Savage 技能](skills/savage/SKILL.md)
- [MCP 代理源](src/savage-mcp/index.js)
- [GitHub 仓库](https://github.com/denzasikora-lab/savage-mcp)
## 限制
- 没有隐藏的视觉模型。语义图需要调用者提供的标签。
- 不支持的图像 MIME 块原样传递。
- 压缩避免重写类似代码的 token;确切的技术字符串优先于额外的节省。