Skip to content

Repository files navigation

reasonix-cdp-relay 🚀

reasonix-cdp-relay 是一个面向 Reasonix / MCP Agent 的浏览器控制中继层:
它把 Chrome DevTools Protocol(CDP)封装成标准 MCP 工具,让 Agent 可以稳定地完成导航、截图、快照、点击、输入、等待等操作。

核心目标:

  • 🧩 零侵入接入:作为外置 MCP Server 运行,不需要改 Reasonix 源码
  • 🌐 支持真实浏览器控制:支持独立 Chrome、附着已有 Chrome、浏览器插件三种模式
  • 🔒 更稳的会话与标签页语义:Extension 模式下,sessionIdtabId 绑定明确
  • 🧪 测试覆盖较完整:包含 contract / driver / extension / concurrency / session 测试

特性一览 ✨

  • stdio MCP Server,适合本地 Agent / Reasonix 接入
  • 3 种驱动模式:
    • launch:启动隔离的 headless Chrome
    • attach:附着到已有 Chrome 的 CDP 端口
    • extension:通过 Chrome 扩展控制你正在使用的浏览器
  • 14 个浏览器工具,覆盖:
    • 会话管理
    • 导航
    • DOM 快照
    • 截图
    • 鼠标键盘交互
    • JavaScript 执行
    • 原始 CDP 逃生舱
  • Extension 模式支持:
    • tab 复用 / 收编现有页面
    • 同一 sessionId 强绑定同一 tab
    • 同 tab CDP 命令串行执行,减少竞态
  • 内置排障能力:
    • 结构化错误返回
    • 可选日志看板(本地 log server)

架构概览 🏗️

当前实现是三层:

MCP Server (stdio)
  -> dispatcher
  -> actions
  -> driver (launch / attach / extension)
  -> Chrome DevTools Protocol

extension 模式下,链路会进一步变成:

MCP -> ws server -> Chrome extension service worker -> chrome.debugger -> Chrome

关键设计点:

  • dispatcher 只负责路由,不做复杂决策
  • ExtensionDriver 是唯一的 sessionId <-> tabId 真相源
  • 同一 tab 的 CDP 命令按队列串行执行
  • wait 事件(如 domcontentloaded)按 tab 隔离,不会被错误 tab 的事件误触发

安装与构建 📦

npm install
npm run build

开发模式:

npm run dev

运行测试:

npm test

本地排障日志面板:

npm run log:dev

然后打开:

http://127.0.0.1:19999

启动方式 ▶️

1. 默认启动(launch 模式)

reasonix-cdp-relay

等价于:

reasonix-cdp-relay --driver launch

2. 附着已有 Chrome(attach 模式)

reasonix-cdp-relay --driver attach --port 9222

attach 模式要求 Chrome 已开启远程调试端口。
如果浏览器已打开但没带 CDP 端口,默认不会强制重启用户浏览器。

如需允许 relay 重启 Chrome 以补上 CDP 端口,可显式设置:

REASONIX_ATTACH_RESTART_CHROME=1 reasonix-cdp-relay --driver attach --port 9222

3. 浏览器插件模式(extension,推荐接入真实日常 Chrome) 🧠

reasonix-cdp-relay --driver extension --port 19223

特点:

  • 不需要重启你的日常 Chrome
  • 更适合接入已经登录的站点
  • 更适合真实用户浏览器自动化

Chrome 扩展安装(仅 extension 模式) 🧩

扩展源码在:

extension/

安装步骤(Chrome):

  1. 打开 chrome://extensions
  2. 开启“开发者模式”
  3. 选择“加载已解压的扩展程序”
  4. 选中本仓库的 extension/ 目录
  5. 启动 relay:
reasonix-cdp-relay --driver extension --port 19223

扩展连接成功后,会主动连到:

ws://127.0.0.1:19223

Reasonix / MCP 接入 🔌

推荐:直接使用已安装 bin

~/.reasonix/config.json

{
  "mcpServers": {
    "cdp-relay": {
      "type": "stdio",
      "command": "reasonix-cdp-relay",
      "args": ["--driver", "extension", "--port", "19223"],
      "env": {
        "RELAY_LOG_LEVEL": "info"
      }
    }
  }
}

本地开发调试:直接指向 dist/index.js

{
  "mcpServers": {
    "cdp-relay": {
      "type": "stdio",
      "command": "node",
      "args": [
        "/absolute/path/to/reasonix-relay/dist/index.js",
        "--driver",
        "extension",
        "--port",
        "19223"
      ]
    }
  }
}

三种驱动模式怎么选? 🤔

模式 适合场景 优点 注意事项
launch 测试、CI、纯自动化 隔离、稳定 登录态与日常浏览器隔离
attach 已有 Chrome 已开 CDP 端口 共享真实浏览器状态 依赖 CDP 端口
extension 已登录站点、真实浏览器任务 不重启浏览器、接近真实使用 需要安装扩展

如果你要操作:

  • 已登录的站点(如后台、工作台、控制台)👉 优先 extension
  • 自动化回归测试 👉 优先 launch
  • 已有自动化 Chrome 环境 👉 可用 attach

MCP 工具列表 🛠️

当前共 14 个工具

会话管理

Tool 说明
browser_session_open 打开浏览器会话
browser_session_close 关闭浏览器会话
browser_session_list 列出当前会话

导航与页面状态

Tool 说明
browser_navigate 跳转到 URL
browser_snapshot 抓取 DOM 快照,返回 rid
browser_screenshot 返回 base64 截图
browser_screenshot_save 直接把截图保存到本地文件

交互

Tool 说明
browser_click 点击元素
browser_type 输入文本
browser_keypress 发送按键
browser_scroll 页面或元素滚动
browser_waitFor 等待 selector 出现

高级

Tool 说明
browser_evaluate 执行 JS
browser_cdp 原始 CDP 指令逃生舱

会话与标签页语义 📘

launch / attach

  • 一个 driver 实例本质上对应一个浏览器上下文
  • launch/attach 当前采用 单 session 语义
  • 重复 browser_session_open 会复用已有会话,而不是开多个独立 tab

extension

这是最重要的一部分:

  • sessionIdtabId 绑定
  • 如果你已经拿到了一个 sessionId,后续 browser_navigate(sessionId, url) 会在这个 tab 上导航
  • 不会再偷偷新开第二个 tab(除非你没有传 sessionId

推荐调用顺序

browser_session_open
-> browser_navigate
-> browser_snapshot / browser_screenshot / browser_click / browser_type ...

一个重要细节

如果你在 extension 模式下 不传 sessionId 直接 navigate,系统会:

  • 优先匹配现有 URL
  • 再决定复用 / 收编 / 新开 tab

如果你传了 sessionId,则会优先复用这个会话绑定的 tab。


browser_navigate 的当前语义 🌍

默认等待策略

如果你不传 waitUntil

{ "waitUntil": "domcontentloaded" }

也就是说,默认等价于:

  • DOM 建立完成
  • 但不保证所有资源都加载完

可选值

含义
domcontentloaded DOM 建好即可继续(默认)
load 等待 Page.loadEventFired / readyState=complete
networkidle 等待网络安静下来,更适合截图或 SPA 稳定后操作

成功返回前的硬校验

当前 navigate 在返回 status=ok 前,会额外确认页面“可用”,至少包括:

  • URL 不是 about:blank
  • readyState 与当前 waitUntil 语义相符
  • 页面不是空壳
    • bodyChildCount > 0,或
    • outerHTML.length > 80,或
    • title 非空
  • URL 连续读取两次一致

这能避免“只收到一个事件就误报成功”。


关键返回结构 📤

1. browser_navigate

返回 content 数组,通常至少两段文本:

  1. 人类可读文本
  2. 页面状态 JSON
  3. (extension 模式)会再追加 session 元信息

示例:

[
  { "type": "text", "text": "Navigated to https://www.baidu.com" },
  {
    "type": "text",
    "text": "{\"status\":\"ok\",\"requestedUrl\":\"https://www.baidu.com\",\"finalUrl\":\"https://www.baidu.com/\",\"readyState\":\"interactive\",\"title\":\"百度一下,你就知道\",\"bodyChildCount\":12,\"htmlLength\":53821,\"tabId\":123}"
  },
  {
    "type": "text",
    "text": "{\"sessionId\":\"sess_xxx\",\"reused\":true,\"driver\":\"extension\"}"
  }
]

⚠️ Agent 端不要只看第一段文本,要解析后面的 JSON。

2. browser_snapshot

返回 JSON:

{
  "generation": 2,
  "tabId": 123,
  "tree": {
    "rid": "r1",
    "tag": "html",
    "children": []
  }
}

说明:

  • generation:快照代际;新快照会让旧 rid 失效
  • tabId:extension 模式下用于确认落点
  • tree:可用于后续 browser_click / browser_type

3. browser_screenshot

返回:

  • 第 1 段:图片
  • 第 2 段:结构化页面状态 JSON

4. browser_screenshot_save

返回:

  • 第 1 段:保存路径文本
  • 第 2 段:结构化页面状态 JSON

错误处理 ❗

所有工具失败时,都会返回:

  • MCP 层:isError: true
  • 内容:结构化 JSON

格式:

{ "error": "E_...", "message": "...", "details": {} }

示例错误码:

  • E_DRIVER_UNAVAILABLE
  • E_CHROME_NOT_FOUND
  • E_TAB_BUSY
  • E_NAV_TIMEOUT
  • E_SELECTOR_NOT_FOUND
  • E_CDP_RAW_FAILED
  • E_SESSION_EXPIRED

✅ Agent 端必须优先检查 isError,不要只解析 content[0].text


一些接入建议 💡

1. 操作真实登录站点时

优先用:

--driver extension

2. 如果你准备截图 / 读动态内容

推荐 navigate 时显式传:

{ "waitUntil": "networkidle" }

这样通常比默认的 domcontentloaded 更适合截图与信息抓取。

3. snapshot 后尽快使用 rid

因为新快照会让旧 rid 失效。

4. 如果你已经有一个 session

后续所有操作都继续传同一个 sessionId,这样才能稳定复用同一个 tab。


本地排障与日志 🔍

项目内置了一个轻量日志服务,方便排查:

  • ws 服务有没有把命令发到正确 tab
  • 插件有没有成功 attach
  • navigate / screenshot / snapshot 是否落在同一 tab

启动日志服务

npm run log:dev

打开页面:

http://127.0.0.1:19999

日志会显示:

  • ws_start
  • ext_connected
  • cdp_send
  • cdp_result
  • cdp_event
  • sw_cdp_recv
  • sw_attach_ok
  • sw_cdp_ok
  • sw_tab_open

非常适合排查“为什么明明 navigate 成功,但 screenshot 还是空白页”这类问题。


开发与测试 🧪

常用命令:

npm run dev
npm run build
npm test
npm run log:dev

测试覆盖包括:

  • contract
  • driver
  • extension
  • concurrency
  • session
  • tool-result

当前测试会序列化执行,以避免多个 Chrome 实例并发导致不稳定。


目录结构 📁

src/
  actions/         # navigate / snapshot / screenshot / click / type ...
  common/          # logger / errors / cdp queue / debug helpers
  drivers/         # launch / attach / extension
  mcp-server/      # MCP server / dispatcher / schemas
  session/         # session state / rid mapping / queue routing

extension/         # Chrome MV3 扩展
scripts/           # 调试脚本 / 日志服务
test/              # 自动化测试
dist/              # 编译产物

当前版本说明 📝

当前版本:0.1.0

这是一个偏工程实用型的 MCP relay,重点在于:

  • 接入真实浏览器
  • 稳定管理 session / tab
  • 给 Agent 提供结构化页面状态
  • 能快速定位“工具成功但页面不对”的链路问题

如果你准备把它接入自己的 Agent,建议优先从 extension 模式开始。👍

About

MCP Server + CDP Driver for browser automation via Chrome DevTools Protocol

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages