Agent 浏览器集成:三种连接与多 Agent 并发
Agent 浏览器集成:三种连接与多 Agent 并发
学习目标
本章学习 Chrome DevTools MCP 如何管理浏览器生命周期、支持多种连接模式,以及如何实现多 Agent 并发操作。你将理解:
- 三种浏览器连接模式(Launched / Connected / AutoConnect)的代码实现
Isolated BrowserContext如何实现 Agent 会话隔离--experimentalPageIdRouting如何解决共享 MCP Server 的并发冲突- 浏览器生命周期管理的延迟初始化模式
前置知识
- Agent 浏览器集成模式 — 三种连接模式与并发隔离策略
项目实践
三种连接模式的代码实现
index.ts 中的 getContext() 函数是浏览器连接/启动的入口:
getContext() 被首次调用时: ├─ 如果有 browserUrl 或 wsEndpoint 或 autoConnect │ → ensureBrowserConnected() │ ├─ browserURL: 通过 CDP HTTP 端点连接 │ ├─ wsEndpoint: 直接 WebSocket 连接(支持自定义 headers) │ └─ autoConnect: Chrome 144+ 自动发现(仅传 channel) │ └─ 否则 → ensureBrowserLaunched() ├─ headless: 无头模式 ├─ executablePath: 自定义 Chrome 路径 ├─ isolated: 临时 profile(自动清理) ├─ userDataDir: 自定义 profile 路径 └─ enableExtensions: 启用扩展支持(pipe 连接)设计决策:连接模式在首次工具调用时才执行,而非 MCP Server 启动时。这意味着:
- 如果 Agent 只使用非浏览器工具(理论上),浏览器不会被启动
- MCP Server 的启动成本最低化
Isolated Context 与多 Agent 并发
new_page 工具支持 isolatedContext 参数:
new_page({ isolatedContext: "my-session" }) → 如果 "my-session" 的 BrowserContext 不存在 → browser.createBrowserContext() → 注册到 McpContext.#isolatedContexts → 在该 context 下创建新页面效果:
- 每个 isolated context 有独立的 cookies 和 storage
- Agent A 在
isolated-context-1中的登录态不影响 Agent B 在isolated-context-2中的操作 - context 名称可由 Agent 自由选择(如
"codex-session","claude-session")
自动发现:项目还会自动发现未被注册的 BrowserContext(如用户手动打开的无痕窗口),并分配 isolated-context-N 名称。
Page ID Routing:解决共享 Server 冲突
当多个 Agent 共享同一个 MCP Server 实例时,selectedPage 的切换会造成操作错乱。
--experimentalPageIdRouting 启用后: ├─ 页面级工具的输入 schema 自动添加 pageId 参数 │ (在 ToolHandler 构造时:pageScoped && experimentalPageIdRouting → 注入 pageIdSchema) │ └─ 工具执行时: ├─ 如果 params.pageId 存在 → context.getPageById(pageId) └─ 否则 → context.getSelectedMcpPage()(回退行为)关键实现:ToolHandler 在构造时检查 experimentalPageIdRouting,如果启用,自动将 pageIdSchema 合并到页面工具的输入 schema 中:
const inputSchema = tool.pageScoped && serverArgs.experimentalPageIdRouting ? { ...pageIdSchema, ...tool.schema } // 注入 pageId : tool.schema;浏览器生命周期管理
McpContext 复用:getContext() 检查当前 browser 实例是否与上次相同:
if (context?.browser !== browser) { context = await McpContext.from(browser, logger, opts);}return context;这意味着跨多个工具调用,同一个浏览器实例的 McpContext 被复用——保留了页面状态、网络请求历史、控制台消息等。
Focused Page Emulation
在 createPagesSnapshot() 中,每个新页面自动调用:
page.emulateFocusedPage(true)这使得每个页面的 document.hasFocus() 返回 true,Agent 操作不会因为”页面未聚焦”而出现行为异常(如某些 CSS :focus-visible 样式不会触发)。
设计取舍
| 取舍 | 选择 | 原因 |
|---|---|---|
| 启动时 vs 延迟启动浏览器 | 延迟 | 减少不必要的资源消耗 |
| 每次新浏览器 vs 复用 | 复用 | 保持浏览状态连续性 |
| 默认 profile vs 临时 profile | 默认 + isolated 可选 | 保留用户数据,同时提供隔离选项 |
陷阱与对策
| 陷阱 | 后果 | 对策 |
|---|---|---|
| 多 MCP Server 共享同一 profile | Cookie/Storage 损坏 | 使用 --isolated 或独立 userDataDir |
| Agent 忘记选对页面 | 操作到错误的标签页 | 启用 --experimentalPageIdRouting |
| 浏览器在长时操作中崩溃 | 后续工具全部失败 | 每次工具调用前检查 browser.isConnected() |
| 关闭最后一个页面 | 浏览器变为空白 | close_page 在只剩一页时抛出错误 |