Browser Controller
这个项目解决了什么问题
你提交了一个修复。你的智能体说"完成了,请验证。" 你切换到 Chrome,导航到页面,登录,点击各处,找到 bug。
你的智能体刚刚写了代码。它也可以验证。 你的浏览器已经为它打开着。它只是看不到。
现在它可以了。 Browser Controller 让任何兼容 MCP 的 AI 智能体(Cursor、Claude Desktop、Windsurf……)直接控制你已经打开的浏览器——你真实的会话、你的登录状态、你的 cookie。无需无头浏览器,无需全新配置文件,无需重新认证。
Related MCP server: Tabryn
核心能力
同时支持多个智能体。 Cursor 可以驱动标签页 10,而 Claude 驱动标签页 11——两者通过同一个共享守护进程,互不阻塞。
按标签页定位,而非"当前活动标签页"。 每个操作都指定一个
tabId。移动鼠标、切换标签页、看 YouTube——智能体始终在你指定的标签页上工作。它永远不会劫持你正在阅读的页面。按标签页隔离。 元素引用、控制台日志和网络缓冲区都按标签页隔离。来自标签页 10 的引用永远无法点击标签页 20 中的内容。
按标签页并发。 对同一个标签页的两个操作会串行化(无竞争);对不同标签页的操作并行执行。
标签页锁定。 智能体可以认领一个标签页,让其他智能体排队等待而不是竞争(
browser_tabs { action: "lock" })。锁在 Chrome 的 service-worker 回收后依然有效(chrome.storage.session)。智能体控制防护罩。 当智能体在某个标签页上工作时,你会看到一个半透明的蓝色内框,并且你在该标签页上的输入会被阻止(鼠标、键盘、滚轮)——徽章显示
agent <name> controlling the tab,操作完成后消失。锁定标签页会在锁的存续期间保持一个普通边框。同源 iframe 穿透。 位于 iframe 内的旧式/企业界面(例如
iframe#mainFrame中的 ONT 控制台)可以访问:所有定位工具都会搜索 iframe 文档,find/click_text会遍历每个框架。原生对话框救援。 原生的
alert/confirm/prompt会冻结页面的 JS 线程——browser_handle_dialog通过 CDP 带外解除它,无需页面 JS,这也会解除该标签页上所有其他工具的阻塞。browser_tabs close/focus始终有效,即使在冻结的标签页上也是如此。经过认证的本地连接。 令牌 + 一次性注册密钥,因此没有其他本地进程可以静默驱动你的浏览器。一切都在 localhost 上——无云端,无遥测。
无调试器横幅。
browser_evaluate通过chrome.scripting在页面的 MAIN 世界中运行——没有黄色的"此标签页正在被调试"横幅,真实值可以跨 MV3 世界边界返回。诚实的错误。 每个工具失败都会以真实的
isError结果连同完整负载到达你的智能体——不会用"成功"响应来掩盖工作流中途的失败。
工作原理
三个部分,全部在你的机器上。没有任何内容离开 localhost。
Agent (Cursor / Claude / Windsurf) ── other agents connect too ──┐
│ stdio (MCP protocol) │
▼ ▼
┌─────────────────────────────┐ ┌─────────────────────────────────────────┐
│ thin MCP client │ │ thin MCP client │
│ (node mcp-server/dist/ │ │ (node mcp-server/dist/ │
│ index.js) │ │ index.js) │
│ - speaks MCP over stdio │ │ - spawns daemon if not running │
│ - forwards calls to daemon │ │ - gets its own sessionId │
└──────────────┬──────────────┘ └────────────────────┬───────────────────┘
│ local IPC socket (AF_UNIX / named pipe, token-auth) │
▼ ▼
┌──────────────────────────────────────────────────────────────────────────┐
│ DAEMON (single long-running process, owns port 7225) │
│ - multiplexes N clients → 1 extension │
│ - tags every call with the client's sessionId │
│ - heartbeat eviction, per-session rate limiting │
└──────────────────────────────┬───────────────────────────────────────────┘
│ WebSocket ws://127.0.0.1:7225 (token-auth)
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ Chrome Extension (Manifest V3 service worker) │
│ - resolves the target tabId (never "the active tab" implicitly) │
│ - serializes same-tab actions, parallelizes cross-tab actions │
│ - executes click/type/snapshot/evaluate against the named tab │
└──────────────────────────────────────────────────────────────────────────┘关键思路: 任何智能体首次运行时,瘦客户端会启动一个后台守护进程,它拥有端口 7225 和扩展连接。之后每个智能体(即使来自不同的 MCP 客户端)都通过本地 IPC 套接字连接到同一个守护进程,并获得自己的 sessionId。扩展看到的是一个稳定的连接,并将每个调用路由到调用者指定的确切标签页。
快速开始
该项目不在 Chrome 网上应用店或 npm 上——你需要从这个仓库安装。两个部分:MCP 服务器(在你的机器上运行,与你的 AI 智能体通信)和 Chrome 扩展(位于你的浏览器中,执行命令)。
前置条件: Node.js ≥ 20 和 Chrome/Chromium/Edge。
1. 克隆并构建
git clone https://github.com/compnew2006/browser-controller.git
cd browser-controller
npm install
npm run build # compiles TypeScript → mcp-server/dist/2. 加载 Chrome 扩展
打开
chrome://extensions并启用开发者模式(右上角的开关)点击加载已解压的扩展程序,选择克隆仓库中的
extension/文件夹将 Browser Controller 图标固定到工具栏
灰点 = 等待守护进程。绿色 = 已连接。
3. 将 MCP 服务器添加到你的客户端
Cursor:设置 → MCP → "添加新的 MCP 服务器"。Claude Desktop:编辑 claude_desktop_config.json。Windsurf:设置 → MCP。任何兼容 MCP 的客户端都可以。
将 /path/to/browser-controller 替换为你克隆的绝对路径(Windows:使用 C:\\path\\to\\browser-controller\\mcp-server\\dist\\index.js):
{
"mcpServers": {
"browser-controller": {
"command": "node",
"args": ["/path/to/browser-controller/mcp-server/dist/index.js"]
}
}
}默认情况下,守护进程会根据其父级 IDE("Cursor"、"Claude"……)为每个连接命名。要覆盖此设置——例如当多个智能体共享一个 IDE 时,或按项目标记它们——在参数中传入 --agent <name>。它优先于所有自动检测:
{
"mcpServers": {
"browser-controller": {
"command": "node",
"args": ["/path/to/browser-controller/mcp-server/dist/index.js", "--agent", "My Project Agent"]
}
}
}该名称会出现在弹窗的已连接智能体列表中。(你也可以设置 MCP_AGENT_NAME 环境变量——效果相同。)使用相同名称重新连接会替换旧条目,因此 IDE 重启不会堆积重复项。
4. 将扩展与守护进程配对
守护进程使用两个密钥,均在首次运行时生成到 ~/.browser-controller/(Windows:%USERPROFILE%\.browser-controller\)。先让智能体"列出我的浏览器标签页"来启动它一次,然后:
读取密钥:
cat ~/.browser-controller/enrollment.json # one-time pairing secret cat ~/.browser-controller/token.json # WebSocket auth token(注册密钥在首次运行时也会打印到 MCP 客户端的日志中。)
点击扩展图标 → 设置标签页 → 粘贴注册密钥和认证令牌(除非你更改了
WS_PORT,否则端口保持7225)。
绿点 = 你已连接。你的智能体现在可以看到你的浏览器了。
这些密钥可防止任何其他本地进程打开 WebSocket 并驱动你经过认证的浏览器会话。要轮换它们,请停止你的 MCP 客户端,删除该文件夹,下次运行时会重新创建两个密钥。完整的威胁模型请参阅 SECURITY.md。
使用方法
模型是标签页优先的:智能体总是说明要对哪个标签页执行操作。它从不假设是"当前活动标签页"。
基本工作流
列出标签页以获取
tabId:browser_tabs { action: "list" } → [{ id: 15, url: "...", title: "...", active: true, lockedBy: null }, ...]快照该标签页以查看其结构并获取元素引用:
browser_snapshot { tabId: 15 } → { tree: [ { ref: "e3", role: "button", name: "Sign in" }, ... ] }引用仅对此 tabId 有效。如果你导航或 DOM 发生变化,请重新快照。自上次快照以来的新元素会被标记为
isNew: true——在操作打开覆盖层/下拉菜单后,智能体可以只关注这些元素,而无需重新读取整个树。交互,使用引用和相同的 tabId:
browser_click { tabId: 15, ref: "e3" } browser_type { tabId: 15, ref: "e5", text: "hello@example.com" } browser_press_key { tabId: 15, key: "Enter" }如果引用已过期但元素仍然存在,会通过稳健的选择器 + 文本/角色扫描自动找到它(响应携带
via: "fallback")。如果元素被完全滚动出视野(虚拟化信息流),响应会携带freshRefs: [...]及内联的新快照——在同一个步骤中用这些新引用之一重试,无需单独快照。验证——操作后再次快照或读取文本。
多智能体协调(两个智能体,两个标签页)
智能体 A 列出标签页,选择标签页 10,可选地锁定它:
browser_tabs { action: "lock", tabId: 10 }智能体 B 列出标签页,选择标签页 11,锁定它:
browser_tabs { action: "lock", tabId: 11 }两者并行工作。每个智能体的调用针对自己的标签页串行化;两个标签页互不干扰。
完成后:
browser_tabs { action: "unlock", tabId: 10 }。
弹窗就是你的控制面板
一个固定高度的标签式外壳(主体从不滚动,只有列表滚动):
标签页 — 每个打开的标签页及其锁所有者,工具栏中还有全部解锁,用于在智能体锁定中途崩溃时一键释放。
智能体 — 每个已连接的智能体及其名称、会话 ID、运行时间,以及一个 ✕ 可立即断开连接(清除心跳尚未回收的僵尸进程)。
设置 — WebSocket 端口、认证令牌、注册密钥。
活动栏 — 底部一个可折叠的条带,显示最新的工具活动;展开可查看滚动日志。
需要了解的事项
忘记
tabId了? 你会收到明确的错误:tabId is required. Call browser_tabs list first.受保护的页面(
chrome://、网上应用店、devtools)无法被脚本化——你会收到Cannot access protected page (chrome://...),而不是静默挂起。browser_navigate是唯一一个tabId可选的工具(默认为活动标签页)——但为了多智能体安全,请显式传入。仅哈希变化(例如/page→/page#section)在 URL 设置后立即解析,无需等待complete事件(SPA 在哈希变化时不会重新加载,因此该事件永远不会触发)。browser_evaluate在页面的 MAIN 世界中运行(无调试器横幅,CSP 安全)并返回真实值(跨世界边界进行 JSON 序列化)。它很强大但非幂等——超时后不会自动重试。滚动虚拟化信息流(Facebook/Instagram/Twitter):
browser_scroll返回refsMayBeStale: true,因为这些站点会回收 DOM 节点。下次交互前请重新快照。重复元素:当多个元素共享文本+角色时(例如 3 个"点赞"按钮),回退解析器按序号(
nth)选择正确的那个,而不仅仅是第一个匹配项。冻结的标签页(原生对话框阻塞)不会让你死锁:
browser_handle_dialog通过 CDP 解除它,browser_tabs { action: "close" }始终有效,是保证退出的方式。
🧠 教你的智能体
智能体开箱即可使用全部 22 个工具,但当它了解标签页优先的工作流时会表现更好。从仓库根目录:
npm run setup:cursor # or: node mcp-server/dist/index.js --setup cursor这会安装:
~/.cursor/rules/browser-controller.mdc— 标签页定位工作流、下拉菜单处理、何时锁定标签页~/.cursor/commands/check-browser.md— 为你的 Cursor 聊天添加/check-browser
之后,在任何聊天中输入 /check-browser。或者直接说"在我的浏览器中检查结果",智能体就知道该做什么。
npm run setup:claude在你的项目根目录添加一个 AGENTS.md。Claude Code 会自动发现它。
手动安装或自定义规则请参阅 agent-config/。
它能做什么
22 个工具。每个页面交互工具都接受一个 tabId(唯一的例外是 browser_navigate,其中它是可选的)。
查看
工具 | 功能 |
| 带元素引用的无障碍树。紧凑模式(默认)仅返回可交互元素。遍历 shadow DOM 和 iframe。 |
| 将标签页捕获为图像(先激活标签页再捕获) |
| 从页面或元素提取原始文本 |
| 通过自然语言查询元素——也会遍历同源 iframe |
交互
工具 | 功能 |
| 通过引用或 CSS 选择器点击——穿透同源 iframe |
| 按可见文本点击。可穿透 React portals 和覆盖层 |
| 在输入框和 contenteditable 字段中键入 |
| 按键组合(Enter、Escape、Ctrl+A) |
| 滚动页面和虚拟容器 |
| 触发工具提示和下拉菜单 |
| 从原生 |
| 等待元素出现或消失 |
| 一次调用填充多个表单字段(React/Vue 安全的 setter) |
| 元素到元素的拖拽(使用 CDP 保证可靠性) |
| 通过 |
browser_upload_file 将本地文件注入 <input type="file">,就像用户选择了它们一样:原生对话框永远不会打开,之后会触发 input/change 事件,因此 React/Vue 表单会做出响应。
browser_upload_file { tabId: 15, selector: "#resume", filePath: "/Users/me/resume.pdf" }
browser_upload_file { tabId: 15, ref: "e12", files: ["/tmp/a.png", "/tmp/b.png"] }路径是绝对的,并且位于运行浏览器的机器本地。省略 ref/selector 会自动定位页面上的第一个文件输入;一次上传多个文件需要带有 multiple 的输入。
导航
工具 | 功能 |
| 在标签页中转到 URL( |
| 列出 / 创建 / 关闭 / 聚焦 / 锁定 / 解锁 标签页 |
调试与高级
工具 | 功能 |
| 控制台输出(log、warn、error)——按标签页,上限 200 条 |
| 带状态码的 XHR/fetch 请求——按标签页,可选 |
| 在页面的 MAIN world 中运行 JavaScript(无横幅,CSP 安全) |
| 通过 CDP 关闭/接受打开的 alert/confirm/prompt(在冻结页面上也可用) |
| 通过 CDP 运行自包含的 JS action 对象 |
与其他方案对比
Browser Controller | Playwright MCP | Chrome DevTools MCP | |
使用你现有的浏览器 | 是 | 否,会启动新的 | 部分,需要调试端口 |
会话和 Cookie | 已经存在 | 全新配置文件 | 手动设置 |
支持企业 SSO 登录 | 是 | 否 | 视情况而定 |
多代理、多标签页 | 是 | 否 | 否 |
标签页定位(不会劫持活动标签页) | 是 | 不适用 | 否 |
经过身份验证的本地连接 | 是 | 不适用 | 否 |
设置 | 从源码构建 + 扩展 | 无头浏览器 | 带 |
配置
环境变量 | 默认值 | 功能 |
|
| 守护进程用于扩展连接的 WebSocket 端口 |
| (未设置) | 设置为 |
| (自动:IDE 名称) | 覆盖弹出窗口中显示的代理名称(与 |
守护进程状态文件
守护进程将所有内容保存在 ~/.browser-controller/(Windows:%USERPROFILE%\.browser-controller\):
文件 | 用途 |
| 扩展的一次性配对密钥(模式 |
| 扩展在每次 WebSocket 连接时必须提供的认证令牌(模式 |
| 瘦客户端连接的 IPC 套接字(mac/linux 上为 AF_UNIX;Windows 上为命名管道) |
| 守护进程元数据(pid、端口、启动时间)——用于检测正在运行的守护进程 |
| 客户端启动守护进程时的 stdout/stderr |
要完全重置:停止你的 MCP 客户端,删除该文件夹,下次运行时会用新的密钥重新创建。
可靠性
守护进程在任何客户端首次运行时自动启动,并保持分离运行。
连接断开时使用指数退避(1 秒 → 30 秒),每 10 秒进行 ping/pong 健康检查;错过 3 次 pong 的客户端将被驱逐。
每会话 120 次/分钟的速率限制保护守护进程免受失控的代理循环影响。
每个工具都有超时(大多数操作 5–15 秒,导航 60 秒),与每个工具的定义放在一起,因此不会与注册表脱节。
幂等读取工具(snapshot、screenshot、text、find)在超时时会重试;有副作用的工具(click、type、navigate、evaluate)——以及
console/network(在clear:true时会修改状态)——绝不重试,因此点击不会触发两次。如果另一个进程已经占用了端口 7225,守护进程会拒绝启动,而不是杀死它没有启动的进程——它会报告冲突,以便你有意识地解决它。
通过为每个客户端设置 WS_PORT,在不同的端口上运行两个守护进程:
{
"mcpServers": {
"browser-work": {
"command": "node",
"args": ["/path/to/browser-controller/mcp-server/dist/index.js"]
},
"browser-personal": {
"command": "node",
"args": ["/path/to/browser-controller/mcp-server/dist/index.js"],
"env": { "WS_PORT": "9333" }
}
}
}更新每个扩展弹出窗口中的端口以匹配。
架构
一切都在你的机器上。扩展通过 localhost 上的经过身份验证的 WebSocket 连接到守护进程;MCP 客户端通过本地 IPC 套接字连接到守护进程。没有云、没有代理,没有任何东西离开你的浏览器。
browser-controller/
├── mcp-server/ MCP server (TypeScript)
│ └── src/
│ ├── daemon.ts Single multi-client daemon (owns WS :7225)
│ ├── daemon-config.ts IPC protocol, paths, auth/enrollment tokens
│ ├── index.ts Thin stdio MCP client (spawns daemon, multiplexes)
│ ├── bridge.ts Extension WS server + cross-platform port probe
│ ├── register-tools.ts Progressive-disclosure wiring
│ └── tools/ One file per tool (22), registry pattern
├── extension/ Chrome extension (Manifest V3, plain JS, ES modules)
│ ├── background.js Wiring only (~30 lines): inject router, register events, connect
│ ├── lib/ state (buffers/locks/persistence), connection (WS lifecycle),
│ │ router (dispatch + mutex/locks + control shield), page-exec,
│ │ overlay, lock-ops, tab-concurrency (pure, unit-tested)
│ ├── handlers/ Tool implementations: navigation, interaction, inspection, tabs, cdp
│ ├── utils/ navigation + smart-selector fallback resolution
│ ├── events.js chrome.* listeners (console capture, popup, webRequest, lifecycle)
│ ├── content.js Console capture
│ └── popup/ Fixed tabbed shell (Tabs · Agents · Settings) + collapsible activity bar
├── agent-config/ Pre-built configs for Cursor + Claude Code
│ ├── cursor/ Rules and commands
│ ├── skills/ Browser automation skill
│ └── setup.mjs One-command installer
└── tests/ 15 suites / 215 tests技术栈: TypeScript(严格模式)· MCP SDK · WebSocket · Chrome 扩展 Manifest V3 · Vitest
开发
git clone https://github.com/compnew2006/browser-controller.git
cd browser-controller
npm install
npm run build
npm test命令 | 功能 |
| 将 TypeScript 编译到 |
| 监视模式 |
| 运行完整测试套件(215 个测试) |
| 仅进行类型检查,不输出 |
| 安装 Cursor 规则 + 命令 |
| 安装 Claude Code |
该套件涵盖 WebSocket 桥接(包括令牌认证拒绝和统一错误通道)、工具注册表、守护进程生命周期(心跳驱逐、速率限制、IPC 认证)、每标签页并发(同标签页串行化 + 跨标签页并行),以及通过模拟的 chrome API 测试扩展行为(路由分发、shield 语义、evaluate 往返、iframe 穿透、对话框救援)。CI 在 Node 20 和 22 上运行该套件,外加 CodeQL 和 Scorecard 扫描。
更新现有安装
git pull
npm install
npm run build然后有两个手动步骤:在 chrome://extensions 中重新加载扩展(运行中的 service worker 不会自行拾取文件更改),以及重启守护进程——它是长期运行的,也不会重新加载 dist/(杀掉它,或者直接重启你的 MCP 客户端,下次运行会在新构建上重新启动它)。
常见问题
这正是它的意义所在。扩展运行在你实际的 Chrome 中——相同的 Cookie、相同的会话、相同的本地存储。无需重新认证。
不会。MCP 客户端、守护进程和扩展都通过 localhost(IPC 套接字 + WebSocket)通信。没有任何东西离开你的机器。没有分析、没有遥测、没有云组件。有关威胁模型、认证设计和首次接触 TOFU 窗口,请参阅 SECURITY.md。
任何兼容 MCP 的客户端。Cursor、Claude Desktop、Claude Code、Windsurf、Cline,以及任何其他支持 MCP 协议的客户端。多个客户端可以同时连接同一个守护进程运行。
可以。每个代理连接到共享守护进程,获取自己的 sessionId,并针对特定的 tabId。同一标签页上的操作通过每标签页互斥锁串行化;不同标签页上的操作并行运行。代理可以选择 lock 一个标签页以声明独占访问权;其他代理会在锁后面排队,而不是失败。
它不会——不会静默发生。每个页面交互工具都需要 tabId,如果缺少,你会收到明确的 tabId is required 错误。代理永远不会意外操作你正在查看的标签页。(唯一的例外是不带 tabId 的 browser_navigate,它使用活动标签页——但在多代理场景下,你应该始终传递 tabId。)
没有它们,你机器上的任何本地进程都可以打开到端口 7225 的 WebSocket,并驱动你已认证的浏览器会话(你的银行、你的邮箱、你公司的 SSO)。enrollment 密钥将扩展与守护进程恰好配对一次(带外,在任何 WebSocket 存在之前);然后 auth token 对每个连接进行认证。两者都存放在 ~/.browser-controller/ 中,权限为 0600。
它们会从零启动一个新的浏览器实例——没有状态、没有 cookie、没有会话。你每次都必须重放完整的登录流程。而这个是连接到你已经打开的浏览器,所有内容都已加载完毕。
贡献
欢迎在问题跟踪器提交错误报告、功能请求和 PR。较大的更改请先打开一个 issue。
安全
参见 SECURITY.md——仅限 localhost 的架构、token + enrollment 设计、威胁模型和报告指南。
许可证
This server cannot be deployed
Maintenance
Related MCP Connectors
Let your AI agent operate any website through a real, logged-in browser session.
Control a real browser from AI agents: navigate, click, type, screenshot, read the page.
Browser MCP for logged-in tasks. Uses your Chrome — credentials stay local. Zero-token replay.
Web scraping, browser automation, screenshots and AI data extraction for AI agents via MCP.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceEnables CLI coding agents to interact with your live browser tabs via MCP, using your real sessions and cookies without a sandbox.MIT
- AlicenseNot gradedqualityCmaintenanceConnects AI agents to your Chrome browser via MCP, enabling real-time control of existing tabs, sessions, and application state for development workflows.MIT
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to control your existing Chrome browser via MCP, using your logged-in sessions for automation on authenticated sites. Provides high-level browser tools plus raw CDP and Chrome API access.MIT
- AlicenseNot gradedqualityAmaintenanceEnables any MCP-compatible AI agent to drive your own Chrome browser with your existing login state, filling forms, clicking elements, fetching data, and handling captchas without API keys or re-authentication.402 npm545MIT