antigravity-browser
Provides policy-constrained automation of Firefox browser profiles, allowing agents to start and stop sessions, navigate and interact with allowlisted pages, take snapshots and screenshots, and execute declarative workflows with challenge-pause handling.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@antigravity-browserStart a persistent Chromium profile and navigate to https://test.example.com"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
浏览器 Profile 隔离工作台与策略约束自动化 MCP
产品定位:面向自有、测试或已获授权网站的本地浏览器环境隔离、持久 Profile 管理与可审计自动化。项目不承诺绕过站点挑战、规避风控、账号不受限制或达到第三方检测站分数。
当前已交付核心能力:
持久 Profile:Firefox/Chromium 环境配置、稳定种子、Cookie 与存储目录生命周期管理;
本地 Studio:Profile 创建、更新、克隆、批量启停、可恢复删除,持久代理池、受管扩展中心、真实出口验证、标签轮换以及带条件/循环/变量/重试/产物的声明式 RPA;
本地安全:REST Bearer/HttpOnly Cookie 鉴权、工作区成员、资源级授权、可撤销哈希 API Key、结构化审计,以及 Cookie、代理密码和 2FA Secret 的 AES-256-GCM 加密;Windows 默认使用 DPAPI 保护 Studio 主密钥;
策略网关:标准 MCP 与 Local REST API 只组合高层动作,限制会话资源、URL 范围和自动化状态。
检测到挑战时,自动化立即暂停。生产挑战的标准结果是 CHALLENGE_DETECTED/SESSION_PAUSED_CHALLENGE,等待受信任人员处理;服务不会刷新、重试、切换环境或与挑战控件交互。
Service Worker 身份链路:Chromium 会通过仅绑定回环地址的浏览器级 CDP 通道,在网站代码执行前暂停目标,统一 UA、平台、Client Hints 与后续网络请求;Firefox 不依赖页面脚本注入,而是使用 Gecko 原生 Profile 首选项统一 Window、Worker、Service Worker 和 HTTP 通道中的 UA、平台、appVersion、语言、硬件并发数及相关请求头。stock Firefox 的 Playwright 时区覆盖不会进入 Service Worker,因此完整时区覆盖仍只在 browser-core/firefox 的版本锁定内核补丁中提供,产品能力接口对此保持 false,不会把部分覆盖冒充为全覆盖。
安装
要求 Node.js 20 或更高版本。
npm ci
npm run install:firefox
npm run build启动本地桌面 Studio:
npm run studio首次启动会在 data/ 创建本机主密钥和 owner token,并通过一次性启动链接写入 HttpOnly Cookie。Windows 上两个启动机密以当前用户 DPAPI 密文保存,旧明文启动文件会自动迁移;业务密文仍使用 AES-256-GCM。可用 STUDIO_MASTER_KEY、STUDIO_ACCESS_TOKEN 和 STUDIO_USERS_JSON 接入外部 KMS 或配置静态多角色凭据。data/ 必须作为敏感目录备份与保护;主密钥丢失后已有密文无法恢复。
Studio 产品 API
Studio API 默认只监听 127.0.0.1,除 /api/v1/health 外均需认证。主要端点包括:
/api/v1/openapi.json:OpenAPI 3.1 入口;Profile 列表支持q、offset、limit,总数返回在X-Total-Count。/api/v1/profiles/trash、/profiles/{id}/restore、/profiles/{id}/purge:回收站、恢复与仅 owner 可用的永久清除。/api/v1/team/*:工作区、成员、资源 grants 以及只在创建时返回明文的可撤销 API Key。/api/v1/extensions、/extensions/import、/profiles/{id}/extensions:仅 owner 可导入的受管 ZIP/XPI 仓库,以及按 Profile 分配扩展。/api/v1/migration/local-browsers、/migration/import-local:仅 owner 可用的本机 Chrome、Edge、Firefox Profile 扫描与网站会话迁移。/api/v1/sessions/{sessionId}/ws:abs-rpc/1有界 WebSocket 自动化协议,支持 status/open/click/type/select/scroll/snapshot/screenshot;它不是原始 CDP。/api/v1/synchronizer/captures:显式启停主窗口动作捕获。只同步可解析的语义目标,密码字段被排除。/api/v1/product/capabilities、/product/runtime-health、/metrics:真实能力边界、外部云运行时适配器状态和本机运行指标。
小状态文件使用同目录临时文件、fsync、原子替换和 .bak 上一版本恢复。该方案为单 Studio 进程设计,不等于支持多个进程同时写入,也不等于已经提供云数据库。云浏览器与 Android 云手机提供标准 provider 适配边界,以及经过鉴权的创建、停止和健康检查 API;未注册并配置真实供应商与凭据时,能力接口会返回未配置,不会创建模拟设备或伪造连接地址。
本机浏览器数据导入
Studio 的“数据迁移与互通”页面可以扫描系统默认位置中的 Chrome、Edge 和 Firefox Profile。导入前必须完全退出源浏览器;服务只接受扫描产生的 sourceId,不接受调用方提交任意本机路径。迁移内容包括 Cookie 数据库、Local/Session Storage、IndexedDB、Service Worker 存储和必要的站点权限数据。密码库、历史记录、书签、自动填充和源浏览器扩展不会复制,符号链接与锁文件也会跳过。
Chromium 的 Cookie 可能受 Windows DPAPI 或 App-Bound Encryption 保护。导入器会复制最小化的 os_crypt 元数据,但不会绕过操作系统保护;如果项目锁定的 Chromium 无法解密,其他站点存储仍会保留,登录 Cookie 需要在导入后的持久 Profile 中手动重新建立。Firefox Cookie 数据库和 origin storage 会复制到 Firefox Profile 根目录。
也可以先从命令行只扫描,再按 sourceId 导入:
npm run import:local
npm run import:local -- <sourceId> confirm-browser-closed代理状态分为 unhealthy、reachable 和 verified。reachable 只代表代理端口可达;只有请求确实通过代理并从出口服务取得 IP 时才是 verified。国家字段只在所配置的出口服务真实返回国家信息时出现,不使用默认国家填充。
开发环境没有 package-lock.json 时可用 npm install 代替 npm ci。npm run install:browsers 下载项目锁定的 Firefox 与 Chromium;只使用单一引擎时可分别运行 npm run install:firefox 或 npm run install:chromium。启动前复制 .env.example,至少设置 BROWSER_ALLOWED_HOSTS:
# POSIX
cp .env.example .env
export BROWSER_ALLOWED_HOSTS='test.example.com,*.staging.example.com'
npm start# Windows PowerShell
Copy-Item .env.example .env
$env:BROWSER_ALLOWED_HOSTS = 'test.example.com,*.staging.example.com'
npm start如果当前网络通过 Meta Tunnel 将已批准的公网域名解析到 198.18.0.0/15 的合成地址,管理员可显式设置 BROWSER_ALLOW_SYNTHETIC_TUNNEL=true。该开关只放行 198.18.0.0/15,仍然拒绝 RFC1918、回环、链路本地和云元数据地址;默认关闭,不应替代正常公网 DNS。
当前版本的服务仍只从环境和 Studio 管理面读取管理员配置;MCP 调用不能传入 allowlist、浏览器可执行文件、扩展包、扩展路径或 profile 路径,但 browser_start 支持按会话传入代理、指纹、GeoIP、语言、时区、地理位置、UA、视口和种子。自定义 UA 的浏览器品牌与主版本必须匹配项目锁定的受管内核,并且只能与受管指纹同时使用;留空时服务会自动生成兼容值。Chromium 的 JS Client Hints 与网络 Sec-CH-UA-* 由同一版本/OS 模型配置,外部 CDP 浏览器禁止注入受管指纹。扩展只能由 Studio owner 导入受管仓库,并通过服务器生成的扩展 ID 分配给持久 Profile。
受管扩展中心
Studio 的“扩展中心”接受最大 12 MiB 的 ZIP/XPI,导入时检查压缩路径、解压大小、文件数量、Manifest V2/V3、危险权限和可执行载荷,并固定原包及解压内容的 SHA-256。nativeMessaging、debugger、management 权限被拒绝;Cookie、代理、历史、下载、剪贴板和 <all_urls> 等高风险权限必须由 owner 显式确认。启动前会再次校验完整性,调用方不能提交任意本机路径。
Chromium 使用服务器受管的解压目录加载扩展;带扩展的 Chromium Profile 必须以 headed 模式启动。Firefox 只接受同时声明固定 Gecko ID 且包内具有 Mozilla 签名结构的 XPI,安装后仍由 Firefox 原生签名校验作最终裁决;服务不会关闭签名要求。扩展的启停或分配在停止并重新启动 Profile 后生效。
默认情况下每个会话使用临时 Profile,停止或到期后清理。若确实需要保留登录态,管理员可将
BROWSER_PERSIST_PROFILES=true,并在 browser_start 中使用安全的 profile 名称;Profile
会保存到 BROWSER_DATA_DIR/profiles/<profile>,因此该目录必须挂载到受保护的持久化卷。服务
不会把同一个持久化 Profile 同时分配给多个活动会话。该开关会持久化 Cookie 和本地存储,
不应在共享主机或未加密卷上启用。
Chromium、Bridge 与控制面
stdio MCP 仍是默认的策略网关;需要浏览器池或远程 Agent 时,先构建并启动独立控制面:
npm run build
CONTROL_PLANE_TOKEN='use-a-long-random-secret' npm run start:control-plane控制面默认监听 127.0.0.1:8081,提供 /api/browsers、/api/bindings、
/api/platforms、/api/skills 和 /ws/agents。设置 CONTROL_PLANE_TOKEN 后,HTTP 使用
Authorization: Bearer ...,WebSocket 可使用同一 Bearer 或查询参数 ?token=...。
Agent 通过 WS 反向连接后可以调用受策略约束的 MCP 工具;这适合 NAT/内网环境。
浏览器池中的 managed 实例由服务启动 Firefox 或 Chromium,cdp 实例通过
cdpEndpoint 接管已有 Chromium,bridge 实例等待 OpenCLI 扩展连接到
/ws/bridge/<browserId>。Bridge 使用带 requestId 的有界 RPC,不开放任意 JavaScript;扩展
侧实现 antigravity-bridge.v1 协议并继续执行页面挑战暂停与人工确认流程。实例可以通过
PATCH /api/browsers/<id> 在停止状态切换模式,并通过 /api/bindings 将站点绑定到指定实例。
仓库现已自带 browser-bridge-extension Manifest V3 扩展。它参考
OpenCLI/Razormind 的“扩展 + 本机守护进程 + chrome.debugger”架构,直接使用常用 Chrome 中
已经登录的页面,不复制 Cookie,也不受 Chrome App-Bound Encryption 的跨内核解密限制。
扩展只允许连接 localhost/127.0.0.1,控制面只接受 navigate、语义快照、点击、输入、
选择、滚动、截图和受限标签页管理;没有原始 Cookie、任意 JavaScript 或任意 CDP 方法入口。
构建并启动控制面后,可生成/复用本机 Chrome Bridge 配置:
npm run build
CONTROL_PLANE_TOKEN='use-a-long-random-secret' npm run start:control-plane
# 另开一个终端,使用相同 Token:
CONTROL_PLANE_TOKEN='use-a-long-random-secret' npm run bridge:setup随后打开 chrome://extensions,启用开发者模式并“加载已解压的扩展程序”,选择命令输出的
绝对目录,在扩展弹窗中填写 endpoint、browserId 和 Token,再在已登录网站点击“绑定当前标签页”。
Chrome 显示“正在调试此浏览器”的提示属于 chrome.debugger 的正常安全提醒。控制面还提供
POST /api/browsers/<id>/call 供受信任的本机客户端调用上述受限操作。
例如把 noVNC 中已登录的 Chromium 接管到池中:
curl -X POST http://127.0.0.1:8081/api/browsers \
-H "Authorization: Bearer $CONTROL_PLANE_TOKEN" -H 'content-type: application/json' \
-d '{"name":"account-a","engine":"chromium","mode":"cdp","cdpEndpoint":"http://127.0.0.1:9222","profileName":"account-a"}'需要可视化登录/扫码时,可选启动 Chromium + noVNC companion:
docker compose -f docker-compose.browser.yml up -d此时 noVNC 在 http://127.0.0.1:6080,CDP 默认在 127.0.0.1:9222。该 compose 使用独立
chromium-profile 卷保存登录态;生产环境应固定经过审计的镜像版本,并将端口置于鉴权反向
代理后。/api/skills 中的动作包目前是可发现目录,带第三方 API Key 的条目会明确标注,
不会伪装成已实现的站点专用选择器。
本地控制面默认允许持续每秒 20 次工具调用、短时突发 40 次,可由管理员通过 MCP_RATE_PER_SECOND 和 MCP_BURST 收紧。限流是单进程安全阀;多实例租户配额仍应由共享网关或 Redis 层统一实施。控制面只审计工具名、阶段、结果和 traceId,不记录调用参数、token、URL、目标名称或输入文本。
每个浏览器会话都有服务端强制的绝对生命周期。BROWSER_SESSION_TTL_MS 未设置时跟随
管理员选择的自动化策略:strict 为 30 分钟、standard 为 2 小时、trusted-local
为 24 小时;显式 TTL 只能收紧策略上限,接受范围仍为 1 分钟至 24 小时。TTL 到期后
服务会停止并清理会话、释放并发槽,后续状态或动作访问返回稳定的 SESSION_EXPIRED。
TTL 不会因导航、动作或人工接管而续期。
BROWSER_AUTOMATION_POLICY 只由管理员通过环境变量选择,默认 standard,模型不能在
工具调用中提升限制。策略同时控制每会话 Tab 数、workflow 步数/时长/结果、滚动档位、
Snapshot 历史容量/保留时间/对象大小和 retained workspace TTL:
策略 | Tab | workflow | 紧凑 Snapshot | workspace |
| 5 | 10 步 / 30 秒 / 64 KB | 32 份 / 10 分钟 / 256 KB | 24 小时 |
| 12 | 50 步 / 2 分钟 / 256 KB | 64 份 / 30 分钟 / 512 KB | 7 天 |
| 20 | 100 步 / 5 分钟 / 1 MB | 256 份 / 24 小时 / 1 MB | 7 天 |
服务仍保留不可被策略提升的硬上限:32 Tab、100 步、5 分钟、1 MB workflow 结果、 20 个滚动档位、256 份 Snapshot、24 小时 Snapshot TTL、16 MiB 历史和 4 MiB 单对象。
Related MCP server: MCP Selenium Grid
allowlist 与网络策略
BROWSER_ALLOWED_HOSTS 必填,逗号分隔,支持精确域名和显式 *.example.com 单标签通配符;裸 *、URL、端口和空项都会导致启动失败。默认只允许 HTTPS,默认阻止环回、RFC1918 私网、链路本地、保留地址和云元数据地址;DNS 解析后的地址和重定向仍会再次检查。
page_fetch 和 cluster_* 任务也使用同一套服务端 URL 策略。轻量抓取仅允许 GET/HEAD,不能提交 body 或任意请求头;重定向逐跳检查且有数量上限,响应体也有大小上限。响应头使用服务端白名单,只返回缓存与内容元数据;Set-Cookie、认证挑战头和其他未批准响应头不会返回给 MCP 调用方或写入集群任务结果。
page_fetch 的 HTTP(S) 连接会复用策略在本次请求中解析并批准的地址,并把该地址固定到 TCP socket;原始主机名仍用于 Host 和 HTTPS SNI。浏览器 Playwright 的导航/资源路由可以在连接前 fail-closed 地检查 allowlist、DNS 和私网地址,但无法在应用层把 Firefox 的每条底层 TCP 连接可靠地 pin 到该解析结果。因此生产部署必须在进程或容器出口使用只允许批准目标的 egress firewall 或显式代理,并在网络层阻断环回、RFC1918、链路本地、云元数据及未批准目标;应用层检查不能替代该出口控制。
仅在自有本地 fixture 且经过评审时,才在服务端显式设置 BROWSER_ALLOW_HTTP=true 或 BROWSER_ALLOW_PRIVATE_NETWORK=true。私网开关打开时会写 stderr 警告;示例配置不会打开它,也不给任何私网地址。
通用生产出口验收
使用环境变量驱动,不把业务域名写入代码:
$env:BROWSER_ALLOWED_HOSTS = 'shop.example.com,static.shop.example.com'
$env:ACCEPTANCE_TARGET_URLS = 'https://shop.example.com/health,https://static.shop.example.com/'
$env:ACCEPTANCE_EXPECTED_EGRESS_IPS = '203.0.113.10'
$env:ACCEPTANCE_EGRESS_IP_URL = 'https://你的出口探针域名/ip'
npm run acceptance:egress脚本只执行无登录 GET、策略预检和手动检查重定向,不跟随未授权重定向,不读取响应正文。未设置 ACCEPTANCE_TARGET_URLS 时仅执行应用层策略矩阵;未设置预期出口 IP 时出口检查标记为 SKIP,不会冒充生产网络验收。使用 npm run acceptance:egress -- --fixture 可在无生产域名时执行确定性的 allowlist、HTTPS、私网和元数据阻断验收。
MCP 配置
mcp-config.example.json 是可复制的最小示例。将 args 改成生成后的绝对路径,并替换为自己管理的测试域名。Windows 配置:
{
"mcpServers": {
"compliant-firefox": {
"command": "node",
"args": ["C:\\path\\to\\antigravity-browser\\dist\\index.js"],
"env": {
"BROWSER_ALLOWED_HOSTS": "test.example.com",
"BROWSER_ALLOW_PRIVATE_NETWORK": "false"
}
}
}
}POSIX 配置:
{
"mcpServers": {
"compliant-firefox": {
"command": "node",
"args": ["/opt/compliant-firefox/dist/index.js"],
"env": {
"BROWSER_ALLOWED_HOSTS": "test.example.com",
"BROWSER_ALLOW_PRIVATE_NETWORK": "false"
}
}
}
}stdio 的 stdout 只承载 MCP 协议帧;启动错误、清理错误和私网警告写 stderr。不要把调试输出重定向到 stdout。
工具
服务注册以下 40 个标准 MCP 工具:
工具 | 作用 |
| 启动受策略约束的 headless/headed Firefox 会话 |
| 查询状态、页面摘要、挑战状态和最近的安全阻断事件 |
| 只读检查 User-Agent、平台、语言、时区、Viewport、硬件、WebGL 与 |
| 停止并清理会话(可重复调用) |
| 仅在暂停状态请求人工接管窗口 |
|
|
| 将会话切换到 headed 人工控制并签发短期一次性 lease token |
| 用 lease token 和显式人工确认把控制权交还自动化 |
| 使用受 URL 策略约束的轻量 HTTP 客户端读取页面(仅 GET/HEAD) |
| 打开 allowlist 内的绝对 HTTP(S) URL |
| 返回带 |
| 结构化批量抽取页面列表/表格数据(根据 Schema 批量提取) |
| 返回内嵌图像与不透明 |
| 按自适应语义目标点击唯一目标 |
| 按自适应语义目标输入有界文本,可选择清空/提交 |
| 按 |
| 以有界档位滚动 |
| 等待短时长或安全语义条件 |
| 按管理员策略串行执行声明式高层步骤,遇中断立即停止 |
| 列出当前活动浏览器工作区 |
| 查询工作区控制权与保留策略 |
| 将工作区交给人工并签发短期 lease |
| 经人工确认后恢复 Agent 控制 |
| 执行有界声明式步骤并返回停止原因/快照 |
| 列出会话中的受控标签页 |
| 切换当前活动标签页 |
| 关闭指定标签页 |
| 返回工具、限制与明确禁止的底层能力 |
| 提交异步爬取/渲染任务至分布式优先级队列(支持轻重双模) |
| 批量提交爬取任务到集群调度队列(支持并发与重试控制) |
| 查询集群 Worker 节点状态与队列统计 |
| 按 |
| 按 |
提交任务时可带稳定的 projectId / runId(仅允许字母、数字、., _, -,最多 64 字符),随后用 cluster_list_tasks 查询某次运行;查询仍按租户隔离并受既有 URL allowlist、重试、租约和审计策略约束。
语义目标示例:
{
"sessionId": "ses_example_1234",
"target": { "role": "button", "name": "保存", "exact": true }
}page_snapshot 返回的短期 opaque ref 也可直接使用,例如
{"target":{"ref":"ref_..."}};页面导航或 DOM 身份变化后 ref 会失效,需重新快照。语义目标会在服务端通过有界快照解析,并且匹配不唯一时拒绝执行。
一个最小流程(具体 sessionId 由 browser_start 返回):
browser_start({"headless":true,"viewport":{"width":1280,"height":800})
page_open({"sessionId":"ses_...","url":"https://test.example.com/login"})
page_snapshot({"sessionId":"ses_..."})
page_type({"sessionId":"ses_...","target":{"label":"邮箱"},"text":"qa@example.com"})
page_click({"sessionId":"ses_...","target":{"role":"button","name":"继续"}})
page_screenshot({"sessionId":"ses_..."})
browser_stop({"sessionId":"ses_..."})动作会经过服务端可见性、唯一性、可操作性、URL 和挑战门禁检查。页面改变后应重新 page_snapshot;不要猜测或复用失效目标。
Snapshot v2 与写动作防重
page_snapshot 在保留原有结构化字段的基础上返回 snapshotId、pageRevision、content、contentBytes 和明确的截断状态。设置 format: "compact" 时,响应只保留模型可直接阅读的紧凑 content,不会重复携带正文和 target 数组;maxBytes(100–4 MiB)限制紧凑内容的 UTF-8 字节数,实际值仍受当前管理员策略限制。
page_snapshot({"sessionId":"ses_...","format":"compact","maxBytes":8000})
page_snapshot({"sessionId":"ses_...","sinceSnapshotId":"snp_..."})传入 sinceSnapshotId 时,服务从当前会话、当前 Tab 的有界内存历史中返回 changes(added、removed、updated 和 revision 变化)。历史容量、保留时间和对象大小跟随管理员策略;只保存脱敏语义节点和文本摘要,不保存页面正文、URL、标题或 compact content。未知、过期或来自其他 Tab 的基线分别返回 SNAPSHOT_NOT_FOUND/SNAPSHOT_EXPIRED。Snapshot 与写动作应同时记录返回的 tabId。
page_open、page_click、page_type、page_select、page_scroll 和 page_workflow 可选携带:
actionId:UUID。同一会话内,同 ID、同参数的重试直接复用首次 Promise/结果,不会重复写;同 ID、不同参数返回ACTION_ID_CONFLICT。expectedPageRevision:写动作开始前必须与当前 revision 一致,否则返回可重试的PAGE_REVISION_MISMATCH,调用方应重新快照并使用新的 actionId。expectedTabId:写动作开始前必须仍在同一受控 Tab;切换 Tab 后即使 revision 数值相同也返回PAGE_REVISION_MISMATCH。
幂等缓存按会话隔离,最多保留 256 项、TTL 10 分钟,停止会话时清空。缓存只保留 SHA-256 参数摘要和安全结果,不持久化输入正文。
声明式 workflow
page_workflow 只接受 open、click、type、select、scroll、wait 和 snapshot。它不接受循环、变量、表达式、JavaScript、CSS/XPath、raw selector 或协议命令。workflow 步数、总时长、结果大小和单个 Snapshot 大小跟随管理员策略,且不会超过硬上限 100 步、5 分钟、1 MiB 和 4 MiB;会话在执行期间被独占,外部交错动作返回 SESSION_BUSY。challenge、popup/page-crash interrupt、revision mismatch、歧义、超时或任一步错误都会停止后续步骤;dialog/download 是否作为状态中断停止由 stopOn 控制,未显式设置时保持安全默认;敏感输入结果只返回长度。
page_workflow({"sessionId":"ses_...","expectedTabId":"tab_1","steps":[
{"op":"open","url":"https://test.example.com/profile"},
{"op":"click","target":{"role":"button","name":"编辑"}},
{"op":"snapshot","format":"compact","maxBytes":8000}
]})page_workflow_execute 使用同一套管理员策略和硬上限,但只接受 WorkflowStepSchema:不接受 type 字段,每一步使用 op 和语义目标,scroll.amount 按策略限制(硬上限 1–20),select.values 会串行执行。stopOn 只控制可选的 navigation/dialog/download/ambiguity 停止条件;challenge、popup/page-crash、revision mismatch、超时和错误仍始终停止。遇到中断时返回 stoppedReason 与有界当前 Snapshot。超过当前策略步数返回 WORKFLOW_STEP_LIMIT_EXCEEDED。
browser_start 可传 workspaceName 与 workspaceRetention。工作区生命周期通过 workspace_list/workspace_get 查询;workspace_handoff 后所有 Agent 写操作返回 USER_CONTROL_HARD_STOP,只有一次性 lease 与 humanConfirmed: true 的 workspace_resume 才能恢复。启用租户认证时,会话/工作区工具必须携带有效 tenantId 与 tenantToken;服务按租户过滤并拒绝跨租户 session/workspace 访问。retain/keep_until 工作区记录只在当前进程内保留,并受服务端 1 分钟至 7 天 TTL 上限约束,进程重启不会恢复浏览器或记录。
一个会话的受控 Tab 数按管理员策略限制(硬上限 32)。新窗口通过 URL 策略后才进入 page_list_tabs;page_switch_tab 切换后续页面操作的目标,Tab 之间的 tabId、page revision、semantic ref 和 Snapshot history 完全隔离;headed handoff/reopen 会恢复已打开 Tab 的数量与 URL;超限弹窗自动关闭并记录 TAB_LIMIT_EXCEEDED。
browser_capabilities 返回服务版本、当前策略、工具清单、有效资源限制、并发/Tab/TTL 限制和私网开关,并明确列出禁止的 raw_evaluate、raw_selector、raw_cdp、unmanaged_extension_loading、arbitrary_extension_path 等底层能力。代理、确定性环境和受管扩展配置通过受校验的高层管理面提供,不开放原始协议或任意路径注入。
安全阻断事件
popup、原生 dialog 和 download 仍默认关闭、dismiss 或 cancel,不会扩大能力边界;但它们不再静默消失。browser_status.interrupts 返回累计数量、最新 sequence 和最多 16 条脱敏事件摘要,类型包括 POPUP_BLOCKED、DIALOG_BLOCKED、DOWNLOAD_BLOCKED 和 PAGE_CRASHED。摘要不包含 dialog 正文、下载 URL 或页面敏感内容。
page_extract 同时限制条数、单字段字符数和完整 JSON 的 UTF-8 字节数。字段会在页面侧先截断,累计结果超过服务端硬上限时返回 RESOURCE_EXHAUSTED,避免把无界页面文本带入 MCP 或 Redis。
Headless、headed 与人工接管
headless: true 适合 CI 和无图形桌面测试,但不能人工操作。如果检测到 Cloudflare/Turnstile/CAPTCHA 或其他机器人挑战,会话进入暂停状态。此时只允许状态、语义快照、截图、有界等待、停止和人工接管;导航、点击、输入、选择和滚动全部拒绝。
headless: false 使用 headed Firefox。browser_reopen_headed 不是挑战求解器:它只把已暂停会话交给受信任操作员。操作员完成站点要求后,调用方仍必须显式发送 browser_resume({"sessionId":"ses_...","humanConfirmed":true});服务端会再次检测,挑战仍在时保持暂停。没有图形桌面的服务器不能提供本地人工接管,应停止或转到经过审批的人工流程。
普通人工复核可调用 browser_handoff,服务会在需要时以 headed 模式重启同一服务端 profile,并进入 USER_CONTROLLED。此期间页面读取和写入都 hard-stop;返回的 lease token 只出现一次,服务仅保留 SHA-256 摘要。操作员结束后调用 browser_takeover 并提供 token 与 humanConfirmed: true,服务重新扫描 challenge 后才恢复。lease 过期不会自动把控制权交回 Agent。本地 stdio 无法从密码学上证明“确认”一定来自人类,因此宿主/操作台必须把这两个工具置于受信任的人机审批边界,不能让普通 Agent 自行完成整套交接。
测试自己的 Turnstile fixture 时,应使用 Cloudflare 官方测试 sitekey/secret,不要把生产凭据或生产挑战放进自动化测试。参见 Cloudflare 官方 Turnstile 测试密钥文档。测试密钥只用于测试环境;遇到真实生产挑战,本服务的合格行为仍然是暂停,不是“通过”。
集群运行
Master 仍是本地 stdio MCP 进程;集群模式只把任务队列放到 Redis。先启动 Redis 和 Worker:
docker compose -f docker-compose.cluster.yml up --build这个 compose 文件提供的是单实例 Redis 开发环境;原生 Redis Cluster 通常连接已有的托管/运维集群,不要把该 compose 的单实例地址直接当作 Cluster 启动节点。
启动前必须设置 BROWSER_ALLOWED_HOSTS。本地 MCP 配置还需要设置 REDIS_URL,Worker 使用 npm run start:worker 对 Redis 队列进行消费。当前集群 Worker 的 HTTP 模式只支持受策略约束的 GET/HEAD;浏览器模式使用服务端受控 Firefox 会话。调高 WORKER_CONCURRENCY 时应同步调高 BROWSER_MAX_SESSIONS。
Worker 与 MCP 进程都读取相同的 BROWSER_SESSION_TTL_MS,浏览器-only Worker 不会
创建控制面 Redis 连接;请在所有 Worker 上保持会话 TTL、并发和 URL 策略配置一致。
适配器支持两种 Redis 形态:
单实例本地开发形态:设置
REDIS_MODE=standalone和REDIS_URL。原生 Redis Cluster:设置
REDIS_MODE=cluster、REDIS_CLUSTER_NODES(逗号分隔的启动节点)和相同的REDIS_SHARD_COUNT。每个“租户 + 分片”使用 Redis hash tag,单次 Lua 出队/租约操作保持在一个 Cluster slot 内;不同租户和分片可以分散到不同节点。REDIS_URL可用rediss://user:password@node:port为 Cluster 节点提供统一 ACL/TLS 配置。
任务、URL 排重、Worker 心跳和状态查询都按租户隔离。同一个 URL 可以被不同租户分别提交;同一租户内仍然是原子排重。URL 声明成功但任务入队失败时会执行租户内补偿释放,避免失败提交占用长期排重 TTL。生产环境设置 TENANT_CREDENTIALS_JSON 后,四个 cluster_* 工具必须携带对应的 tenantId 和 32 字符以上 tenantToken;角色为 read 的租户只能查询,submit 才能提交任务。token 只在 MCP 进程内校验,不写入 Redis 任务记录、审计事件或错误响应。Worker 用 WORKER_TENANTS=tenant-a,tenant-b 声明可信消费范围,未配置时仅消费 default 租户。
cluster_* 是跨进程的控制面能力;browser_* 会话仍归属当前 MCP/Worker 进程,不会因为租户字段而共享浏览器 profile。无 TENANT_CREDENTIALS_JSON 时仅适合本机开发,默认租户为 default;生产部署应同时使用 TLS/ACL、密钥管理和独立的 Worker 租户白名单。
启用 TENANT_CREDENTIALS_JSON 后,browser_*、page_* 和 workspace_* 工具也必须携带对应的 tenantId 与 tenantToken;凭据只在 MCP 进程内校验,绝不转发给 manager、浏览器页面、Redis、审计或错误响应。浏览器工作区不会跨进程共享,租户认证不等于浏览器状态持久化。
错误、审计与测试
工具失败仍返回 MCP isError: true,并在 structuredContent 和短文本中提供稳定 JSON:
{
"ok": false,
"sessionId": "ses_...",
"traceId": "tr_...",
"error": {
"code": "TARGET_NOT_FOUND",
"message": "The target was not found.",
"retryable": false,
"details": {}
}
}常见 code 包括 SESSION_NOT_FOUND、SESSION_EXPIRED、INVALID_STATE、SESSION_PAUSED_CHALLENGE、NAVIGATION_DENIED、PRIVATE_NETWORK_DENIED、TARGET_NOT_FOUND、TARGET_AMBIGUOUS、ACTION_TIMEOUT、BROWSER_LAUNCH_FAILED 和 INTERNAL_ERROR。输入、密码、Cookie、Authorization、页面全文和 URL query 不写入普通审计;审计为服务端追加 JSONL。
npm run typecheck
npm test
npm run buildMCP 契约测试覆盖 tools/list 的精确工具集合、严格 schema、注解、输入错误、截图路径隔离和 manager stub 调用。默认集成测试使用注入 launcher,不启动真实浏览器。安装项目锁定的 Firefox 后,可运行 npm run test:firefox,以本机真实 Firefox 访问本地 fixture;该测试不会访问、求解或统计真实 Cloudflare/CAPTCHA 页面。
只有 npm run test:firefox 在部署宿主通过后,才可把该宿主标记为 Firefox 运行时就绪。单元测试、类型检查或 fake-launcher 集成测试通过,不等价于本机 Firefox 可启动。
故障排查
启动立即失败:检查
BROWSER_ALLOWED_HOSTS是否设置、是否包含裸*、URL/端口/空项;检查数据目录和审计路径是否为可写的绝对路径。浏览器启动失败:运行对应的
npm run install:firefox或npm run install:chromium,确认 Node、lockfile 与 Playwright 版本一致,再运行真实浏览器测试。服务只启动 Playwright 锁定的内核,并核对实际版本;不会回退到系统 Firefox、Chrome/Edge,也不会跨引擎替代。Windows 上 Firefox 若只看到spawn UNKNOWN,请同时检查“事件查看器 → Windows 日志 → 应用程序”的SideBySide事件;若指向mozglue激活上下文/程序集错误,应在受支持的干净 Windows 或 Linux 宿主复验,不要修改或替换浏览器二进制。导航被拒绝:确认 URL 为 HTTP(S)、主机精确命中 allowlist,且 DNS 结果不是私网/元数据地址。不要用重试绕过策略。
动作暂停:先调用
browser_status/page_screenshot。挑战状态下不要点击、输入、滚动、导航或刷新;请求人工接管或停止。目标找不到/歧义:先重新
page_snapshot,改用唯一的 role/name、label 或 testId;不要改成 CSS、XPath 或坐标。stdout 出现非 JSON-RPC 文本:将调试日志改写到 stderr,并检查启动脚本、shell profile 和第三方包装器。
This server cannot be deployed
Maintenance
Related MCP Connectors
- TabfleetOAuthcom.tabfleet
Launch, inspect, control, and share isolated cloud browsers for your agents.
Undetectable cloud browser sessions for AI agents and scrapers. Navigate, extract, click, captcha.
Run multi-step tasks in a real Chrome browser: persistent environments, live view, human takeover.
Stealth web automation for AI agents. Login, signup, navigate, screenshot.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables AI applications to automate your existing browser using your logged-in profile. Provides fast, private browser automation that avoids bot detection by working with your real browser fingerprint.5,899 npmApache 2.0
- AlicenseNot gradedqualityDmaintenanceEnables browser automation by managing Selenium Grid browser instances (Chrome, Firefox, Edge) in Docker or Kubernetes environments. Provides secure, scalable browser control for AI agents and testing workflows.8MIT
- AlicenseNot gradedqualityDmaintenanceProvides stealthy browser automation capabilities using a custom Firefox build designed for anti-detection. It enables users to navigate pages, interact with elements, and manage persistent browser sessions through natural language.17 npm1MIT
- AlicenseAqualityDmaintenanceEnables browser automation with anti-detection features, including navigation, interaction, form filling, and session management.229MIT