AionUi CDP 远程调试集成指南:通过 Chrome DevTools Protocol 与 MCP 驱动应用内浏览器
AionUi 内置了一个可被 AI Agent 直接操作的应用内浏览器,其技术底座是 Chrome DevTools Protocol(CDP)。本文基于 docs/guides/cdp.md 编写,系统讲解 AionUi 如何暴露 CDP 通道、如何在生产环境中开启远程调试、如何为 Cursor / VS Code / Claude Desktop / Codebuddy 配置 chrome-devtools-mcp,并深入源码说明端口分配、安全模型与底层桥接实现。读完本文,你将能够把任意支持 MCP 的 AI 编码工具接入 AionUi 的内置浏览器,让 Agent 执行打开页面、点击、填写表单、截图与导航等真实浏览器操作。
一、背景:AionUi 的 CDP 能力概览
AionUi 是一个开源的 24/7 协作用户端,支持 OpenClaw、Hermes、Claude Code、Codex、OpenCode 等 20+ 种 CLI Agent。为了让 Agent 能够真正"看见"并操作界面,AionUi 内置浏览器(基于 Electron webview 实现)通过 CDP 对外暴露调试通道,任何实现了 CDP 客户端的工具(如 chrome-devtools-mcp、Chrome DevTools 页面)都可以接入。
核心结论(开发模式默认开启):在开发模式下运行 just dev 时,CDP 能力默认启用。官方文档标注的端口是 9230;需要说明的是,从当前仓库源码看,端口分配已经演进为"系统动态分配"模型(详见第三节),9230 是文档与历史实现中的默认保留端口号,实际以运行时设置页展示或 getCdpStatus 返回值为准。
二、在生产环境启用 CDP
在打包发布的生产版本中,CDP 默认关闭,需要用户在界面中手动开启:
- 打开 AionUi 设置 → 系统 → 开发者调试(对应源码 DevSettings.tsx);
- 开启 "启用远程调试 (CDP)" 开关;
- 重启应用使配置生效。
该开关通过 IPC 调用持久化配置。在源码中,设置项渲染后调用 ipcBridge.application.getCdpStatus.invoke() 读取状态,切换开关时调用 ipcBridge.application.updateCdpConfig.invoke({ enabled: checked })(见 DevSettings.tsx 与 BrowserDataSection.tsx)。配置写入用户数据目录下的 cdp.config.json 文件,格式为:
{
"enabled": true
}
注意:配置支持
enabled(布尔值)与历史遗留的port(数字,已不再读取,仅为兼容旧配置保留)两个字段。修改enabled后必须重启应用才能生效,这一点在源码updateCdpConfig的注释中明确标注(见 configureChromium.ts)。
三、源码解读:端口分配与单目标桥的实现
3.1 从"保留端口"到"动态端口"的演进
早期实现曾使用 Chromium 的应用级 remote-debugging-port 开关,并在 9230-9250 段预留端口、通过 ~/.aionui-cdp-registry.json 做实例注册。该方案有两个致命缺陷,源码注释有详细说明(configureChromium.ts):
- 暴露面过大:
remote-debugging-port是应用级开关、没有 per-target ACL,一开启就会把包括主窗口(挂有 preload 桥)在内的所有 WebContents 暴露给本机任意进程,且无需认证; - 端口不可达:移除该开关后 9230-9250 段没有任何进程监听,设置页却仍把保留号当作"当前 CDP 端口"展示并据此生成 MCP 配置,用户拿到的是一个永远连不上的地址。
因此当前实现改由 cdpBridge 提供单目标 CDP 通道:只暴露"侧边浏览器那一个 webview",端口改用 httpServer.listen(0) 让操作系统分配空闲端口,真实端口在桥启动后才能得知,并通过 setActiveCdpPort() 回填(见 configureChromium.ts 与 cdpBridge.ts)。这也解释了为何文档中的 9230 是"默认/历史端口",而运行时的真实端口需要以实际状态为准。
3.2 启停判定优先级
CDP 是否在启动时启用,遵循 环境变量 > 配置文件 > 默认值 的优先级(见 configureChromium.ts):
| 输入 | 取值 | 结果 |
|---|---|---|
AIONUI_CDP_PORT 环境变量 | "0" 或 "false" | 关闭 |
AIONUI_CDP_PORT 环境变量 | 其他非空值 | 开启(数值不再决定端口,仅作开关) |
cdp.config.json 的 enabled | 布尔值 | 依配置 |
| 以上均未设置 | — | 默认开启 |
值得注意的是,当前默认行为是开箱即用(默认开启,含正式版):应用内浏览器若没有这条通道,Agent 就无法操作它,因此源码选择默认启用,同时只绑定 127.0.0.1、不对外暴露;用户仍可在设置里关闭,关闭后 MCP 启动器会拒绝启动,而不是偷偷拉起一个用户看不见的独立 Chrome。
3.3 桥的启动与信息发布
主进程在启动早期动态加载 startCdpBridge(),成功后把真实端口与随机口令写入自身环境变量,供后续子进程继承(见 index.ts):
AIONUI_CDP_ACTIVE_PORT:桥的实际监听端口;AIONUI_CDP_BRIDGE_TOKEN:随机生成的 24 字节十六进制访问口令。
渲染进程侧的 WebviewHost.tsx 在 webview dom-ready 时通过 reportBrowserWebContentsId 把当前 webview 的 webContents id 上报给主进程,桥随之附加到该目标;每次导航或切换 tab 后都会重新上报,保证通道始终指向用户正在看的页面。附加时有严格校验(cdpBridge.ts):只接受 getType() === 'webview' 的 WebContents,从机制上杜绝把主窗口交出去。
3.4 安全模型(务必理解)
桥的 HTTP 发现段(/json/version、/json/list)不校验口令,响应中直接携带带 token 的 WebSocket 地址;口令校验发生在 WebSocket 段(路径固定为 /aionui-cdp),不带正确 token 的连接一律 403 Forbidden(cdpBridge.ts)。源码注释对此的结论非常直白:
- token 的作用是阻止盲连(不知道端口、没读过发现段的连接被拒),不是身份证明——同机同用户的任意进程扫到端口后一次 GET 即可取回 token;
- 对本机进程而言,真正的屏障只有"listen(0) 随机分配的临时端口"这一层;
- 该设计将"同用户本机进程"视为可信边界,与 Chrome 原生
remote-debugging-port的/json/version处于同一安全水平。
四、配置 MCP chrome-devtools
4.1 配置文件位置
将 chrome-devtools-mcp 注册进 IDE 的 MCP 配置,配置文件路径因 IDE 而异:
| IDE | 配置路径 |
|---|---|
| Cursor | ~/.cursor/mcp.json |
| VS Code | ~/.vscode/mcp.json |
| Claude Desktop | ~/Library/Application Support/Claude/claude_desktop_config.json(macOS)或 %APPDATA%\Claude\claude_desktop_config.json(Windows) |
| Codebuddy | ~/.codebuddy/mcp.json |
4.2 最小可用配置
{
"mcpServers": {
"chrome-devtools": {
"command": "npx",
"args": ["-y", "chrome-devtools-mcp@0.16.0", "--browser-url=http://127.0.0.1:9230"]
}
}
}
使用 npx -y 会自动下载并启动指定版本的 MCP 服务器,--browser-url 指向 AionUi 的 CDP 端点。版本务必锁定为 0.16.0:AionUi 内置浏览器 MCP 启动器也正是固定使用该版本拉起 chrome-devtools-mcp(见 browserServer.ts),其注释解释了原因——@latest 每次首启都要联网解析、离线直接失败,且会让不受控的上游随时替换驱动浏览器的代码,而该浏览器里持有用户的登录态。
4.3 内置启动器:静态注册 + 动态端口的解耦
在 AionUi 内部,chrome-devtools-mcp 通过 browserServer.ts 这个中间层拉起。设计动机在源码注释中写得很清楚:chrome-devtools-mcp 只接受命令行参数 --browser-url、不认环境变量,而 CDP 端口是启动时动态决定的;若把端口直接写进 MCP 注册记录的 args,每次启动都要改写注册记录,多实例共用注册表时还会写串。因此注册记录保持静态(永远指向该脚本),端口通过环境变量传入,脚本在运行时翻译成 --browser-url 再拉起真正的服务器:
- 端口来自
AIONUI_CDP_ACTIVE_PORT,且只信任本进程树继承下来的值——拿不到就退出,绝不猜测端口,避免把 Agent 连到另一个实例的浏览器上(见 browserServerPort.ts); - 若端口或口令缺失,启动器直接
process.exit(1)拒绝启动,而不是让 MCP 回退去开独立 Chrome——那会造成"Agent 以为在操作应用内浏览器、实际在操作一个用户看不见的 Chrome"的难排查故障; - Windows 平台通过
cmd.exe /c npx ...拉起(npx.cmd是批处理文件,直接 spawn 会抛EINVAL,见 browserServerPort.ts 中对 CVE-2024-27980 / issue #3883 的说明)。
4.4 手动配置时如何拿到真实端口
由于当前实现端口是动态分配的,手动配置 MCP 时请先确认当前生效的 CDP 端口:
- 在 AionUi 设置 → 系统 → 开发者调试页查看展示的当前端口(UI 通过
getCdpStatus读取cdpPort,只有在桥真正监听时才会显示有效端口,见 configureChromium.ts); - 或直接访问
http://127.0.0.1:<port>/json验证通道是否可达(见下一节)。
五、其他 AI 友好的 MCP 开发工具
除 chrome-devtools-mcp 外,AionUi 还可以与其他 MCP 服务器配合,覆盖浏览器自动化、文件与 Git 操作等场景:
| 工具 | 用途 | 配置 |
|---|---|---|
| Playwright MCP | 浏览器自动化(chrome-devtools 替代方案) | "@playwright/mcp@latest" |
| Puppeteer MCP | 浏览器自动化 | "@puppeteer/mcp@latest" |
| Filesystem MCP | 文件操作 | @modelcontextprotocol/server-filesystem |
| Git MCP | Git 仓库操作 | @modelcontextprotocol/server-git |
Playwright / Puppeteer MCP 属于"独立浏览器自动化"路线,与本文的"驱动 AionUi 应用内浏览器"路线不同:前者会由 MCP 服务器自行拉起浏览器进程,而
chrome-devtools-mcp --browser-url是连接既有 CDP 端点。选择哪条路线取决于你是希望 Agent 操作 AionUi 内的界面,还是操作一个独立浏览器实例。更多 MCP 服务器可参考 MCP 官方 servers 仓库。
六、配置完成后与 AionUi 交互
配置并重启 IDE / Claude Desktop 后,MCP 工具即可操作 AionUi 内置浏览器,常用工具包括:
| MCP 工具 | 作用 |
|---|---|
list_pages | 列出 AionUi 中所有打开的页面 |
take_snapshot | 获取当前页面的可访问性树快照(Accessibility Tree) |
click | 点击 UI 元素 |
fill | 填充表单输入框 |
hover | 悬停 UI 元素 |
navigate_page | 导航到指定 URL |
使用前请确保 AionUi 的浏览器面板已打开且至少有一个页面。这是桥的硬性前置条件:attach 只由渲染进程在 webview dom-ready 时上报触发,而 Target.createTarget 在桥内被明确拒绝(见 cdpBridge.ts)。若桥未附加任何目标,Agent 侧会收到明确的错误提示 "The in-app browser is not currently attached. Open the browser panel in AionUi so a page is available to control.",此时在 AionUi 中打开浏览器面板即可恢复。
七、使用 Chrome DevTools 手工检查
不借助 MCP,也可以直接用 Chrome 的 DevTools 手工接入 CDP 端点:
- 在 Chrome 中打开
http://127.0.0.1:9230/json(若实际端口为动态值,请以设置页展示的端口为准),页面会列出可调试目标及其 WebSocket 调试地址; - 点击页面链接,使用 DevTools 检查对应页面;
- 或使用 Chrome 的
chrome://inspect→ Configure → 添加127.0.0.1:9230,即可在 Chrome 内部直接发现并调试 AionUi 的浏览器页面。
/json(即 /json/list)与 /json/version 是桥实现的发现端点(cdpBridge.ts),其中 /json/version 响应里的 webSocketDebuggerUrl 已内嵌访问 token,客户端(含 puppeteer)取回后原样使用即可完成带口令的 WebSocket 连接。
八、故障排查与注意事项
- 端口对不上:文档示例中的 9230 是历史默认值,当前实现端口由系统动态分配。请以设置页"开发者调试"区域展示的端口为准,或核对
getCdpStatus返回的port字段。 - Agent 报 protocolTimeout 超时:除网络因素外,回包必须回显入站的
sessionId,否则页面级命令会永久挂起并最终以超时形式浮现;这是桥专门处理过的坑(cdpBridge.ts)。用户侧通常的修复是确认浏览器面板已打开、页面已就绪。 - Agent 连不上但 Chrome 起来了:说明 MCP 回退拉起了独立 Chrome 而不是连接应用内浏览器。请检查 CDP 开关已开启、应用已重启、
--browser-url指向正确的端口。 - 修改 CDP 开关不生效:
enabled状态的变更需要重启应用。 - 安全提醒:CDP 通道只绑定
127.0.0.1,但在同机多用户/多进程场景下并非绝对隔离,不要在不可信环境中长期开启远程调试。
九、相关资源
- 本文档依据:docs/guides/cdp.md
- 桥实现:packages/desktop/src/process/resources/builtinMcp/cdpBridge.ts
- 启动器实现:packages/desktop/src/process/resources/builtinMcp/browserServer.ts、browserServerPort.ts
- 配置与状态:packages/desktop/src/process/utils/configureChromium.ts
- 设置页开关:DevSettings.tsx
- webview 上报:WebviewHost.tsx
- 开发模式入口:仓库根目录 justfile 中
dev与dev-ext任务
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



