mcpField
C
cdp

cdp

github.com/browser-use开发496707 次安装8 周前更新
代码仓库

关于 cdp

cdp 是供编码 Agent 使用的浏览器控制技能,提供覆盖 CDP 协议的类型化 SDK 和 browser-harness-js 命令行工具。它通过常驻会话复用与 Chrome 的连接,并允许跨调用保留状态。

主要功能

  1. 01提供类型化 CDP 方法封装
  2. 02命令行执行 JavaScript 片段
  3. 03后台服务维持持久 CDP 会话
  4. 04连接并切换浏览器标签页
  5. 05支持事件订阅与等待

适用场景

  1. 01通过脚本导航和检查网页
  2. 02在多个调用间复用浏览器会话
  3. 03监听页面或网络事件

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/browser-use/browser-harness-js --skill cdp

安装后在 Claude Code 中输入/cdp确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

README.md
LICENSE
.gitignore
interaction-skills/
connection.md
dialogs.md
cookies.md
cross-origin-iframes.md
drag-and-drop.md
downloads.md
dropdowns.md
iframes.md
print-as-pdf.md
screenshots.md
network-requests.md
scrolling.md
shadow-dom.md
tabs.md
uploads.md
viewport.md
sdk/
bun.lock
browser-harness-js
gen.ts
repl.ts
package.json
session.ts
session.test.ts
js_protocol.json
tsconfig.json

详细介绍

CDP — browser-harness-js 技能

自定义生成的 CDP SDK(browser_protocol.json 和 js_protocol.json 中的每个方法都有类型化封装),以及一个持有单个持久化 CDP Session 的小型 HTTP 服务。browser-harness-js CLI 会在首次使用时自动启动服务,并将 JS 代码片段转发给它。

SDK 位于此技能的 sdk/ 目录中。在本文其余部分,<skill-dir> 指 npx skills add 安装此技能的位置(Claude Code:~/.claude/skills/cdp;Cursor:~/.cursor/skills/cdp;其他 Agent 的位置各不相同)。CLI 应已位于 PATH 中,名称为 browser-harness-js。

设置(一次性,首次使用时)

npx skills add 会将技能放入 Agent 的技能目录,但不会将 CLI 添加到 PATH。首次调用前,请确认 CLI 可用;如果不可用,请将其符号链接到 PATH 中的某个目录:

# macOS (Apple Silicon + Homebrew)
command -v browser-harness-js >/dev/null || ln -sf <skill-dir>/sdk/browser-harness-js /opt/homebrew/bin/browser-harness-js

# macOS (Intel) / most Linux — may need sudo
command -v browser-harness-js >/dev/null || ln -sf <skill-dir>/sdk/browser-harness-js /usr/local/bin/browser-harness-js

# Linux without sudo (ensure ~/.local/bin is on PATH)
command -v browser-harness-js >/dev/null || { mkdir -p ~/.local/bin && ln -sf <skill-dir>/sdk/browser-harness-js ~/.local/bin/browser-harness-js; }

如果缺少 bun,CLI 会在首次运行时自动安装它(服务基于 Bun 运行)。设置 BROWSER_HARNESS_SKIP_BUN_INSTALL=1 可选择不自动安装。

使用方法

只需运行 browser-harness-js '<JS>'。首次调用会在后台启动服务;后续调用会连接到同一进程,因此会复用相同的 session、相同的 Chrome WebSocket,以及你设置的所有全局变量。

browser-harness-js 'await session.connect()'
browser-harness-js 'await session.Page.navigate({url:"https://example.com"})'
browser-harness-js '(await session.Runtime.evaluate({expression:"document.title",returnByValue:true})).result.value'

输出是原始结果内容,不会附带 {ok,result} 封装。

结果类型标准输出
字符串原样文本,不带 JSON 引号(例如 Example Domain)
数字 / 布尔值42、true
对象 / 数组(非空)紧凑 JSON(例如 {"frameId":"..."}、[1,2,3])
undefined / null / "" / {} / []空(无输出)

错误会输出到标准错误,退出代码为 1。CDP 错误消息和 JS 堆栈会原样打印,例如:

Error: CDP -32602: invalid params
    at _call (.../session.ts:117:33)
    ...

使用 if browser-harness-js '...'; then ...; else handle_error; fi 检测失败,或检查 $?。

通过 stdin 传入多行代码片段(heredoc)。 注意:多语句代码片段不会自动返回最后一个表达式的值,必须显式写出 return X。作为第一个参数传入的单表达式代码片段则会自动返回。

browser-harness-js <<'EOF'
const tabs = await listPageTargets();
globalThis.tid = tabs[0].targetId;
await session.use(globalThis.tid);
return globalThis.tid;
EOF

CLI 命令

命令行为
browser-harness-js '<js>'如有需要则自动启动服务,执行 JS 并打印结果。
browser-harness-js <<EOF…EOF同上,代码从 stdin 读取。
browser-harness-js --status打印健康状态 JSON(运行时间、连接状态、sessionId);服务未运行时退出代码为 1。
browser-harness-js --start显式启动(如果已运行则不执行任何操作)。
browser-harness-js --stop正常关闭。丢弃会话状态。
browser-harness-js --restart停止并重新启动。
browser-harness-js --logs使用 tail -f 查看服务日志(/tmp/browser-harness-js.log)。

环境变量:CDP_REPL_PORT(默认值为 9876)、CDP_REPL_LOG(默认值为 /tmp/browser-harness-js.log)。

代码片段中的 API

以下全局变量已预加载,无需导入:

  • session — 持久化的 Session。已挂载所有 CDP 域:session.Page、session.DOM、session.Runtime、session.Network……共 56 个域、652 个方法。
  • listPageTargets() — 通过 CDP 的 Target.getTargets 列出真实页面目标(Chrome 144+ 也适用),并过滤掉 chrome:// 和 devtools:// URL。不接受参数,使用已连接的会话。
  • detectBrowsers() — 扫描特定操作系统的配置目录,查找已运行且启用了远程调试的 Chromium 浏览器。返回 [{name, profileDir, port, wsPath, wsUrl, mtimeMs}],按启动时间从新到旧排序。
  • resolveWsUrl(opts) — 根据 {wsUrl} | {port, host?} | {profileDir} 解析 WS URL。对于不带参数的自动检测流程,请直接调用 session.connect()。
  • CDP — 生成的命名空间(CDP.Page、CDP.Runtime……),可用于查询类型名称。
调用 CDP 方法

每个方法都接受一个对象参数,其格式与 CDP 线上参数一致;方法会解析为类型化返回值(不带 result 封装或 id 关联,这些由 SDK 自动处理)。

// no params
await session.DOM.enable()

// required params
await session.Page.navigate({ url: 'https://example.com' })

// all-optional params (object also optional)
await session.Page.captureScreenshot()
await session.Page.captureScreenshot({ format: 'png', quality: 80 })

// returns are stripped to the typed shape
const { root } = await session.DOM.getDocument()
const { nodeId } = await session.DOM.querySelector({ nodeId: root.nodeId, selector: 'h1' })
连接

默认情况下:直接调用不带参数的 session.connect()。 它会扫描特定于操作系统的配置文件目录,查找 DevToolsActivePort 文件,以自动检测正在运行的 Chromium 内核浏览器(Chrome、Chromium、Edge、Brave、Arc、Vivaldi、Opera、Comet、Canary),按最近启动时间排序,并选择 WebSocket 接受连接的第一个浏览器。与操作系统无关,适用于 macOS、Linux 和 Windows。

await session.connect()   // auto-detect

如果想在连接前查看可用浏览器(或让用户选择),请先使用 detectBrowsers():

const found = await detectBrowsers()
// [{ name: 'Google Chrome', profileDir, port, wsPath, wsUrl, mtimeMs }, ...]

显式形式——仅当自动检测选错浏览器,或你已知道要连接的位置时使用:

形式使用时机
{ profileDir }多个浏览器正在运行时,指定目标浏览器。直接读取 <profileDir>/DevToolsActivePort。
{ wsUrl }你已获得 ws://…/devtools/browser/<uuid>(例如从其他位置管道传入)。
await session.connect({ profileDir: '/Users/<you>/Library/Application Support/Google/Chrome' })
await session.connect({ wsUrl: 'ws://127.0.0.1:9222/devtools/browser/<uuid>' })

各操作系统的配置文件路径——与 { profileDir } 配合使用:

  • macOS:~/Library/Application Support/<Browser>(例如 Google/Chrome、Comet、BraveSoftware/Brave-Browser、Arc/User Data)
  • Linux:~/.config/<browser>(例如 google-chrome、chromium、BraveSoftware/Brave-Browser)
  • Windows:%LOCALAPPDATA%\<Browser>\User Data(例如 Google\Chrome、Microsoft\Edge、BraveSoftware\Brave-Browser)

每个候选项的 WS 打开超时默认为 5s——运行中的浏览器会在约 100ms 内响应打开/关闭,因此 5s 已经很充裕。只有当 Chrome 显示 Allow 弹窗并等待用户点击时,5s 才会不够。如果预期会出现这种情况,请传入 timeoutMs: 30000:

await session.connect({ profileDir: '/Users/<you>/Library/Application Support/Google/Chrome', timeoutMs: 30_000 })

如果看到 No detected browser accepted a connection——浏览器存在 DevToolsActivePort 文件,但当前没有浏览器在提供 WS 服务。最常见的原因是:已启用远程调试,但用户还没有点击提示中的 Allow。请让用户点击 Allow,然后重试(或增大 timeoutMs)。

选择目标(标签页)

调用 connect() 后,调用一次 session.use(targetId);之后的页面级调用(Page/DOM/Runtime/Network 等)会自动路由到该目标的 sessionId。Browser.* 和 Target.* 调用始终发往浏览器端点。

const tabs = await listPageTargets()                     // no args; uses the connected session
const sid  = await session.use(tabs[0].targetId)
await session.Page.enable()
await session.Page.navigate({ url: 'https://example.com' })

listPageTargets() 使用 CDP 的 Target.getTargets(而不是 /json),因此也适用于 Chrome 144+。它已经过滤掉 chrome:// 和 devtools:// URL。等效的原始调用:

const { targetInfos } = await session.Target.getTargets({})
const tabs = targetInfos.filter(t => t.type === 'page' && !t.url.startsWith('chrome://') && !t.url.startsWith('devtools://'))

要切换标签页:session.use(otherTargetId)。要分离:session.setActiveSession(undefined)。

事件
// Subscribe (returns an unsubscribe fn)
const off = session.onEvent((method, params, sessionId) => { ... })

// Or wait for a single matching event with optional predicate + timeout
await session.Network.enable()
const ev = await session.waitFor(
  'Page.frameNavigated',
  (p) => p.frame.url.includes('example.com'),
  10_000
)
在多次调用间保留状态

每个代码片段都在自己的异步包装器内运行,因此其中的 let/const 声明会在返回时消失。要在后续调用中沿用数据,请将其附加到 globalThis:

browser-harness-js '(await listPageTargets()).forEach((t,i)=>globalThis["tab"+i]=t.targetId)'
browser-harness-js 'await session.use(globalThis.tab0)'
browser-harness-js 'await session.Page.navigate({url:"https://example.com"})'

session 本身、活动的 sessionId 和事件订阅者已由服务保留——只有临时数据才需要全局变量。

连接到正在运行的 Chrome(chrome://inspect 流程)

连接到用户已运行的浏览器时:

  1. 先尝试 await session.connect()(不带参数)——自动检测会通过 DevToolsActivePort 处理所有 Chromium 内核浏览器。如果返回成功,就完成了。
  2. 如果自动检测失败,并提示 No running browser with remote debugging detected,则用户需要启用远程调试。打开检查页面:
    # macOS — prefer AppleScript over `open -a` (reuses current profile, avoids the profile picker)
    osascript -e 'open location "chrome://inspect/#remote-debugging"'
    
    # Linux
    google-chrome 'chrome://inspect/#remote-debugging'     # or: chromium, google-chrome-stable
    
    # Windows (PowerShell)
    Start-Process chrome 'chrome://inspect/#remote-debugging'
    
    只有 macOS 的 AppleScript 路径可以跳过配置文件选择器;Linux/Windows 可能会先提示用户选择配置文件。
  3. 勾选 chrome://inspect 中的“Discover network targets”,然后在 Chrome 提示时点击 Allow。
  4. 如果自动检测选错浏览器(有多个浏览器正在运行,而你想指定某一个):调用 await detectBrowsers() 列出浏览器,然后调用 await session.connect({ profileDir: <the one you want> })。
  5. 如果 session.connect() 返回 No detected browser accepted a connection,说明用户已启用远程调试,但还没有点击 Allow。请让用户点击后重试,或传入 timeoutMs: 30000 等待用户点击。

使用目标(标签页)

  • 过滤 Chrome 内部页面。 listPageTargets() 已经会过滤掉 chrome:// 和 devtools:// URL。如果直接调用 Target.getTargets(),请手动过滤。
  • CDP 目标顺序 ≠ 可见的标签栏顺序。 用户说“我能看到的第一个标签页”时,请使用截图或页面标题来识别——Target.activateTarget 只能切换到已知的 targetId。

查找方法

完整的类型化接口位于 <skill-dir>/sdk/generated.ts(约 655 KB,仅在读取时加载)。每个方法都有 CDP 描述(JSDoc 注释),以及按域命名空间划分的类型化 *Params / *Return 接口。

grep -n "navigate" <skill-dir>/sdk/generated.ts | head

重新生成 SDK

上游协议 JSON 发生变化时,替换 sdk/browser_protocol.json 和/或 sdk/js_protocol.json,然后重新运行:

cd <skill-dir>/sdk && bun gen.ts
browser-harness-js --restart   # pick up the new bindings

文件

所有路径均相对于 <skill-dir>(安装路径,参见本文档顶部)。

  • /usr/local/bin/browser-harness-js → <skill-dir>/sdk/browser-harness-js(CLI)
  • sdk/repl.ts — HTTP 服务(在 127.0.0.1:9876 上运行的 Bun.serve)
  • sdk/session.ts — Session 类(传输、连接、目标路由、事件)
  • sdk/generated.ts — 代码生成输出:每个 CDP 方法均为类型化封装
  • sdk/gen.ts — 代码生成脚本
  • sdk/{browser,js}_protocol.json — 上游协议(随附)

评论

支持 Markdown