<p align="center"> <img src="https://assets.kitploit.com/production/public/readmes/9109/91d664ea266713affb29238906792777021b43fa6627b13f46382f48ad340a61.png" alt="mcpsnoop" width="440"> </p> **MCP 的 Wireshark。** mcpsnoop 位于你的 AI 客户端与 MCP 服务器之间, 记录双方交换的每一个 JSON-RPC 帧,且无需对任何一方做改动。 你可以在终端中实时查看,根据其发现让 CI 失败,或将其作为 OpenTelemetry span 发送到你的 追踪后端。 [](https://github.com/kerlenton/mcpsnoop/actions/workflows/ci.yml) [](https://pkg.go.dev/github.com/kerlenton/mcpsnoop) [](LICENSE) [](https://github.com/marketplace/actions/mcpsnoop) <p align="center"> <img src="https://assets.kitploit.com/production/public/readmes/9109/30466d77be15250e4bab6639e48a4cd5b0d44cd96e48606b60c6bacb67c15c3d.gif" alt="mcpsnoop demo"> </p> ## 问题所在 官方的 [MCP Inspector](https://github.com/modelcontextprotocol/inspector) 以自身作为客户端连接,因此它永远看不到*你的*客户端(Cursor、Claude Code、Codex)实际发送给你的服务器的内容。而任何等待请求到达的工具都无法显示模型从未发起的调用, 或使用错误参数发起的调用。当某个工具悄无声息地未被调用、能力不匹配,或某个调用 直接挂起时,你只能翻查日志并猜测。 **mcpsnoop 则位于真实的数据路径中。** 用它包裹你的服务器命令, 在真实客户端与服务器通信时实时查看每一个 JSON-RPC 帧。 ## 在 CI 中 本页面同时也是 [mcpsnoop GitHub Action](https://github.com/marketplace/actions/mcpsnoop) 的列表页, 因此这里给出其全部内容。它会检查捕获的会话,将每个发现作为 代码扫描警报提交,并根据你所设置的门槛让任务失败。 ```yaml permissions: security-events: write contents: read steps: - uses: kerlenton/[email protected] with: session: artifacts/session.jsonl ``` 没有现成的抓包可用?用你自己的客户端录制一次会话,提交它,然后让该 action 针对每个构建重放它。CI 中不运行任何模型,也不需要 API 密钥。重放会像任何抓包一样被检查,并且当服务器的表现比录制时更差时,该任务也会失败。通过 Streamable HTTP 访问的服务器会获得带有其端点的 `target`,而录制内容可在任一传输方式上重放。 ```yaml steps: - uses: kerlenton/[email protected] with: replay: tests/mcp/claude-code-session.jsonl server: node dist/index.js ``` 固定你想要的任意版本。最新版本位于 [releases 页面](https://github.com/kerlenton/mcpsnoop/releases)。每个输入和 输出都在下方的 [The GitHub Action](#the-github-action) 中,检查可能失败的每个 信号都在 [docs/CI.md](https://github.com/kerlenton/mcpsnoop/blob/main/docs/CI.md) 中。 ## OpenTelemetry MCP 修订版 2026-07-28 弃用了协议级 Logging,并建议 实现迁移到 stdio 上的 `stderr`,或迁移到 OpenTelemetry 以实现结构化 可观测性。mcpsnoop 无需改动客户端或服务器即可覆盖两者。 stdio 服务器的 `stderr` 行会与其帧一起出现在流中,并且 每个请求都会变成一个遵循 [OpenTelemetry MCP 语义约定](https://github.com/open-telemetry/semantic-conventions-genai/tree/main/model/mcp)的 span。 ```bash mcpsnoop export -T otlp -o trace.json # spans from a capture mcpsnoop --otlp-endpoint http://localhost:4318/v1/traces -- node build/index.js # spans as calls complete ``` 一次工具调用以名为 `tools/call search` 的 span 到达,携带 `mcp.method.name`、`gen_ai.tool.name` 和 `error.type`,因此它会落入为 MCP 构建的仪表盘,而不是为 mcpsnoop 构建的仪表盘。Prometheus 的实时工具指标 只需一个标志即可启用。两者都记录在 [docs/OPENTELEMETRY.md](https://github.com/kerlenton/mcpsnoop/blob/main/docs/OPENTELEMETRY.md) 中。 ## 快速开始 无需任何配置,即可立即体验。 ```bash mcpsnoop demo ``` 要在实际场景中使用它,请将你的服务器包装进客户端的 MCP 配置中。 ```json { "mcpServers": { "my-server": { "command": "mcpsnoop", "args": ["--", "node", "build/index.js"] } } } ``` `--` 之后的所有内容就是通常用于启动服务器的命令。替换为你已经在使用的任何命令,例如 `python server.py`、`npx -y @scope/server` 或编译后的二进制文件。 在 Claude Desktop 上,你无需手动进行该编辑。 ```bash mcpsnoop wrap my-server # route my-server through mcpsnoop mcpsnoop unwrap my-server # put it back ``` `wrap` 会找到 `claude_desktop_config.json`,首次运行时将其复制为 `claude_desktop_config.json.mcpsnoop.bak`,并且只重写该服务器的条目, 因此你的格式设置和其他所有服务器都保持不变。 在重写后的条目中,键会按字母顺序返回。`unwrap` 会恢复该文件,并在没有任何服务器被包装后删除备份。 无论执行哪一步,之后都要重启 Claude Desktop,因为 MCP 服务器只在启动时启动一次。 然后照常使用你的客户端并打开 UI。 ```bash mcpsnoop ``` 无需标志、无需套接字路径、无需记住启动顺序。shim 与 UI 会自行找到彼此,UI 会从磁盘回填过去的会话。 对于可流式 HTTP 服务器,将 mcpsnoop 作为反向代理运行。 ```bash mcpsnoop http --target http://localhost:3000/mcp --listen :7000 ``` 每个响应的 HTTP 状态都会显示在流中,因此即使某个响应本身不携带任何 JSON-RPC 消息,它仍然是一个可见的帧,而不是什么都没有。这涵盖了 401 质询、因 Origin 被拒绝而返回的 403、确认通知的 202,以及目标完全无法访问时的 502。401 的 `WWW-Authenticate` 头会原样保留并显示在检查器中,因为它指明了认证方案以及接下来要访问的资源元数据。在 TUI 中用 `status:401` 按状态过滤,或用 `status:err` 按任意失败过滤。4xx 或 5xx 计为错误,因此默认的 `mcpsnoop check` 运行会因此失败,而它所应答的调用会被视为失败,而不是仍在等待。 没有自己的服务器?[实际试一试](https://github.com/kerlenton/mcpsnoop/blob/main/docs/TRY_IT.md),针对一个已发布的测试服务器,由你自己的客户端驱动。要在会话结束后检查会话,请参阅[从日志回顾过去的会话](https://github.com/kerlenton/mcpsnoop/blob/main/docs/POST_MORTEM.md)。 ## 安装 ### npm 无需 Go 工具链。大多数 MCP 服务器是用 Node 或 Python 编写的,因此这是最快捷的入门方式。 ```bash npx mcpsnoop -- node build/index.js ``` npm 包本身不包含任何代码。六个平台包各自携带一个构建版本,npm 只会安装与你机器匹配的那一个,因此安装时无需下载任何内容,也无需在代理中放行任何东西。若想将其保留在本地而不是每次运行时都重新获取,请执行 `npm i -g mcpsnoop`。 ### Homebrew ```bash brew install mcpsnoop ``` ### Go ```bash go install github.com/kerlenton/mcpsnoop/cmd/mcpsnoop@latest ``` 各平台的预构建二进制文件位于 [Releases](https://github.com/kerlenton/mcpsnoop/releases) 页面。 mcpsnoop 为 bash、zsh、fish 和 PowerShell 提供补全功能。运行 `mcpsnoop completion <shell> --help` 查看设置步骤,其中涵盖启用补全 以及适用于你所用操作系统的安装路径。 ## 命令 | 命令 | 作用 | |---|---| | `mcpsnoop -- <server>` | 将 stdio 服务器包装为透明垫片 | | `mcpsnoop` | 打开实时 TUI | | `mcpsnoop --metrics-listen <addr>` | 运行无头 hub 并暴露实时 Prometheus 指标 | | `mcpsnoop http --target <url>` | 代理 streamable-HTTP 服务器 | | `mcpsnoop export` | 将会话渲染为 json、html、text、har 或 otlp | | `mcpsnoop check` | 在出现错误、无效帧、警告(例如工具结果违反其自身的 outputSchema)、路由不匹配、挂起的调用、迟到的结果、可能执行了两次的调用、工具漂移、放宽或未作任何说明的注解,或超出延迟预算时使 CI 失败 | | `mcpsnoop baseline` | 检查、接受或重置受信任的工具定义 | | `mcpsnoop diff` | 比较两个已捕获会话中的工具和调用 | | `mcpsnoop open` | 在 TUI 中打开已保存的会话 | | `mcpsnoop mock <session>` | 将已捕获的会话作为 stdio MCP 服务器回放 | | `mcpsnoop replay <session> -- <server>` | 通过 stdio 或 Streamable HTTP,用录制会话的客户端侧驱动服务器,并将结果与录制内容进行比较 | | `mcpsnoop inventory` | 列出本机上所有通过 mcpsnoop 运行过的服务器 | | `mcpsnoop stats` | 将每个已存储的捕获汇总为每个服务器和工具一行 | | `mcpsnoop clients` | 按名称和版本显示每个 MCP 客户端在网络上实际做了什么 | | `mcpsnoop prune` | 删除早于截止时间的已保存会话日志 | | `mcpsnoop wrap <server>` | 将 Claude Desktop 的某个服务器路由通过 mcpsnoop | | `mcpsnoop unwrap <server>` | 将该服务器的条目恢复为原来的样子 | | `mcpsnoop remote <user@host>` | 打印 SSH 隧道命令 | | `mcpsnoop demo` | 播放一个脚本化会话 | 运行 `mcpsnoop help` 查看完整列表,或运行 `mcpsnoop help <command>` 查看某个命令的标志。 ## 对比 以下每个工具回答的是不同的问题,而一个工具所处的位置决定了它能回答哪些问题。 | 工具 | 所处位置 | 用途 | |---|---|---| | [MCP Inspector](https://github.com/modelcontextprotocol/inspector) | 它自己的客户端,连接到你的服务器 | 在浏览器中或通过可脚本化的 CLI 手动试用服务器 | | [Snyk Agent Scan](https://github.com/snyk/agent-scan) | 它自己的客户端,启动或连接你配置中的服务器以读取其工具 | 扫描你机器上的 agent、MCP 服务器和技能,以发现提示注入和漏洞 | | [MCP Observatory](https://github.com/KryptosAI/mcp-observatory) | 它自己的客户端,启动你的服务器以进行测试、记录和验证 | 在 CI 中进行安全和回归测试,具备攻击模拟、schema 漂移,以及记录、回放和验证功能 | | [mcprec](https://github.com/erphq/mcprec) | 位于网络上,客户端与服务器之间 | 录制一次会话,并在无网络的情况下将其作为测试夹具回放 | | mcpsnoop | 位于网络上,客户端与服务器之间 | 观察你真实的客户端和服务器说了什么,针对每个构建回放真实客户端的会话,据此使 CI 失败,并将其导出为 OpenTelemetry | 作为自身客户端的工具只能看到它自己发起的调用,而看不到你的客户端发起的调用,因此它无法显示你的模型从未发起的调用,或以错误参数发起的调用。扫描器在你信任某个定义之前读取它。mcpsnoop 则观察在你信任之后发生了什么,包括该定义之后是否发生变化。 ## 工作原理 <p align="center"> <picture> <source media="(prefers-color-scheme: dark)" srcset="assets/architecture-dark.svg"> <img alt="mcpsnoop 位于你的 AI 客户端与 MCP 服务器之间的管道中,将每个 JSON-RPC 帧复制到实时终端 UI" src="https://raw.githubusercontent.com/kerlenton/mcpsnoop/HEAD/assets/architecture-light.svg" width="760"> </picture> </p> mcpsnoop 在一个二进制文件中承担两种角色。`mcpsnoop -- <server>` 是你的客户端所生成的透明 shim,它逐字节转发数据,同时将每一帧的副本发送到 hub。不带参数的 `mcpsnoop` 就是该 hub 及其实时 TUI。它们通过一个众所周知的 socket 和磁盘上的日志进行配对,因此两者都不必先启动。 由于它位于实际的管道中,而不是像 Inspector 那样位于一旁,因此无论服务器是用什么语言编写的,它都能准确看到你的真实客户端与服务器之间的通信内容。关于其内存和磁盘的界限,请参见 [docs/HOW_IT_WORKS.md](https://github.com/kerlenton/mcpsnoop/blob/main/docs/HOW_IT_WORKS.md)。 ## GitHub Action 以下所有内容都是该 action 为你完成的工作。它会安装 mcpsnoop、检查捕获内容、将发现结果归档到 Security 选项卡,并在你设置门禁的条件上使任务失败。 ```yaml permissions: security-events: write contents: read steps: - uses: kerlenton/[email protected] with: session: artifacts/session.jsonl ``` 固定一个发布版本,任选其一。最新版本在 [releases 页面](https://github.com/kerlenton/mcpsnoop/releases)。这里刻意不提供 浮动的 `v1`。固定的发布版本同时也是该 action 安装的二进制文件,因此两者永远不会 不一致,也不存在会过时的默认版本。 | 输入 | | |---|---| | `session` | 要检查的 `.jsonl` 捕获文件,或包含它们的目录,相对于仓库根目录。提供此项或 `replay` | | `replay` | 已提交的录制,用于对 `server` 或 `target` 进行重放,随后会检查其捕获,并在出现回归时同样使任务失败 | | `server` | 启动被测服务器的命令,按命令行方式加引号。对于 stdio 服务器,与 `replay` 一起使用时为必填。与 `target` 一起使用时,它会先启动一个 HTTP 服务器,若已有服务器在运行则可省略 | | `target` | 要重放的目标 Streamable HTTP 端点,例如 `http://localhost:3000/mcp`。凭据通过 `replay-args` 以 `--header` 传入,步骤日志中其值会隐藏显示 | | `replay-args` | 其他 `replay` 标志,例如 `--read-only`。`--out` 会被拒绝,因为由该 action 决定捕获的存放位置 | | `fail-on` | 同 `--fail-on`,默认值与 CLI 的默认值一致 | | `args` | 其他 `check` 标志,按命令行方式加引号。`--format` 会被拒绝,因为该 action 会读取报告 | | `upload-sarif` | 将报告发送到代码扫描。`true` | | `category` | 代码扫描命名空间。`mcpsnoop`。在矩阵的每个分支中应有所不同,否则各分支会互相覆盖 | | `fail-on-findings` | 在发现问题时使任务失败。`true`。设为 `false` 可仅提交告警,由代码扫描的必需检查来决定 | | `version` | 要安装的 mcpsnoop 版本。默认为你固定的发布版本 | | `install` | 当 mcpsnoop 已在 PATH 中时为 `false`,这是在未构建发布版本的平台上使用的方式 | 输出为 `outcome`、`sarif`、`exit-code` 和 `capture`,即被检查的捕获路径,在使用 `replay` 时即为重放写入的捕获,值得作为工件保留。`outcome` 为 `passed`、 `findings` 或 `error`,第三种值得单独处理。它表示没有检查任何内容,这与没有发现问题并不相同。**无法执行检查的运行会使任务失败,无论 `fail-on-findings` 如何设置**,因为一个什么都没验证却显示为绿色的流水线比失败的流水线更糟。 该任务需要 `security-events: write`,否则上传会返回 403。在没有代码扫描的仓库中设置 `upload-sarif: false`。不使用该 action 运行相同检查、它可能因之失败的信号以及退出码,见 [docs/CI.md](https://github.com/kerlenton/mcpsnoop/blob/main/docs/CI.md)。 ## 文档 | 页面 | 内容 | |---|---| | [在 CI 中检查会话](https://github.com/kerlenton/mcpsnoop/blob/main/docs/CI.md) | 对每次构建重放录制、`check` 可能因之失败的每个信号、退出码、断言、JUnit 和 SARIF 报告、比较两个会话,以及不使用该 action 进行接入 | | [OpenTelemetry 和 Prometheus](https://github.com/kerlenton/mcpsnoop/blob/main/docs/OPENTELEMETRY.md) | span 及其携带的内容、将其流式传输到 collector,以及实时工具指标 | | [使用 mcpsnoop](https://github.com/kerlenton/mcpsnoop/blob/main/docs/USING.md) | 按键、过滤、导出格式、重放、服务器向你的用户请求了什么、上下文成本、跨捕获的统计、每个客户端实际发送的内容、服务器清单、远程机器和配置文件 | | [脱敏](https://github.com/kerlenton/mcpsnoop/blob/main/docs/REDACTION.md) | 在捕获期间或从已有捕获中清除机密 | | [工作原理](https://github.com/kerlenton/mcpsnoop/blob/main/docs/HOW_IT_WORKS.md) | 内存限制、历史记录上限,以及清理旧日志 | | [实际试用](https://github.com/kerlenton/mcpsnoop/blob/main/docs/TRY_IT.md) | 一个已发布的测试服务器,由你自己的客户端驱动 | | [回顾过去的会话](https://github.com/kerlenton/mcpsnoop/blob/main/docs/POST_MORTEM.md) | 会话日志的存放位置以及事后如何打开 | | [捕获格式](https://github.com/kerlenton/mcpsnoop/blob/main/docs/CAPTURE_FORMAT.md) | 会话日志每一行包含的内容以及其中永远不会改变的部分,并附有供读取捕获的工具使用的 JSON Schema | ## 安全 mcpsnoop 会运行你包装的服务器命令,因此只包装你信任的服务器,并在容器中运行不受信任的服务器。它绝不会执行你未放入客户端配置中的任何内容。 对于远程工作流,请使用 SSH 隧道或 SSH 文件传输,以便传输认证、加密、主机验证、密钥轮换和审计策略都保留在你现有的 SSH 配置中。 ### 捕获内容可能对你的终端造成的影响 捕获内容是对方选择的数据。帧和 stderr 行可能携带终端转义序列,而终端会对其接收到的任何内容做出反应,其影响范围从显示混乱一直到通过 OSC 52 写入你的系统剪贴板。mcpsnoop 只保留它自己生成的颜色序列,并在帧到达你的终端之前丢弃所有其他转义序列。表格单元格会对控制字符进行转义显示,以便你能看到它们确实存在;检查器会完整地将其列出;剪贴板副本携带的是转义后的形式,因此没有任何内容对你隐藏,也没有任何内容被执行。 ### 捕获内容包含什么 捕获的帧可能包含提示词、工具参数、凭据和工具结果。[docs/REDACTION.md](https://github.com/kerlenton/mcpsnoop/blob/main/docs/REDACTION.md) 介绍了在捕获过程中或捕获之后如何清理这些内容。如需报告漏洞,请参阅 [SECURITY.md](https://github.com/kerlenton/mcpsnoop/blob/main/SECURITY.md)。 ## 贡献 欢迎提交 issue 和 pull request。详情请参阅 [CONTRIBUTING.md](https://github.com/kerlenton/mcpsnoop/blob/main/CONTRIBUTING.md)。 ## 许可证 [MIT](https://github.com/kerlenton/mcpsnoop/blob/main/LICENSE)