内容
# cdp-toolkit
**一款轻量级、即插即用的替代品 [`chrome-devtools-mcp`](https://github.com/ChromeDevTools/chrome-devtools-mcp),不会卡死你的代理。** 它通过原始 DevTools 协议驱动你指定的单个 Chrome 标签页,并对每个调用都有一个有界限的超时时间 - 因此卡住的页面会返回一个干净的错误,而不是挂起你的代理并强制重启 `/mcp`。同样的想法,一个标签页,没有扩展 - 加上一个内置的网络模拟后端。 **33 种工具。**
> 适用于 AI 代理开发人员和 Claude Code / Cursor 用户,他们需要 **一个可靠驱动的已知标签页** - 而不是 Puppeteer 管理的浏览器。
[](https://github.com/sblattj/cdp-toolkit/actions/workflows/ci.yml)
[](https://www.npmjs.com/package/cdp-toolkit)
[](./LICENSE)
[](https://modelcontextprotocol.io)
<!-- Demo: record with `vhs docs/demo.tape` (see docs/demo.tape) and commit docs/demo.gif -->

## 简而言之
你让一个 AI 代理 - Claude Code、Cursor、任何 MCP 主机 - 控制一个 Chrome 标签页:点击、输入、读取页面、截取屏幕截图,甚至可以模拟 API 响应来测试 UI,而无需后端存在。 [`chrome-devtools-mcp`](https://github.com/ChromeDevTools/chrome-devtools-mcp) 也可以做到这一点,但它运行一个完整的 Puppeteer 浏览器,并与所有打开的标签页通信 - 这就是为什么一个繁忙的标签页可以冻结它,并迫使你重启服务器。
`cdp-toolkit` 保持简单:**与你指定的一个标签页建立一个连接,并对每个操作设置时限。** 当某些操作卡住时,你会收到一个错误 - 而不是一个冻结的代理。 同样的功能,你可以做到以前的事情,但不会出现卡死和重启。
这就是全部。技术细节 - 无扩展、延迟域启用、`Network.enable` 挂起 - 在下面。
## 为什么存在
如果 [`chrome-devtools-mcp`](https://github.com/ChromeDevTools/chrome-devtools-mcp) 曾经 **卡死你的代理在一个繁忙的标签页上**,你已经遇到了它的设计:它管理一个 Puppeteer 浏览器,将每个操作扩展到 **所有** 已连接的目标,并启用 `Network` 域以进行连接,以便它可以被动地缓冲所有内容。这种通用性正是它在你已经知道要驱动哪个标签页时变得脆弱的原因。
`cdp-toolkit` 做出了相反的赌注。每个工具都附加一个直接的 WebSocket 到 **一个** 解析的目标,只启用它需要的 CDP 域,并强制执行 **每个命令的超时**,以便卡住的渲染器永远不会挂起调用者。对于驱动一个你已经拥有的标签页 - 常见的自动化和证据收集情况 - 它在材料上更加健壮。
## cdp-toolkit vs chrome-devtools-mcp
| | **cdp-toolkit** | **chrome-devtools-mcp** |
|---|---|---|
| **目标范围** | 一个已解析的标签页 (`active` / `index:N` / `url:` / `title:`) | 所有已连接的目标 - 无扩展可能卡在一个无关的标签页上 |
| **`Network.enable`** | 延迟 - 仅当工具需要时 | 急切连接 - 一个已知挂起的忙碌渲染器 |
| **每个命令超时** | ✅ 有界限 (`CDP_TIMEOUT_MS`, 默认 15 秒) - 拒绝,永不挂起 | ❌ 无 - 卡住的渲染器无限期阻塞 |
| **元素引用** | 无状态 `backendDOMNodeId` (按需解析) | 服务器端句柄表 (可能漂移/过期) |
| **网络模拟** | ✅ 持久的每个目标假后端 | ❌ 不可用 |
| **运行时依赖** | CDP/CLI 层:原生 `WebSocket` + `fetch` (MCP 服务器仅添加 MCP SDK) | Puppeteer 堆栈 |
| **自动等待/重试** | ❌ 单次 - 重快照之间步骤 | ✅ Puppeteer 的自动等待信封 |
**使用 `chrome-devtools-mcp`** 如果您需要多目标自主或 Puppeteer 的自动等待/重试在一个未知的页面上。 **使用 `cdp-toolkit`** 当您有一个标签页在手上,需要它不会挂起。它们共存 - `cdp-toolkit` 的工具命名为 `mcp__cdp-toolkit__*`,与 `mcp__chrome-devtools__*` 不同。
## 是否适合你?
**是的,如果你 -**
- 驱动一个已知的标签页从 Claude Code / Cursor / 任意 MCP 主机,并希望它永远不会卡死;
- 需要 **模拟后端** 以构建或测试 UI,而无需后端存在;
- 曾被急切的 `Network.enable` 挂起在一个忙碌的渲染器上。
**可能不是,如果你 -**
- 需要跨浏览器(Firefox / WebKit)- 使用 [`playwright-mcp`](https://github.com/microsoft/playwright-mcp);
- 需要 Puppeteer 的自动等待/重试信封,用于一个未知、变化的页面;
- 希望一个服务器扩展到 **所有** 打开的标签页。
## 快速开始(约 30 秒)
```bash
# 1. 启动 Chrome/Chromium 并打开 DevTools 端口
open -a "Google Chrome" --args --remote-debugging-port=9222
# (Linux: google-chrome --remote-debugging-port=9222)
# 2. 将 cdp-toolkit 添加到 Claude Code 的用户范围(每个项目,无需安装步骤)
claude mcp add cdp-toolkit --scope user -- npx -y cdp-toolkit # 或:bunx -y cdp-toolkit
# 3. 在 Claude Code 中,调用一个工具来证明它有效:
# mcp__cdp-toolkit__list_pages
```
就这样。工具以 `mcp__cdp-toolkit__<tool>` 的形式出现。服务器 **延迟连接** 到 Chrome **每个调用**,因此即使 Chrome 没有运行,它也能干净地加载。
更喜欢 **CLI**?每个工具都可以直接运行:
```bash
npx -y --package cdp-toolkit cdp list_pages # 或交换 npx → bunx
npx -y --package cdp-toolkit cdp navigate_page --target index:0 --url https://example.com
# …或从克隆:`bun run src/cli.ts <tool> …`
```
**要求:** Node ≥ 22 **或** Bun ≥ 1.1 - `npx -y cdp-toolkit` 和 `bunx -y cdp-toolkit` 都有效(已发布的二进制文件是纯 Node ESM)。带有 `--remote-debugging-port=9222` 的 Chrome/Chromium。烟雾检查端口:`curl -s http://127.0.0.1:9222/json/version`.
### MCP 客户端设置
<details>
<summary><b>Claude Code</b></summary>
```bash
claude mcp add cdp-toolkit --scope user -- npx -y cdp-toolkit # 或:bunx -y cdp-toolkit
claude mcp get cdp-toolkit # 状态(应显示 ✓ 已连接)
```
新注册的服务器在下一次 Claude Code 启动时加载;在现有的会话中,通过 `/mcp` 重新连接。
</details>
<details>
<summary><b>Cursor / Windsurf / 任意 MCP 主机</b></summary>
将以下内容添加到您的 MCP 配置文件(例如 `~/.cursor/mcp.json`):
```json
{
"mcpServers": {
"cdp-toolkit": { "command": "npx", "args": ["-y", "cdp-toolkit"] }
}
}
```
</details>
<details>
<summary><b>从本地克隆</b></summary>
```bash
git clone https://github.com/sblattj/cdp-toolkit && cd cdp-toolkit && bun install
claude mcp add cdp-toolkit --scope user -- bun run "$(pwd)/src/mcp.ts"
bun run mcp:smoke # 启动服务器 + 真实的初始化/工具列表/工具调用往返
```
</details>
## 关键功能
- **单目标可靠性。** 一个 WebSocket 连接到一个已解析的目标,对每个 CDP 命令都有一个有界限的超时时间,延迟域启用和无状态元素引用。没有广播步骤可以在一个卡住的标签页上阻塞。
- **网络模拟 - 在后端存在之前构建/测试 UI。** `mock_request` 启用一个持久的每个目标假后端:返回预先准备好的响应,强制错误或注入延迟/错误率。模拟在重新加载和导航之间存活,直到 `clear_mocks`。
- **完整的 chrome-devtools-mcp 兼容性 + 额外功能。** 所有 29 个上游工具,加上 `performance_trace` (一个强大的单次跟踪),Lighthouse 审计和堆快照。 33 个单一用途的工具 - 无发现开销,并且它们与 `chrome-devtools-mcp` 在不同的命名空间中共存。
## 为什么原始 CDP 胜过 MCP 的已知目标
下面的每个要点都以你可能遇到的症状开始,然后是原因,最后是解决方法。
- **当打开一个繁忙的后台标签页时,您的代理会在调用时卡住** → 那就是 **所有目标的扩展**:每个操作都会广播到所有连接的目标。 cdp-toolkit 解析 **一个** 目标 (`active | index:N | url:<substr> | title:<substr> | <targetId>`) 并仅将一个 WebSocket 连接到该页面。
- **一个工具无限期挂起,永远不会返回** → MCP 的急切 `Network.enable` 在一个忙碌或挂起的渲染器上是一个已知的楔入点。 cdp-toolkit 仅在需要时 **延迟启用** 域(记录器启用 `Network`/`Runtime`/`Log`;大多数工具仅触及 `Page`/`Runtime`/`DOM`)。
- **无法绑定慢速调用** → `CdpConnection.send()` 对 **每个** 命令强制执行 `CDP_TIMEOUT_MS`(默认 15 秒),并且 **拒绝而不是挂起**,因此一个卡住的渲染器永远不会无限期阻塞调用者。
- **元素句柄在调用之间漂移** → 一个 `uid` **是** 一个 CDP `backendDOMNodeId`,按需解析为 `DOM.resolveNode`。 没有服务器端句柄表来漂移或过期。
权衡是通用性:该工具包一次针对一个已知的页面,并且 **不** 复制 Puppeteer 的自动等待/重试信封。 在步骤之间重新 `take_snapshot` 而不是期望隐含的等待。
## 网络模拟 - 用于构建/测试 UI 的假后端
在后端(或其数据)存在之前,构建和测试 UI。 `mock_request` 启用一个持久的每个目标拦截会话;模拟多个端点,重复调用它,然后迭代页面 - 模拟在重新加载和导航之间存活,直到 `clear_mocks`。
```bash
# 返回空搜索结果并重新加载以查看 UI 如何渲染零状态
cdp mock_request --urlPattern '*/api/search*' --body '{"results":[],"total":0}' --reload true
# 强制端点出错(UI 是否显示干净的错误或挂起?)
cdp mock_request --urlPattern '*/api/search*' --action fail --errorReason Failed --reload true
# 弹性:失败 30% 的调用 + 添加 800ms 延迟
cdp mock_request --urlPattern '*/api/*' --failRate 0.3 --delayMs 800
cdp list_mocks
cdp clear_mocks --all true
```
> 跨源提取(例如,从 `data:` 页面)需要在模拟中添加 `Access-Control-Allow-Origin` 头:`--json '{"urlPattern":"*api*","body":"{}","headers":{"Access-Control-Allow-Origin":"*"}}'`。持久模拟会话存在于长期运行的 MCP 服务器进程中;在一次调用中,每个 `mock_request` 都是一个独立的进程,因此使用 `--reload true` 在单个调用中应用和观察。
## CLI 用法
```bash
# 按 MCP 名称运行任何工具;参数来自 --json 和/或 --key 值标志。
cdp <tool> [--target <sel>] [--json '<obj>'] [--<key> <value> ...]
cdp --list # 列出每个可用的工具名称
cdp list_pages
cdp navigate_page --target index:0 --url https://example.com
cdp take_snapshot --target url:example --interactiveOnly true
cdp click --target index:0 --uid 42
cdp evaluate_script --json '{"expression":"document.title"}'
cdp take_screenshot --target url:example --fullPage true
cdp lighthouse_audit --url https://example.com --json '{"categories":["performance"]}'
```
**参数解析:** 第一个位置参数是工具名称。 `--json '<obj>'` 将 JSON 对象合并到参数中(首先应用)。 `--target <sel>` 设置 `args.target`。重复的 `--key value` 对变成 `args.key`,强制转换(`true`/`false` → 布尔值,数字字符串 → 数字,否则字符串);一个裸 `--flag` 为 `true`。 显式标志覆盖来自 `--json` 的键。 输出是 `JSON.stringify(result, null, 2)` 输出到 stdout(退出 0);在任何抛出时,`{"error":"<message>"}` 输出到 stderr,进程退出 1。
### 程序化使用
```ts
import { TOOLS, withPage, resolveTarget, CdpError } from "cdp-toolkit";
const pages = await TOOLS.list_pages({});
await TOOLS.navigate_page({ target: "index:0", url: "https://example.com" });
```
### 环境变量
| 环境变量 | 默认值 | 用途 |
|---|---|---|
| `CDP_BASE` | `http://127.0.0.1:9222` | DevTools HTTP 源(驱动发现和 Lighthouse 端口)。 |
| `CDP_TIMEOUT_MS` | `15000` | 每个命令的超时时间。 |
| `CDP_ARTIFACT_DIR` | `/tmp/cdp-toolkit` | 截图、跟踪、堆快照、Lighthouse 报告、录制缓冲区。 |
| `CDP_STATE_DIR` | `/tmp/cdp-toolkit` | `select_page` 选择的目标文件、正在进行的跟踪状态。 |
## 工具(29 个兼容工具 + 4 个超集 = 33 个)
29 个兼容工具与 `chrome-devtools-mcp` 相同;4 个超集工具(`performance_trace` + `mock_request`/`list_mocks`/`clear_mocks` 组)是工具包的附加功能。 每一行都注明了底层 CDP 方法和精确的兼容性差距。
| MCP 名称 | CDP 方法 | 兼容性说明 / 差距 |
|---|---|---|
| `list_pages` | `GET /json/list` | `all` 标志还公开了工作线程/后台目标;MCP 只列出页面标签。 |
| `new_page` | `Target.createTarget` | 返回 `{targetId,url}`;不等待导航(使用 `navigate_page`)。 |
| `close_page` | `Target.closeTarget` | 在新 Chromium 返回的空结果上报告 `success:true`。 |
| `select_page` | `Target.activateTarget` + 已选状态文件 | 写入一个平面文件已选目标;`resolveTarget` 不读取它,因此 `active` 仍然意味着 `index:0` 除非工具选择加入。 |
| `navigate_page` | `Page.navigate` / `Page.reload` + 加载事件 | 返回 `{url,frameId,waitedFor}`(无自动快照)。 `waitUntil` 支持 `load`/`domcontentloaded`。 `reload:true`(+ `ignoreCache:true` 进行硬重载)。 |
| `wait_for` | `Runtime.evaluate`(轮询 `innerText`) | 仅文本子字符串等待;超时时抛出而不是返回 `{found:false}`。 |
| `evaluate_script` | `Runtime.evaluate` / `callFunctionOn` | 无实时 `page` / 元素句柄;`args` 为纯 JSON。 仅限主世界上下文。 |
| `take_snapshot` | `Accessibility.getFullAXTree` | uid 为原始 `backendDOMNodeId`(无状态,非顺序)。 一次获取完整树;框架展平。 `interactiveOnly` 为工具包新增功能。 |
| `click` | `Input.dispatchMouseEvent` | 无隐含自动等待/重试 - 解析并一次执行;步骤之间重新快照。 |
| `hover` | `Input.dispatchMouseEvent` (`mouseMoved`) | 与 `click` 相同的单次模式。 |
| `drag` | `Input.dispatchMouseEvent` (press→move→release) | 合成鼠标拖动;近似原生 HTML5 DnD。 |
| `fill` | `Input.insertText` | 原子粘贴类似提交,非字符按键。 |
| `fill_form` | 每个字段:`callFunctionOn` + `insertText` | `{uid|selector,value}` 数组;与插入文本的相同注意事项。 |
| `type_text` | `Input.insertText` | 追加(不先清除);插入文本,非按键。 |
| `press_key` | `Input.dispatchKeyEvent` |精选的命名按键表 + 单个字符;不是完整的 Puppeteer KeyInput 枚举。 |
| `upload_file` | `DOM.setFileInputFiles` | 需要可解析的 `<input type=file>`(uid 或选择器)。 |
| `take_screenshot` | `Page.captureScreenshot` (+ 布局指标) | 剪辑缩放固定在 1。完整页面使用 `captureBeyondViewport` + 布局指标剪辑。 |
| `emulate` | `Emulation.*` / `Network.emulateNetworkConditions` | 无状态:UA/CPU/媒体/网络覆盖在每次调用连接关闭时重置。 无命名设备预设。 |
| `resize_page` | `Emulation.setDeviceMetricsOverride` | 通过 `window.innerWidth/innerHeight` 验证。 覆盖持续应用于目标。 |
| `handle_dialog` | `Page.javascriptDialogOpening` / `handleJavaScriptDialog` | 调用者武装第一个并触发出站(或 `handleDialogForExpression` 以原子方式触发和处理)。 支持等待下一个和自动处理 N 毫秒。 |
| `list_console_messages` | `Runtime`/`Log` 事件(+ `Page.reload`) | `reload:true` 记录控制台 + 网络到唯一捕获文件;默认读取返回最新文件。 参数展平最佳努力。 |
| `get_console_message` | 读取共享的“最新”缓冲区 | 索引到最新捕获;如果超出范围则抛出。 |
| `list_network_requests` | `Network.*` 事件(+ `Page.reload`) | 来自捕获缓冲区的相关行;重定向链折叠为第一行。 无时间分解 / POST 数据。 |
| `get_network_request` | 上述 + `Network.getResponseBody` | 仅通过新的重新加载捕获主体(CDP 从实时会话提供主体);`includeBody` 按 **url** 匹配。 |
| `performance_start_trace` | `Tracing.start` / `dataCollected` | 仅在单个进程内工作 - 实时跟踪缓冲区绑定到其连接。 使用 `performance_trace` 获取健壮性。 |
| `performance_stop_trace` | `Tracing.end` / `tracingComplete` | 必须在与 `performance_start_trace` 相同的 **进程** 中运行;否则抛出明确的错误。 |
| `performance_analyze_insight` | 解析跟踪 JSON 文件 | **CDP 原始近似** 的 MCP 洞察分析器(FCP/LCP/CLS/TBT/长任务);接近但不完全相同。 需要明确的 `tracePath`。 |
| `take_heapsnapshot` | `HeapProfiler.takeHeapSnapshot` | 返回 `{path,bytes,chunks,target}`;不解析快照(在 DevTools 内存面板中加载 `.heapsnapshot`)。 |
| `lighthouse_audit` | **无(非 CDP)** - 启动 `npx --yes lighthouse …` | 工具包的唯一非 CDP 工具。 默认桌面预设。 返回数字类别分数(完整报告在磁盘上)。 |
| `performance_trace` *(超集)* | `Tracing.*` (+ `Page.reload`) | **工具包便利。** 一个强大的单次跟踪:启动 → 可选重新加载 → 捕获 `durationMs` → 结束 → 编写跟踪 JSON → 返回 `{path,bytes,events,metrics}`。优于 `start`/`stop` 对(CDP 跟踪是浏览器全局的,绑定到一个连接)。 |
| `mock_request` *(超集)* | `Fetch.*` (+ `Page.reload`) | **假后端。** 在目标的持久会话上注册一个规则:用预备响应、失败或继续(带有可选的 `delayMs`/`failRate`)完成。 在重新加载后持续到 `clear_mocks`。 请求阶段仅限。 缓存的请求不会被拦截(使用 `reload:true`)。 |
| `list_mocks` *(超集)* | `Runtime.evaluate`(活跃度探测) | 列出带有规则 + 点击次数的活动模拟会话;修剪已关闭的标签页会话。 |
| `clear_mocks` *(超集)* | `Fetch.disable` | 拆除已解析目标的模拟会话(或所有会话,`all=true`)。 |
## 如何构建
```
src/
client.ts # CdpConnection, openPage/withPage/openBrowser, resolveTarget, timeouts
types.ts # Target, TargetSelector, Uid, CDP envelopes
index.ts # TOOLS 注册表(33 个)+ 重新导出的客户端基元
cli.ts # Bun CLI
mcp.ts # stdio MCP 服务器(通过 @modelcontextprotocol/sdk 暴露 TOOLS)
manifest.ts # MCP 服务器宣传的 JSON 模式(每个工具一个)
tools/
pages.ts navigation.ts evaluate.ts snapshot.ts input.ts
screenshot.ts emulation.ts dialogs.ts recorder.ts console.ts
network.ts performance.ts heap.ts lighthouse.ts network_mock.ts
test/
smoke.ts # 安全端到端工具烟雾测试(bun run smoke)
mock-smoke.ts # 端到端网络模拟烟雾测试(bun run mock:smoke)
network_mock.test.ts # 纯逻辑单元测试(bun test)
mcp-smoke.ts # MCP 握手 + 实时工具/调用往返(bun run mcp:smoke)
```
每个模块都以注释结尾,列出它使用的确切 CDP 方法及其兼容性差距。完整的设计合同存在于 [`CONTRACT.md`](./CONTRACT.md)。
## 贡献
问题和 PR 欢迎 - 请参阅 [`CONTRIBUTING.md`](./CONTRIBUTING.md) 和 [`good first issue`](https://github.com/sblattj/cdp-toolkit/labels/good%20first%20issue) 标签。 通过参与,您同意 [代码行为](./CODE_OF_CONDUCT.md)。
## 许可
[MIT](./LICENSE) © Stephen Blatt
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器