← 返回 FEED
GITHUB2026-04-20

mcp-cli:让 AI Agent 免交 fork/exec 税,73% 系统调用开销消失

每次 Claude Code 或 Codex 调用一次 catrggit status,都在交一笔隐形的"税":fork + exec + 页面表建立 + 库加载 + syscall 流 + 清理。

对于一个任务发几百个小读操作的 agent 来说,这不是业务逻辑,这是 kernel 开销。

madeye 开源的 mcp-cli 用一个 sidecar-daemon + MCP bridge 架构,把这笔税彻底取消。

核心数字

在 Codex(rust-v0.121.0)分析自身代码库上实测:

指标基线使用 mcp-cli变化
execve 总调用8322−73%
rg 调用次数130−100%
sed 调用次数502−96%
墙壁时间201s324s+61%(见正文说明)

架构

Claude Code / Codex
        ↓ stdio (MCP)
mcp-cli-bridge  ←→  Unix Domain Socket  ←→  mcp-cli-daemon(长驻进程)
                                            ├── mmap VFS(文件读)
                                            ├── libgit2(git 状态)
                                            ├── ripgrep searcher(搜索)
                                            └── 未来:tree-sitter、LSP

三层:

  • mcp-cli-bridge:小型 stdio 二进制,agent 启动时 spawn。负责把 MCP 工具请求翻译成 UDS 调用。生命周期短暂,重量级状态全在 daemon。
  • mcp-cli-daemon:长驻进程,拥有整个项目。用 mmap 做文件读(无需每次 read(2) syscall)、用 libgit2 做 git 状态(无需 fork git)、用 ripgrep 的 grep-searcher 库做搜索。
  • fs.snapshot / fs.changes:增量同步机制。daemon 在 inotify(Linux)/FSEvents(macOS) 上监听 gitignore 过滤的文件变化,维护单调版本游标,返回自客户端指定版本以来的合并变化事件。如果客户端落后太远,返回 overflowed: true 让客户端重新扫描而不是静默丢事件。

为什么这样设计

传统 shell-wrapped 工具调用的成本构成:

fork + exec + page-table setup + 库加载 + syscall 流 + 清理

对于发几百个小读操作的 agent,这笔开销不是例外,是常态。

解法:保持一个热 daemon。

  • 文件读来自已在 page cache 的 mmap——内核只在 miss 时 page-in,没有每次调用的 read syscall 流
  • Git、搜索、(计划中的)解析操作都在进程内——内核看到的是一个长驻进程,不是 thousands of 短命进程
  • Bridge 保持极小,每次调用成本就是一个预热 socket 上的 UDS 往返

这是未来更多层的地基:增量 inotify diffs、tree-sitter 索引、clangd/rust-analyzer 等语言后端复用。

安装与使用

cargo build --release
target/release/mcp-cli install

Daemon 在首次工具调用时自动按项目根目录 spawn,空闲 30 分钟后自动退出,无需手动运行任何东西。

对于 Codex,要额外传 --prefer-mcp 让它真正走 daemon 而不是 fallback 到自己的 Bash:

target/release/mcp-cli install --target codex --prefer-mcp

--prefer-mcp 写入 ~/.codex/config.toml

  • [features] shell_tool = false(停用 Codex 内置 Bash 工具)
  • 每个 mcp-cli 工具设 approval_mode = "approve"(非交互执行时不弹确认)

墙壁时间回归的问题

注意到 mcp-cli 的墙壁时间反而从 201s 增加到 324s(+61%)。这是因为每次 MCP 调用是原子的,而 bash 是管道——两个调用可以合并成一个进程完成。

作者已在迭代解决这个问题:第一个 run 124 MCP turns / 375s,经过 search_grep ?context 合并优化后降到 73 turns / 324s。开放方向包括 compound/batch MCP 工具(合并多个操作)、io_uring I/O、per-request arenas。

现状与路线图

已完成:skeleton、增量同步、tree-sitter 解析缓存、drop-in 安装、per-cwd auto-spawn、reconnect-on-dead、mimalloc + buffer pool、M5/M7 基准测试。

开放项:rust-analyzer / clangd 语言后端、compound/batch MCP 工具(解决 wall-clock 回归)、io_uring I/O、per-request arenas、LSP / WASI 挂载层。

GitHub:https://github.com/madeye/mcp-cli