跳转到内容

Agent 浏览器集成:三种连接与多 Agent 并发

Agent 浏览器集成:三种连接与多 Agent 并发

学习目标

本章学习 Chrome DevTools MCP 如何管理浏览器生命周期、支持多种连接模式,以及如何实现多 Agent 并发操作。你将理解:

  • 三种浏览器连接模式(Launched / Connected / AutoConnect)的代码实现
  • Isolated BrowserContext 如何实现 Agent 会话隔离
  • --experimentalPageIdRouting 如何解决共享 MCP Server 的并发冲突
  • 浏览器生命周期管理的延迟初始化模式

前置知识


项目实践

三种连接模式的代码实现

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 共享同一 profileCookie/Storage 损坏使用 --isolated 或独立 userDataDir
Agent 忘记选对页面操作到错误的标签页启用 --experimentalPageIdRouting
浏览器在长时操作中崩溃后续工具全部失败每次工具调用前检查 browser.isConnected()
关闭最后一个页面浏览器变为空白close_page 在只剩一页时抛出错误

参考来源