浏览器MCP容器
先决条件
- 码头工人 Docker Compose
- 谷歌Chrome或Chromium(用于cookie导出扩展程序)
快速开始
- 克隆仓库:
git clone https://github.com/kenpodragon/browser-mcp-containers.git
cd browser-mcp-containers- 在Chrome中安装cookie导出器扩展程序:
- 引导到 chrome://extensions - 启用 开发人员模式 (右上角切换) - 点击 装载时未包装 - 选择 docker/extensions/cookie-exporter/ 目录
- 导出会话数据:
- 单点:导航到您的应用程序(已登录),单击扩展图标,单击 下载全部 - 多个站点:访问每个网站,单击 抓取数据库 在每个上,然后单击 下载全部 一次
- 将导出的文件复制到共享目录中:
cp ~/Downloads/storageState.json docker/shared/- 启动容器:
cd docker && docker compose up -d- 复制
.mcp.json.sample从repo根目录到项目目录.mcp.json重命名服务器密钥以匹配您的项目(例如。,playwright-webapp,chrome-devtools-extension-dev).
- 如果使用克劳德代码,请复制
CLAUDE.md.sample对于您的项目CLAUDE.md用于工具参考和故障排除提示。
- 在项目目录中打开Claude Code(或您的MCP客户端)。浏览器工具现在可用。
- 验证:
curl http://localhost:9100/sse应返回SSE事件流。在Claude Code中,尝试browser_navigate确认工具是否正常工作。
剧作家vs Chrome DevTools
| 编剧 | Chrome DevTools | |
|---|---|---|
| 最佳 | Web应用程序测试、身份验证浏览 | Chrome扩展程序测试/开发 |
| 扩展支持 | 否(扩展程序无法在内部运行) | 是(完全加载Chrome扩展程序) |
| Cookie加载 | 原生via storageState.json | 通过cookie注射器扩展 |
| 反侦测 | --init-script JS注入 | 内容脚本+Chrome标志 |
| 基础镜像 | mcr.microsoft.com/playwright/mcp | node:20-slim +测试用Chrome |
| 内部端口 | 3000 | 9222 |
| 更简单的设置 | 是 | 更多活动部件 |
出口管道
为什么是浏览器扩展? 从Chrome v146+开始,Cookie使用应用程序绑定的DPAPI进行加密。外部工具(Python脚本、独立解密器)无法再解密Chrome Cookie。IndexedDB(Firebase和其他框架存储身份验证令牌的地方)也是源范围的,外部无法访问。浏览器扩展是导出两者的唯一可靠方法。
扩展捕获了什么:
- 所有Cookie 从当前Chrome配置文件
- 完整浏览器指纹:用户代理、平台、屏幕尺寸、WebGL渲染器、客户端提示、语言、时区、连接信息、硬件规格
- 索引数据库 从活动选项卡的来源(Firebase身份验证、应用程序状态等)
多站点工作流程: IndexedDB是源范围的-- indexedDB.databases() 仅返回当前页面来源的数据库。要从多个站点捕获身份验证状态,请执行以下操作:
- 引导到 现场A (已登录)--单击 抓取数据库
- 引导到 现场B (已登录)--单击 抓取数据库
- 对所有需要身份验证的站点重复此操作
- 点击 下载全部 --一个
storageState.json与一切
该扩展通过以下方式跨站点累积IndexedDB数据 chrome.storage.local。可折叠面板显示所有收集的来源和数据库,以便在下载前进行查看。这 清除 按钮重置集合。
导出格式: 剧作家兼容 storageState.json 加上 browserSignature 和 indexedDB 领域。这 indexedDB 密钥按来源组织:
{
"cookies": [...],
"browserSignature": {...},
"indexedDB": {
"http://localhost:5173": [{ "name": "firebaseLocalStorageDb", ... }],
"https://www.linkedin.com": [{ "name": "beacons", ... }]
}
}Playwright和Chrome DevTools容器都从同一个文件读取。注入是源感知的——每个页面只获取与其源匹配的IndexedDB数据,因此多个站点具有相同的数据库名称(例如。 firebaseLocalStorageDb)不要碰撞。
反检测和身份注入
在容器启动时, generate-anti-detect.py 读取 docker/shared/storageState.json 并生成JavaScript补丁,使容器的浏览器与真实浏览器的指纹相匹配,并在页面脚本运行之前注入IndexedDB身份验证数据。
欺骗信号:
navigator.platform,navigator.languages,navigator.userAgentscreen.width,screen.height,screen.availWidth,screen.availHeightnavigator.deviceMemory,navigator.hardwareConcurrencynavigator.connection(有效类型、下行链路、rtt)navigator.webdriver(设置为false)- 客户提示(
Sec-CH-UA,Sec-CH-UA-Platform等等) colorDepth,pixelDepth,devicePixelRatio,doNotTrack
匹配的Chrome标志: --user-agent, --accept-lang, --window-size 被设置为与捕获的签名相匹配。
索引DB注入: 如果 storageState.json 包含一个 indexedDB 字段,生成的脚本拦截 indexedDB.open() 在页面脚本运行之前调用并注入导出的记录。这是源代码感知的——只有与当前页面的源代码匹配的数据库才会被注入。这使得Firebase Auth、会话令牌和其他IndexedDB存储状态能够转移到容器中。
已知限制: 没有GPU透传,WebGL渲染器就无法被欺骗。容器报告SwiftShader,而不是真实的GPU。这是唯一剩下的可检测信号。
MCP配置
示例 .mcp.json 对于您的项目:
{
"mcpServers": {
"playwright-myproject": {
"type": "sse",
"url": "http://localhost:9100/sse"
}
}
}要点:
- 重命名服务器密钥 每个项目(例如。,
playwright-webapp,chrome-devtools-extension-dev) - 使用
/sse端点,不/mcp流式HTTP返回400。 - 始终设置
"type": "sse"明确地 当您有其他使用HTTP传输的MCP服务器时.mcp.json没有它,Claude Code会悄无声息地跳过SSE服务器——没有错误,没有警告,只是缺少工具。 - 看
.mcp.json.sample以repo根目录为例。
本地主机转发
当你的应用程序运行时 localhost:3000 在主机上,默认情况下容器内的浏览器无法访问它。这 FORWARD_PORTS 环境变量修复了这个问题。
它是如何工作的: 在容器启动时, socat TCP转发器在容器内启动。每个人都在听 localhost:PORT 并透明地将流量代理到 host.docker.internal:PORT,到达主机。
配置 在 docker-compose.yml:
environment:
FORWARD_PORTS: "5173,8080,8000"这将转发三个端口。容器内的浏览器现在可以导航到 localhost:5173, localhost:8080,或 localhost:8000 并联系您的主机服务。
端口冲突警告: 不要将容器的内部MCP端口包含在 FORWARD_PORTS.Playwright内部使用端口3000,Chrome DevTools使用9222。包含这些将导致socat绑定失败(入口点会跳过它们并发出警告)。
当你需要它时: 每当容器中的浏览器需要访问开发服务器、API或主机上运行的任何服务时。
所有端口分配(主机映射、内部MCP端口和转发端口)都在中定义 docker-compose.yml,为您提供了一个查看和管理所有集装箱的完整港口布局的单一位置。
添加更多容器
- 在中复制服务块
docker/docker-compose.yml - 更改这些值:
- 服务名称(例如。, playwright-newproject) - 主机端口映射(例如。, 9102:3000 对于剧作家来说, 9103:9222 Chrome DevTools) - PROJECT_NAME 环境变量 - FORWARD_PORTS 根据项目需要
- 跑
docker compose up -d启动新容器。
每个项目一个集装箱。 MCP服务器设计为单客户端(基本协议限制)。连接到同一容器的两个代理会话将发生冲突。
港口惯例: 使用端口 9100-9149 用于浏览器MCP服务。
已知的戈查斯
- MCP单客户端:每个容器一次服务一个AI代理会话。如果连接似乎卡住,请重新启动:
docker compose restart "type": "sse"必需的:在混合HTTP+SSE传输时.mcp.json,你 必须 集"type"在每个服务器条目上明确显示。否则,Claude Code会自动跳过SSE服务器。- 陈旧的项目缓存:如果配置更改后MCP工具未出现,请删除 `~/.claude/projects/
/` 并重新加载IDE。
- Chrome DevTools屏幕截图:保存在容器内
/app/。使用以下方式检索:docker cp :/app/screenshot.png ./ - 环境变量变化:
docker compose restart不会重新读取环境变量。使用docker compose up -d相反。 - 剧作家基础形象:官方形象为非根。Dockerfile使用
USER root之前apt-get install. - 身份验证页面首先显示登录信息:导航到受身份验证保护的页面(例如Firebase)时,初始页面快照可能会显示登录屏幕。身份验证注入是异步的——检查
browser_console_messages或者在片刻之后拍摄第二张快照。这Page URL将显示重定向的URL(例如。/game或/feed)一旦身份验证完成。 - 约束错误警告A.
ConstraintError: An object store with the specified name already exists控制台中可能会出现警告。这是无害的——IndexedDB拦截器和页面框架都试图创建相同的存储。Auth仍然有效。 - 导出时IndexedDB为空:确保你在实际的网站页面上(而不是
chrome://或about:blank)并在单击之前登录 抓取数据库IndexedDB是源范围的,只返回当前选项卡源的数据库。 - Auth停止工作:代币
storageState.json到期。重新导出:在Chrome浏览器中访问网站(登录),点击 抓取数据库那么 下载全部,保存到docker/shared/,以及docker compose up -d --force-recreate.
学分
- 作者 斯蒂芬·萨拉卡
- 博客: does-god-exist.org
- 作为多项目AI代理基础设施的一部分构建
- 看
FINDINGS.md了解这是如何建造的以及我们学到了什么 - 看
Q_and_A.md用于故障排除和常见问题
