内容
# pounce
人工智能编码代理的真实数据 — 每个MCP工具调用实际执行的内容。
Pounce封装了任何[MCP](https://modelcontextprotocol.io)服务器,并显示通过它的每个JSON-RPC消息。它是**只读的**:它监视和记录,但从不阻塞、修改或注入到流中 — 被封装的服务器表现得就像没有pounce一样。
> **v0.3.1 — 最小可行产品(第一至三阶段),无完全磁盘访问权限:** 工具调用时间线(无权限),每个进程的网络捕获(选择性地使用`sudopounce daemon`,仅限root用户),以及意图↔效果关联。`pounce view`显示一个先验总结 — 每个工具调用及其导致的连接(`✓`确认 / `?`未验证) — 而`--all`则将多个并行服务器合并为一个概览。文件/执行捕获(`eslogger`)是第四阶段 — 请参见[路线图](#路线图)。
## 安装
从[发布页面](https://github.com/tstefank/pounce/releases)下载二进制文件,或从源代码构建(Go 1.22+):
```sh
go build -o pounce ./cmd/pounce
```
## 使用
封装MCP服务器 — 在您已经运行的命令前加上`pounce wrap --`:
```sh
pounce wrap -- npx -y @modelcontextprotocol/server-filesystem /tmp
```
Pounce启动真正的服务器,按字节转发标准输入/输出,并将每个JSON-RPC消息记录到`~/.pounce/sessions/<id>.jsonl`。所有pounce输出都发送到标准错误流;标准输出保持纯协议流,因此可以直接作为`command`输入到任何MCP客户端配置中。
然后重放工具调用时间线:
```sh
pounce view # 最近的会话
pounce view --session <id> # 特定的会话ID或.jsonl路径
pounce view --color=never # 禁用ANSI颜色(默认情况下自动检测)
```
示例输出:
```
会话 20260607-202936.112
命令:npx -y @modelcontextprotocol/server-filesystem /tmp
22:29:36.122 -> 初始化 -> ok(安全文件系统服务器 0.2.0,协议 2025-11-25)
22:29:36.543 -> 工具/列表 -> 14 个工具 [create_directory, directory_tree, edit_file, …]
22:29:36.544 <- 根目录/列表 -> 1 个根目录 [file:///Users/tomasz/pounce]
22:29:51.454 -> 工具/调用 list_allowed_directories {} -> ok
22:29:58.034 -> 工具/调用 read_text_file {"path":"…/.mcp.json"} -> ok
总结:5 个请求(2 个工具调用,0 个错误),1 个通知
```
### 集成到客户端
任何MCP客户端配置都可以使用 — 只需添加`pounce wrap --`前缀。例如,项目`.mcp.json`:
```json
{
"mcpServers": {
"filesystem": {
"command": "pounce",
"args": ["wrap", "--", "npx", "-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
}
}
}
```
### 操作系统真实数据(网络层,选择性)
第一阶段(上文)是**声明的**意图 — 工具调用。要查看这些调用导致的**实际**进程网络活动,请运行捕获守护进程一次。这是一个选择性升级:**需要root权限,但无需完全磁盘访问权限**。
```sh
sudo pounce daemon # 特权捕获(系统tcpdump on pktap)
```
在守护进程运行时,`pounce wrap`会自动将其子进程PID子树报告给守护进程(无需标志,无需shim侧权限),并将归属的连接记录到同一个会话日志中。如果没有运行守护进程,`wrap`的行为与第一阶段完全相同。然后,`pounce view`会将网络事件交织到时间线中:
```
13:01:27.809 · 网络 udp 172.20.10.1:53 输出 curl 进程 50592
13:01:28.517 · 网络 tcp 104.20.23.154:443 输出 curl 进程 50592 ← 工具导致的连接
```
捕获是只读的,并按PID子树归属。在当前的macOS上,PID来自系统tcpdump的pktap元数据(`-k NP`);会话日志记录`tcpdump`/OS版本以备验证。
### 端到端测试
[`scripts/demo-e2e.sh`](scripts/demo-e2e.sh)(需要`sudopounce daemon`,无完全磁盘访问权限)针对[`examples/trojan-fetch-server.mjs`](examples/trojan-fetch-server.mjs)运行所有三个阶段 — 一个MCP服务器,其`fetch`工具诚实地获取URL,但**还**悄悄地连接到一个硬编码的IP地址。pounce记录工具调用,捕获两个连接,通过DNS将合法连接与声明的URL关联,并将硬编码连接标记为未声明的目标。
## 工作原理
三个刻意解耦的部分:
- **意图源(可插拔):** 捕获**声明的**操作 — 工具调用。标准输入/输出分流器是第一个源;HTTP/SSE代理和日志/钩子收集器在同一接口后插槽。
- **操作系统捕获核心(第二阶段+):** **实际**效果 — 通过系统`tcpdump`上的`pktap`(第二阶段)或`eslogger`(第四阶段)进行网络/文件/执行捕获 — 按PID子树归属。
- **关联器(第三阶段):** 通过PID + 时间关联意图↔效果,并在实际行为偏离时发出标志 — 包括连接到没有DNS查找产生的IP地址(硬编码/数据泄露目标),甚至在良性调用窗口内。有关机制和注意事项,请参见[docs/correlation.md](docs/correlation.md)。
## 路线图
- **第一阶段:** 标准输入/输出shim + 协议分流器。无需权限。
- **第二阶段:** 网络真实数据 — 系统`tcpdump`在`pktap`接口(每个进程的网络)。仅限root用户,**无需完全磁盘访问权限**。
- **第三阶段:** 关联 + 差异检测,基于意图 + 网络。**最小可行产品 = 第一至三阶段**,且无需完全磁盘访问权限 — 标语示例验证在此。
- **第四阶段:** `eslogger`文件/执行捕获(读取文件,生成进程)。需要root权限**和完全磁盘访问权限**;选择性增强。
- **第五阶段:** 可读的TUI / 可选的`--web`本地UI。
- **第六阶段:** 打包(goreleaser,Homebrew tap,curl安装程序)。
## 许可证
请参见[LICENSE](LICENSE)。