本文面向需要通过 AI Agent、MCP Inspector 或自研 MCP 客户端操作 BroSDK
浏览器环境的开发者。目标是从一个已经启动的 BroSDK 环境出发,完成连接、
页面观察、点击输入、结果确认、截图和会话清理。
本文使用当前公开的单环境 MCP endpoint:
```text
http://127.0.0.1:{sdkPort}/sdk/v1/mcp/env/{envId}
```
## 1. BroSDK MCP 是什么
BroSDK MCP 把一个正在运行的指纹浏览器环境暴露为 MCP Streamable HTTP
服务。每个 endpoint 只操作路径中指定的 `envId`,不会跨环境选择页面。
主要能力分成两层:
1. 页面自动化层:通过 BroSDK 已有 CDP 连接完成页面发现、AX snapshot、
原生鼠标键盘输入、截图、PDF、上传和下载。
2. 浏览器 UI/Profile 层:通过可选的 BroSDK MCP Bridge 扩展完成窗口、标签
排序、标签组、书签、历史和统一浏览器状态管理。
扩展不是基础页面操作的前置条件。未安装扩展时,Agent 仍可以导航、观察、
点击、输入、滚动、拖动、截图和打印 PDF。
## 2. 使用前准备
连接 MCP 前应满足以下条件:
- BroSDK 已完成初始化,并启动本地 HTTP 服务。
- 目标 `envId` 的浏览器已经启动且 CDP 已就绪。
- MCP 客户端运行在用户本机,并能访问 `127.0.0.1:{sdkPort}`。
- 如需窗口、书签、历史或标签组能力,启动环境时已经加载 Bridge 扩展。
本文示例使用:
```powershell
$SdkPort = 65535
$EnvId = "2063816419455275008"
$McpUrl = "http://127.0.0.1:$SdkPort/sdk/v1/mcp/env/$EnvId"
```
请将端口和环境 ID 替换为当前 BroSDK 实例的实际值。
### 2.1 健康检查
先确认 endpoint 存在:
```powershell
Invoke-RestMethod "$McpUrl/health"
```
正常结果类似:
```json
{
"ok": true,
"server": "brosdk-mcp",
"envId": "2063816419455275008",
"transport": "streamable-http",
"sessionTtlMs": 43200000,
"toolCount": 18
}
```
`toolCount` 可能因构建开关而变化,应以当前响应和 `tools/list` 为准。
## 3. 使用 MCP Inspector 或 Agent 客户端连接
在支持 Streamable HTTP 的 MCP 客户端中新增 server:
```text
Transport: Streamable HTTP
URL: http://127.0.0.1:{sdkPort}/sdk/v1/mcp/env/{envId}
```
不同 MCP 客户端的配置文件格式并不统一。等价的概念配置如下:
```json
{
"mcpServers": {
"brosdk-current-env": {
"transport": "streamable-http",
"url": "http://127.0.0.1:65535/sdk/v1/mcp/env/2063816419455275008"
}
}
}
```
连接成功后应先检查:
1. `tools/list` 能列出 `tabs`、`snapshot`、`act` 等公开工具。
2. 调用 `tabs` 的 `list` action 能返回至少一个 `page`。
3. 调用 `snapshot` 能返回 AX tree,而不是 `BROWSER_NOT_READY`。
不要把下面的地址配置给 MCP 客户端:
```text
http://mcp.brosdk.internal
```
它是 Bridge 扩展在浏览器内部访问的专用主机,不是 Agent 的 MCP endpoint。
普通页面或错误的扩展上下文直接访问它可能得到 `403 Forbidden`。
## 4. 直接调用 Streamable HTTP
一般 MCP 客户端会自动管理 session。自研客户端需要严格执行初始化握手。
### 4.1 Initialize
```powershell
$BaseHeaders = @{
Accept = "application/json, text/event-stream"
"Content-Type" = "application/json"
}
$InitializeBody = @{
jsonrpc = "2.0"
id = 1
method = "initialize"
params = @{
protocolVersion = "2025-06-18"
capabilities = @{}
clientInfo = @{
name = "brosdk-tutorial"
version = "1.0.0"
}
}
} | ConvertTo-Json -Depth 10 -Compress
$Initialize = Invoke-WebRequest `
-Method Post `
-Uri $McpUrl `
-Headers $BaseHeaders `
-Body $InitializeBody
$SessionId = [string]$Initialize.Headers["Mcp-Session-Id"]
$ProtocolVersion = [string]$Initialize.Headers["Mcp-Protocol-Version"]
```
后续请求必须同时携带服务端返回的两个 header:
```powershell
$SessionHeaders = @{
Accept = "application/json, text/event-stream"
"Content-Type" = "application/json"
"Mcp-Session-Id" = $SessionId
"Mcp-Protocol-Version" = $ProtocolVersion
}
```
不要在新的 `initialize` 请求中复用旧 `Mcp-Session-Id`。
### 4.2 发送 initialized notification
```powershell
$InitializedBody = @{
jsonrpc = "2.0"
method = "notifications/initialized"
params = @{}
} | ConvertTo-Json -Depth 5 -Compress
Invoke-WebRequest `
-Method Post `
-Uri $McpUrl `
-Headers $SessionHeaders `
-Body $InitializedBody
```
该 notification 成功时返回 HTTP `202`,响应体为空。
### 4.3 封装 JSON-RPC 调用
```powershell
function Invoke-BroSdkMcp {
param(
[int]$Id,
[string]$Method,
[hashtable]$Params = @{}
)
$Body = @{
jsonrpc = "2.0"
id = $Id
method = $Method
params = $Params
} | ConvertTo-Json -Depth 30 -Compress
Invoke-RestMethod `
-Method Post `
-Uri $McpUrl `
-Headers $SessionHeaders `
-Body $Body
}
$Tools = Invoke-BroSdkMcp 2 "tools/list"
$Tools.result.tools | Select-Object name, description
```
调用 tool 时,tool 名放在 `params.name`,输入放在 `params.arguments`:
```powershell
$Tabs = Invoke-BroSdkMcp 3 "tools/call" @{
name = "tabs"
arguments = @{ action = "list" }
}
$Tabs.result.structuredContent.pages
```
## 5. 第一个完整自动化任务
下面以“打开页面、查找按钮、点击并验证结果”为例。
### 5.1 获取 page
```json
{
"name": "tabs",
"arguments": {
"action": "list"
}
}
```
返回的 `page` 是 BroSDK 公共页面句柄,例如:
```json
{
"action": "list",
"pages": [
{
"page": 7,
"url": "https://example.test/",
"title": "Example",
"active": true,
"stateSource": "cdp",
"stateFresh": false
}
],
"count": 1
}
```
后续只使用 `page: 7`,不要向 Agent 暴露或保存 CDP `targetId`、Chrome
`tabId` 或内部 CDP session ID。
### 5.2 获取 snapshot
```json
{
"name": "snapshot",
"arguments": {
"page": 7
}
}
```
`snapshot` 默认使用 CDP Accessibility Tree,并给可操作节点分配稳定 `ref`:
```text
[ref=e12] button "Submit"
[ref=e13] textbox "Email"
```
只有 AX tree 无法提供目标元素时,才按需启用只读 DOM fallback:
```json
{
"name": "snapshot",
"arguments": {
"page": 7,
"domFallback": true
}
}
```
### 5.3 输入和点击
填写输入框:
```json
{
"name": "act",
"arguments": {
"page": 7,
"kind": "fill",
"ref": "e13",
"value": "user@example.com"
}
}
```
点击按钮:
```json
{
"name": "act",
"arguments": {
"page": 7,
"kind": "click",
"ref": "e12"
}
}
```
鼠标和键盘动作使用原生 CDP 输入。`inputMode: "cdp"` 只能证明输入已分发,
不能单独证明业务操作成功。
### 5.4 验证结果
优先检查 `act` 返回的 `autoDiff`。如果页面异步更新,再等待明确条件:
```json
{
"name": "wait",
"arguments": {
"page": 7,
"for": "text",
"value": "Saved",
"timeout": 10000
}
}
```
也可以用 `grep` 做低成本检查:
```json
{
"name": "grep",
"arguments": {
"page": 7,
"pattern": "Saved",
"over": "content",
"limit": 20
}
}
```
页面导航、刷新或 document 被替换后,应丢弃旧 ref 并重新 `snapshot`。
## 6. 推荐的 Agent 工作流
稳定的页面任务应遵循“观察、动作、验证”循环:
```text
tabs(list/current)
-> snapshot(page)
-> act(page, ref/kind)
-> 检查 autoDiff
-> 必要时 wait/grep/diff
-> 页面大改或导航后重新 snapshot
```
选择观察工具时从轻到重:
| 目的 | 首选 tool |
|------|-----------|
| 查找少量文字或控件 | `grep` |
| 获取可操作控件和 ref | `snapshot` |
| 提取正文 | `read` |
| 理解 Canvas、图表、视觉布局 | `screenshot` |
| 归档打印页面 | `pdf` |
| 读取 AX/正文都没有的小段页面状态 | `evaluate` |
不要在每个动作后固定等待若干秒。优先使用 `autoDiff`、文本、selector、URL、
下载完成或新 page 等可验证条件。
## 7. 常用 tools
当前默认公开工具可按用途分组:
| 类别 | Tools | 用途 |
|------|-------|------|
| 浏览器空间 | `browser_state`, `tabs`, `tab_groups`, `windows` | 页面、窗口和标签组管理 |
| Profile 数据 | `bookmarks`, `history` | 书签和历史记录 |
| 页面观察 | `snapshot`, `diff`, `read`, `grep`, `screenshot`, `pdf` | 结构、文本和视觉状态 |
| 页面动作 | `navigate`, `act`, `wait`, `evaluate` | 导航、输入和条件等待 |
| 文件 | `upload`, `download` | 文件输入和浏览器下载 |
`dialogs` 当前默认构建未公开。诊断、legacy tools 和 `cdp.send` 也不属于默认
Agent tool surface,除非宿主显式启用对应环境开关。
### 7.1 `act` 常用 kind
| kind | 主要参数 | 用途 |
|------|----------|------|
| `click` / `double_click` | `ref` | 单击、双击语义元素 |
| `click_at` | `x`, `y` | 点击截图推导出的 viewport 坐标 |
| `fill` | `ref`, `value` 或 `fields` | 清空并填写一个或多个字段 |
| `type` / `type_at` | `text`,可选 `clear` | 向焦点或坐标输入文本 |
| `press` | `key` | Enter、Tab、Escape 或组合键 |
| `hover` / `hover_at` | `ref` 或 `x`, `y` | 悬停菜单和提示区域 |
| `check` / `uncheck` | `ref` | 设置 checkbox/switch 状态 |
| `select` | `ref`, `value` | 选择原生 select option |
| `scroll` | 可选 `ref`, `direction`, `amount` | 滚动页面或命中的容器 |
| `drag` | `ref`, `targetRef` | 从语义源元素拖到目标元素 |
| `drag_at` | 起止坐标,可选 `waypoints` | 滑块、轨迹和视觉拖动 |
复杂拖动应先通过 snapshot 或 screenshot 确定目标。`drag_at` 的坐标属于当前
viewport CSS 坐标,页面滚动或缩放后必须重新观察。
## 8. Bridge 扩展:何时需要
### 8.1 无扩展可以完成
- `tabs`: `list`、`current`、`new`、`close`。
- 所有页面级导航、snapshot、文本读取和搜索。
- `act` 的点击、输入、滚动、双击和拖动。
- screenshot、PDF、upload、download、wait 和 evaluate。
### 8.2 当前需要扩展
- `browser_state`。
- `tabs`: `focus`、`move`、`pin`、`unpin`。
- `windows` 和 `tab_groups`。
- `bookmarks`。
- `history` 的 `recent`、`search`、`delete_url`、`delete_range`。
`history action="open"` 直接使用传入 URL 创建页面,不依赖历史查询能力。
扩展不可用时,依赖扩展的 tool 会返回 `BRIDGE_NOT_READY`。这不是 MCP session
失效;Agent 可继续使用页面级 CDP tools。
## 8. Page、ref 与 session
这三个身份不要混用:
| 身份 | 作用域 | 失效条件 |
|------|--------|----------|
| `envId` | BroSDK 浏览器环境 | 环境被删除 |
| `page` | 同一 env 的页面 | 页面关闭 |
| `ref` | 同一 env/page/document 的元素 | 导航、document 替换或元素结构失效 |
| `Mcp-Session-Id` | 一次 MCP 客户端连接 | DELETE、TTL 过期或服务重启 |
同一页面在不同 MCP session 中使用相同公共 `page`。同一 document 的稳定 AX
节点也应产生相同 `ref`。底层 CDP attach session、`diff` 基线和 artifact 列表
仍按 MCP session 隔离,避免多个 Agent 相互推进观察状态。
MCP session 默认 TTL 为 12 小时。客户端断线重连通常应重新 initialize,不要
假定旧 session 永远可复用。
任务结束后主动释放 session:
```powershell
Invoke-WebRequest `
-Method Delete `
-Uri $McpUrl `
-Headers $SessionHeaders
```
## 9. Screenshot 与坐标动作
截图调用示例:
```json
{
"name": "screenshot",
"arguments": {
"page": 7,
"format": "png",
"fullPage": false,
"size": {
"width": 1280,
"height": 900
}
}
}
```
返回结果除了 artifact,还包含 viewport、pageSize、scroll、remaining 和图片到
CSS viewport 的坐标变换:
```text
viewportX = imageX * coordinateScaleX + coordinateOffsetX
viewportY = imageY * coordinateScaleY + coordinateOffsetY
```
全页截图中的目标可能不在当前 viewport。不能直接把全页图片坐标传给
`click_at`;应先滚动到目标区域,再获取当前 viewport 截图或 snapshot。
## 10. Artifact、PDF 与下载文件
`screenshot` 和 `pdf` 返回 MCP artifact 元数据,而不是承诺一个永久磁盘路径。
artifact:
- 只属于创建它的 MCP session。
- 默认 30 分钟过期。
- 可以通过 `resources/read` 读取。
- 也可以使用带 session header 的 HTTP 分块接口读取。
HTTP 分块读取格式:
```text
GET /sdk/v1/mcp/env/{envId}/artifacts/{artifactId}?offset=0&limit=1048576
Mcp-Session-Id: ...
Mcp-Protocol-Version: ...
```
`download` 与 artifact 不同。它等待浏览器下载完成,并在成功结果中返回实际
本地 `path`、安全 `filename`、`bytes` 和 `state="completed"`。调用方应使用
返回的路径,不猜测系统 Downloads 目录。
## 11. Prompts 与 Resources
### 11.1 Prompt
服务公开一个可选 prompt:
```text
browser-automation
```
调用示例:
```json
{
"jsonrpc": "2.0",
"id": 20,
"method": "prompts/get",
"params": {
"name": "browser-automation",
"arguments": {
"task": "打开订单页并读取待处理订单数量"
}
}
}
```
它提供推荐的观察、动作、验证和安全规则。Agent 不依赖该 prompt 也能使用
tools。
### 11.2 Resources
`resources/list` 默认包含:
```text
browser://state
```
它是 `browser_state action="get"` 的 resource 形式,因此仍需要 Bridge 扩展。
当前 session 创建的 screenshot/PDF 等 artifact 也会出现在 resources 中。
服务当前不实现 Resource Templates。客户端调用 `resources/templates/list` 得到
JSON-RPC `-32601 method not found` 是预期行为,不应把它判定为 MCP 连接失败。
## 12. 常见错误与处理
| 错误或现象 | 原因 | 处理方式 |
|------------|------|----------|
| `/extension/health` 返回 403 | 把扩展内部接口当作 MCP endpoint | 改用 `127.0.0.1:{sdkPort}/sdk/v1/mcp/env/{envId}` |
| `Mcp-Session-Id is required` | initialize 后请求没有携带 session header | 保存 initialize 响应 header,并在后续请求中携带 |
| `Mcp-Protocol-Version is required` | session 请求缺少协商后的版本 | 使用 initialize 响应中的版本,不要自行猜测 |
| `BROWSER_NOT_READY` | 环境未运行或 CDP 尚未就绪 | 检查环境状态,等待启动完成后有限重试 |
| `BRIDGE_NOT_READY` | 扩展未安装、未连接、状态过期或实例不匹配 | 使用 CDP 可降级能力,或检查扩展是否随当前 env 启动 |
| `PAGE_NOT_FOUND` / `NO_ACTIVE_PAGE` | page 已关闭或尚未发现 | 重新调用 `tabs list` |
| `REF_NOT_FOUND` / stale ref | 页面导航或 document/元素已变化 | 重新 snapshot 后最多重试一次 |
| `REF_FORBIDDEN` | ref 来自其它浏览器环境 | 在目标 env/page 重新观察 |
| `TIMEOUT` | 等待条件未在期限内成立 | 先 snapshot/grep 当前状态,不要立即重复动作 |
| `resources/templates/list -32601` | 服务没有 Resource Templates | 使用 `resources/list`;忽略 Inspector 中该模板入口 |
同一个动作连续失败两次且页面状态没有变化时,应停止盲目重试,重新观察或向
用户报告阻塞原因。