自建 MCP 浏览器自动化服务:给 AI Agent 装上"能登录的手"

本文记录我自己搭建的一套 MCP 浏览器自动化服务:把"能操作浏览器"(打开网页、登录、点击、输入、过人机验证)做成 MCP 工具,让 AI Agent 像调用普通函数一样操作真实浏览器,登录态跨调用保持。

技术栈:Hermes Agent + conhub MCP Server + 无头 Chromium + Playwright + CDP。京东扫码登录 + 选电脑只作为端到端验证案例——真正的重点是 MCP 服务本身怎么设计、怎么踩坑、怎么修。


一、为什么需要"浏览器 MCP"

AI Agent 能读文档、调 API、写代码,但有一类事做不了:需要登录态的真实网页操作——比如查电商价格、处理后台、填表单。普通 HTTP 请求做不到(要 JS 渲染、要登录 Cookie、要人机验证)。

方案对比:

方案 登录态保持 人机验证 结论
browserless REST API ❌ 每次独立 ❌ 无法介入 放弃
browserless CDP 直连 ❌ 导航后 target 被关 放弃
本地 Chromium 长驻 + MCP 会话管理 ✅ 人工介入通道 采用

关键决策:不接任何第三方打码平台/代理池(数据隐私 + 成本),人机验证走"人工介入通道"——需要扫码/滑块时,人在网页上看着画面操作,Agent 的浏览器同步执行。


二、架构设计

┌─────────────────────────────────────────────┐
│           conhub-mcp 容器 (:9100)            │
│                                              │
│  MCP Server (Python, mcp==2.0.0)             │
│   ├── browser_session_start/end   会话管理    │
│   ├── browser_goto/click/type/eval 操作      │
│   ├── browser_screenshot/content/pdf 取数    │
│   └── browser_viewer() → 人工介入地址        │
│              │                              │
│              ▼ CDP (127.0.0.1:9222)          │
│   Chromium 无头浏览器(容器启动即预热)        │
│              │                              │
│   viewer.py (:9223)  ←── 实时投影 + 操作     │
│   (人工介入:扫码/滑块/点选/输入)            │
└─────────────────────────────────────────────┘

三个关键设计:

1. Chromium 常驻 + 预热(消除冷启动)

容器启动时立即拉起 chromium(subprocess.Popen),MCP 调用时复用而不是现拉——首次调用不再有 120 秒冷启动超时:

# 容器启动预启动 chromium
def prewarm():
    subprocess.Popen([
        chrome, "--headless=new", "--no-sandbox",
        "--disable-gpu", "--disable-dev-shm-usage",
        f"--remote-debugging-port={9222}", "about:blank"
    ])

2. 会话管理(登录态跨调用保持)

MCP 每次调用都连接同一个 chromium 实例(connect_over_cdp),Cookie、登录态、DOM 全部保留在浏览器进程里:

async def session_start(self):
    if self._browser:  # 已连接 → 复用
        self._page = self._ctx.pages[0]
        return {"reused": True}
    self._browser = await self._pw.chromium.connect_over_cdp(
        f"http://127.0.0.1:{9222}")

3. 网页查看台(人工介入通道)

viewer.py 起一个 HTTP 服务(:9223),把 Agent 浏览器的画面实时投影成网页,并提供操作端点:

端点 功能 用途
GET /shot 实时截图(1920x1080) 看 Agent 在干嘛
POST /click {x,y} 坐标点击 点选图片验证
POST /drag {x1,y1,x2,y2} 鼠标拖拽 过滑块验证
POST /type {text} 键盘输入 填验证码

查看台界面:实时画面 + 点击/滑块/输入操作栏

前端把鼠标事件换算成截图坐标((clientX-rect.left) × naturalWidth/rect.width)POST 回后端,后端用 Playwright 的 mouse.click/drag/type 在真实浏览器里执行——画面永远和 Agent 操作同步


三、10 个 MCP 工具

工具 功能
browser_session_start() 启动/复用浏览器(秒回)
browser_session_end() 关闭浏览器
browser_goto(url) 导航
browser_click(selector) 点击(CSS 或按钮文字)
browser_type(selector, text) 输入(Vue/React 兼容)
browser_eval(js) 执行 JS 取数据
browser_wait(ms) 等待
browser_screenshot() 截图
browser_viewer() 返回人工介入地址
browser_content/pdf/function/connect 取文本/PDF/自定义脚本

Agent 侧用法(一次登录态会话):

1. browser_session_start()      # 复用浏览器
2. browser_goto(url)
3. browser_type(selector, text) # 填表单
4. browser_click('Log In')
5. browser_wait(4000)
6. browser_eval(js)             # 验证登录态 / 取数据
7. browser_viewer()             # 需人工时给用户地址

四、搭建踩坑记录(重点)

坑 1:查看台"点不动"——CDP 连接冲突死锁 ⚠️ 最严重

现象:查看台能打开,但截图永远不刷新、点击毫无反应,请求永久卡死。

排查curl -m 5 :9223/shot 超时;docker logs 看到 BrokenPipe。

根因:查看台的 /shot /click /drag /type 各开了一个独立 sync_playwright() 连接 CDP——与 MCP 会话(asyncio)的 CDP 连接互相抢端口,全部挂起。

修复:查看台操作全部复用主 asyncio loop,用 run_coroutine_threadsafe 提交到 MCP 的同一个事件循环:

def click_at(self, x, y):
    fut = asyncio.run_coroutine_threadsafe(self._click_at_async(x, y), self._loop)
    return fut.result(timeout=10)  # 复用主连接,零冲突

修复后:/shot 0.1s、/click 0.03s 秒回。

坑 2:截图太小(780x437)

connect_over_cdp 的默认 context 没有 viewport,截图只有 780x437。修复:page.set_viewport_size({"width": 1920, "height": 1080})

坑 3:mcp 库版本地狱

  • 基础镜像自带 mcp 1.26.0 → 没有 mcp.server.mcpserver(session 管理)
  • 最新版 → import 报错
  • 锁定 mcp==2.0.0 才正常

坑 4:Dockerfile 构建细节

  • 基础镜像 entrypoint 是 node(hermes-web-ui)会覆盖 CMD → 必须 --entrypoint python3 server.py
  • 构建时 DNS 解析失败 → docker build --network=host
  • 镜像里没 pip → 用 uv pip install --python <venv>
  • chromium 要 playwright install chromium --no-shell 固化进镜像(docker exec 装的重启就没了)

坑 5:Chrome 忽略远程调试地址参数

--remote-debugging-address=0.0.0.0 被忽略,chromium 只绑 127.0.0.1——所以不能用 TCP 转发器转发 9222,改成 viewer.py 在进程内直接起 HTTP 服务(:9223)读取 CDP。


五、端到端验证:京东扫码登录 + 选电脑

服务搭好后,用"京东买电脑"做了端到端验证。二维码登录只演示"人工介入通道"(正常的扫码/验证码场景),验证完即放弃该登录态。

流程

Agent: browser_goto('https://passport.jd.com/new/login.aspx')
Agent: 发现二维码 → browser_viewer() → 把查看台地址发给我
用户: 打开查看台 → 手机京东 App 扫码
Agent: browser_goto('www.jd.com') → 确认登录态保持
Agent: 搜索 → 解析 → 点详情页核实配置 → 比价

京东数据解析(新版 React DOM)

京东搜索页老选择器(#J_goodsList)全失效,实测有效方案:

  • 商品卡 DOM:[data-sku] 元素
  • 价格:卡片 innerText 用正则 ¥\s*(\d{3,5}(?:\.\d{1,2})?)(价格是多行文本,先 replace(/\s+/g,' ')
  • 懒加载:滚动到底触发,等 3-4 秒

一条 browser_eval 抓整页商品:

(() => {
  const items = Array.from(document.querySelectorAll('[data-sku]'));
  const out = items.map(li => {
    const t = li.innerText.replace(/\s+/g, ' ').trim();
    const m = t.match(/¥\s*(\d{3,5}(?:\.\d{1,2})?)/);
    const price = m ? parseFloat(m[1]) : 0;
    const name = t.split('¥')[0].replace(/^广告\s*/, '').trim().slice(0, 75);
    return {price, name};
  }).filter(x => x.price > 0);
  return JSON.stringify({count: out.length, items: out});
})()

京东搜索页:Agent 浏览器实时画面

防山寨(京东特有的坑)

京东搜"笔记本"前排混着大量山寨贴牌(“i9级”=洋垃圾、普惠之家/戴睿=杂牌)。过滤关键词:普惠 | i9级 | 二手 | 壹号本 | 戴睿 | Thinkpaid。区分三类店:京东自营 > 品牌官方授权店 > 山寨贴牌(绝不买)。

点详情页逐项核实(不按标题猜)

买电脑不能只看标题。逐个点进详情页,点「规格参数」读真实配置(CPU/显卡/内存/硬盘/屏幕/电池/到手价):

商品详情页规格参数(惠普星Book 15)

教训:第一次只看搜索结果标题就下结论被质疑——从此每个候选 SKU 都点详情页核实,拿不到的参数如实标"未确认",绝不编

验证结果(5000 元编程笔记本)

机型 核实配置 到手价 店铺
机械革命无界14X/SE 斗战版 Ultra5 226V / 16G / 512G / 2.8K高色域 ¥4249 机械革命旗舰店
红米 REDMI Book 16 焕新版 13代i5标压 / 16G / 1TB / 16" ¥3909 京东自营
惠普星Book 15 R7-7730U / 16G / 512G / 1080P 45%NTSC ¥4248 京东自营
联想小新Pro14 i5-12450H / 16G / 1TB / 1080P ¥4154 京东电竞官方旗舰店
ThinkBook 14+ 锐龙版 R7-8745HS / 16G / 512G ¥5399 旗鹭专卖店(第三方)

结论:5000 内编程性价比最高是机械革命无界14X 斗战版(2.8K 高色域 + 新酷睿 Ultra);预算紧选红米 Book 16 自营(1TB)。


六、京东访问的附加坑

  1. 移动端 ≠ PC 登录态item.m.jd.com 是独立登录态,PC 登录了移动端仍显示"请登录"——不是限流
  2. 京东风控:搜索太频繁软限流(页面只剩历史热词)。对策:页面加载等 6-8s、切换间隔 20-30s、触发后冷却 60-90s
  3. 无头浏览器详情页参数折叠:部分 SKU 参数表不渲染,用「已选配置」+ 标题组合拿核心数据

七、总结

这套方案的真正价值:把"能操作浏览器"变成了 MCP 工具,Agent 可以带着登录态完成真实网页任务,人机验证通过网页查看台人工介入,全程不依赖第三方平台。验证案例(京东)证明链路完整可用。

后续方向:会话持久化(容器重启后登录态保留)、多浏览器实例、更细的验证码识别能力。


本文所有工具均为自建环境实测(Hermes Agent + conhub MCP + 无头 Chromium)。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐