内容
# lazy-mcp-wrapper
中文文档: [README.zh-CN.md](./README.zh-CN.md)
`lazy-mcp-wrapper` 是一个轻量级的 MCP 代理,用于保持本地 AI 客户端快速启动。Codex、Cursor、Claude Code 和 Claude Desktop 可以立即启动包装器;包装器仅在工具调用实际需要时才启动真正的 MCP 服务器。
## 安装
```bash
brew tap sleticalboy/tap
brew install lazy-mcp-wrapper
lazy-mcp-wrapper setup
```
检查结果:
```bash
lazy-mcp-wrapper setup status
```

## 功能
- 减少 Context7、Playwright 和 MasterGo 等 MCP 服务器的空闲内存使用。
- 将 stdio 和远程 HTTP MCP 服务器与客户端启动路径分开,直到需要 `tools/call`、`resources/*` 或 `prompts/*` 时才启动。
- 缓存 `tools/list`,以便客户端无需重复启动繁重的 MCP 进程即可发现工具。
- 通过本地守护进程在多个 Codex CLI 会话之间共享无状态 MCP 服务器。
- 为 Playwright 等服务器保留具有 `sharing: "session"` 的有状态 MCP 隔离。
- 提供 `setup`、`setup status`、`setup update` 和 `setup uninstall`,用于可逆的客户端配置。
当前范围:stdio MCP 服务器和远程 HTTP MCP 服务器。远程 HTTP 默认使用 `streamable-http`;仅为兼容性支持旧版 HTTP+SSE。
## 构建
```bash
make build
make test
```
## 从源代码构建
安装到本地 `~/.local/bin/lazy-mcp-wrapper`:
```bash
make install
```
或:
```bash
./scripts/install-local.sh
```
覆盖前缀:
```bash
PREFIX=/opt/lazy-mcp-wrapper ./scripts/install-local.sh
```
## 配置
示例:
```json
{
"name": "context7",
"sharing": "shared",
"command": "npx",
"args": ["-y", "@upstash/context7-mcp"],
"real_protocol_version": "2024-11-05",
"real_framing": "jsonl",
"cache_dir": "/Users/you/Library/Caches/lazy-mcp-wrapper",
"idle_timeout": "15s",
"startup_timeout": "30s",
"call_timeout": "120s",
"log_file": "/tmp/lazy-mcp-wrapper-context7.log"
}
```
Codex 配置:
```toml
[mcp_servers.context7]
type = "stdio"
command = "/absolute/path/to/lazy-mcp-wrapper"
args = ["--config", "/absolute/path/to/context7.json"]
```
### 远程 HTTP MCP 服务器
`lazy-mcp-wrapper` 可以代理远程 HTTP 上的 MCP 服务器。
推荐的协议是 `streamable-http`(MCP 规范 2025-03-26):
```json
{
"name": "my-remote-mcp",
"url": "https://example.com/mcp",
"protocol": "streamable-http"
}
```
当 `setup` 包装远程 HTTP 服务器时,它通过共享的守护进程启动本地 HTTP 代理,并将客户端配置重写为本地 URL,例如 `http://127.0.0.1:54300`。端口会自动从 `54300` 开始,并存储在生成的包装器配置中作为 `local_port`。
`protocol` 字段接受:
| 值 | 描述 |
|-------|-------------|
| `streamable-http` | **推荐。** MCP 2025-03-26 标准。省略 `protocol` 时默认为此值。 |
| `sse` | **已弃用。** 旧版 HTTP+SSE 传输。仅为兼容性支持。 |
> **`sse`(HTTP+SSE)已被 MCP 规范弃用**。它被支持以兼容旧服务器,但不应用于新部署。
## 行为
- `initialize` 由包装器回答。
- `tools/list`、`tools/call`、`prompts/*` 和 `resources/*` 被转发到真正的 MCP 服务器。
- 真正的 MCP 服务器在 `idle_timeout` 后停止。
- 日志记录到 `log_file`;stdout 保留用于 MCP 帧。
- `real_protocol_version` 可以固定发送到真正 MCP 服务器的协议版本,当客户端发送的版本是真正服务器不支持的新版本时。
- `real_framing` 控制包装器如何与真正 MCP 服务器通信:
- `header` 使用 MCP `Content-Length` 分帧,默认为此值。
- `jsonl` 使用每行一个 JSON-RPC 消息。Context7 v3.2.2、Playwright MCP 1.62.0-alpha 和 MasterGo Magic MCP 目前使用此模式。
- `tools/list` 默认缓存。缓存文件存储在 OS 用户缓存目录下,除非设置了 `cache_dir`。将 `disable_cache` 设置为 `true`,始终查询真正的 MCP 服务器。
- `sharing` 控制守护进程共享策略。`shared` 重用每个 MCP 名称的一个代理。`session` 为有状态 MCP 创建每个客户端连接的一个代理。
## 缓存和检查
在不启动 Codex 会话的情况下刷新缓存:
```bash
lazy-mcp-wrapper --config ./examples/context7.json --refresh-cache
```
检查解析的配置和缓存状态:
```bash
lazy-mcp-wrapper --config ./examples/context7.json --inspect
```
检查共享守护进程的运行时状态:
```bash
lazy-mcp-wrapper status --socket ~/.lazy-mcp-wrapper/lazy-mcpd.sock
lazy-mcp-wrapper status --socket ~/.lazy-mcp-wrapper/lazy-mcpd.sock --format table
```
控制共享守护进程:
```bash
lazy-mcp-wrapper stop --socket ~/.lazy-mcp-wrapper/lazy-mcpd.sock
lazy-mcp-wrapper reload --socket ~/.lazy-mcp-wrapper/lazy-mcpd.sock
lazy-mcp-wrapper reload --socket ~/.lazy-mcp-wrapper/lazy-mcpd.sock --graceful
lazy-mcp-wrapper reload --socket ~/.lazy-mcp-wrapper/lazy-mcpd.sock --force
```
`status` 包括守护进程配置路径、守护进程 pid、启动时间、运行时、活动客户端会话、转发调用、最后错误和每个 MCP 的指标,例如调用、错误、最后方法、最后错误和延迟。
`reload` 仅在守护进程使用 `--daemon-config` 启动时热重载守护进程配置。手动 `daemon --config ...` 模式没有重载源,并返回显式错误。默认情况下,如果有活动客户端连接,重载返回忙碌。使用 `--graceful` 将新客户端路由到新的代理集,而现有客户端保留其旧代理,直到断开连接。使用 `--force` 立即替换代理并关闭旧的真正 MCP 进程。
## 注意事项
`node_repl` 故意不适合此包装器,因为它在调用之间保持状态。除非您愿意丢失 REPL 状态,否则请直接配置它。
## 自动设置
`setup` 扫描已安装的 AI 客户端,创建包装器配置,安装 macOS LaunchAgent,并使用备份更新客户端 MCP 配置:
```bash
lazy-mcp-wrapper setup --dry-run
lazy-mcp-wrapper setup
lazy-mcp-wrapper setup --yes
```
支持的客户端:
- Codex:`~/.codex/config.toml`
- Cursor:`~/.cursor/mcp.json`
- Claude Code:`~/.claude/settings.json`
- Claude Desktop:`~/Library/Application Support/Claude/claude_desktop_config.json`
命令包装 stdio MCP 服务器和远程 HTTP MCP 服务器,跳过 `node_repl`,并为 Playwright 使用 `sharing: "session"`。远程 HTTP 服务器通过本地守护进程管理的 HTTP 代理端口暴露给客户端。
## 共享守护进程模式
多个 Codex CLI 会话可以通过一个守护进程共享无状态 MCP 服务器:
```bash
lazy-mcp-wrapper daemon \
--socket /Users/you/.lazy-mcp-wrapper/lazy-mcpd.sock \
--config ./examples/context7.json \
--config ./configs.local/mastergo-magic-mcp.json
```
或使用守护进程配置文件:
```json
{
"socket": "/Users/you/.lazy-mcp-wrapper/lazy-mcpd.sock",
"configs": [
"./examples/context7.json",
"./examples/playwright.json",
"./configs.local/mastergo-magic-mcp.json"
]
}
```
```bash
lazy-mcp-wrapper daemon --daemon-config /Users/you/.lazy-mcp-wrapper/config.json
```
Codex 客户端配置:
```toml
[mcp_servers.context7]
type = "stdio"
command = "/Users/you/.local/bin/lazy-mcp-wrapper"
args = ["client", "--socket", "/Users/you/.lazy-mcp-wrapper/lazy-mcpd.sock", "--name", "context7"]
```
对 Context7 和 MasterGo 等无状态或只读 MCP 服务器使用 `sharing: "shared"`。对 Playwright 等有状态 MCP 服务器使用 `sharing: "session"`;Codex 会话共享守护进程入口点,而每个客户端连接都有自己的真正 MCP 进程。
在 macOS 上,将共享守护进程安装为用户 LaunchAgent:
```bash
make install-agent
```
使用以下命令卸载它:
```bash
make uninstall-agent
```
安装程序将当前 `PATH` 写入 plist,以便守护进程可以找到 `npx`。
使用 `lazy-mcp-wrapper stop --socket ~/.lazy-mcp-wrapper/lazy-mcpd.sock` 请求守护进程退出。如果启用了 LaunchAgent 管理,launchd 将根据安装的配置重新启动它。
## 验证
运行单元测试和集成测试:
```bash
GOCACHE=/private/tmp/lazy-mcp-wrapper-gocache go test ./...
```
集成测试构建并运行 `cmd/fake-mcp` 以验证 stdio 分帧、子进程启动、`initialize` 和 `tools/list` 转发,无需网络访问。
测试真实 MCP 服务器:
```bash
go build -o bin/lazy-mcp-wrapper ./cmd/lazy-mcp-wrapper
go run ./cmd/mcp-smoke ./bin/lazy-mcp-wrapper ./examples/context7.json
go run ./cmd/mcp-smoke \
--call-tool resolve-library-id \
--call-args '{"query":"Go gin web framework routing middleware","libraryName":"Gin"}' \
./bin/lazy-mcp-wrapper ./examples/context7.json
```
验证示例:
```bash
go run ./cmd/mcp-smoke ./bin/lazy-mcp-wrapper ./examples/context7.json
go run ./cmd/mcp-smoke ./bin/lazy-mcp-wrapper ./examples/playwright.json
go run ./cmd/mcp-smoke ./bin/lazy-mcp-wrapper ./examples/mastergo-magic-mcp.json
```
运行完整的本地烟雾测试套件:
```bash
./scripts/smoke.sh
```
使用本地假 MCP 运行共享守护进程烟雾测试:
```bash
make smoke-shared-daemon
```
运行真实 Playwright 守护进程会话烟雾测试:
```bash
make smoke-playwright-session
```
MasterGo 烟雾测试在存在时使用 `configs.local/mastergo-magic-mcp.json`,因为提交的示例故意不包含真实令牌。其工具调用允许验证错误结果,因为 MasterGo 设计工具需要真实的 `fileId`/`layerId`;这仍然验证包装器可以初始化真实 MCP 并转发 `tools/call`。
如果真实 MCP 服务器没有响应,请检查配置的 `log_file`。`cmd/mcp-smoke` 中的失败且没有来自直接 MCP 启动的响应通常意味着上游 MCP 命令没有进入 stdio 服务器模式,而不是包装器传输损坏。