CSDN-Playwright-MCP集成Hermes踩坑实录
title: 将Playwright MCP集成到Hermes Agent:完整踩坑实录与23个工具全功能验证
date: 2026-05-05
categories: [AI, 工具链]
tags: [Playwright, MCP, Hermes, 浏览器自动化, AI Agent]
将Playwright MCP集成到Hermes Agent:完整踩坑实录与23个工具全功能验证
前言
最近在折腾 Hermes Agent(一个 CLI 端的 AI 代理框架),想要给它加上浏览器自动化能力。最自然的选择就是 Playwright MCP——通过 MCP(Model Context Protocol)协议,让 AI 代理能直接操控浏览器。
这篇文章记录了从零开始集成 @playwright/mcp 的全过程:环境搭建、踩过的坑、以及最终的 23 个工具全功能验证。希望能给同样在折腾 MCP + 浏览器的同学一些参考。
什么是 Playwright MCP?
Playwright 是微软出品的浏览器自动化工具,支持 Chromium、Firefox、WebKit 三引擎。MCP(Model Context Protocol)是 Anthropic 推出的一种让 AI 模型与外部工具交互的开放协议。
@playwright/mcp 就是两者的结合——一个 MCP 服务器,将 Playwright 的浏览器操控能力封装成 23 个标准工具,AI 代理可以通过 MCP 协议直接调用。
环境概览
- 操作系统: WSL2(Windows Subsystem for Linux)
- Hermes Agent: 最新版
- Playwright MCP: @playwright/mcp@latest
- Chromium: Chrome for Testing v148(revision 1222)
- Node.js: 通过 npx 运行时自动管理
第一步:下载 Chromium
Playwright 需要特定版本的 Chromium,不是日常用的 Chrome 浏览器。用的是 Google 的 Chrome for Testing,需要从 Google CDN 手动下载。
下载链接格式:
https://storage.googleapis.com/chrome-for-testing-public/{版本号}/linux64/chrome-linux64.zip
踩坑记录:直接通过 npm / npx 自动下载极其缓慢,国内网络经常中断。建议手动下载 zip 包后解压。
# 创建 Playwright 期望的目录结构
mkdir -p ~/.cache/ms-playwright/chromium-1222/
# 解压已下载的 zip 包
python3 -c "
import zipfile, os
zip_path = '/mnt/d/cc/Downloads/chrome-linux64.zip'
target = os.path.expanduser('~/.cache/ms-playwright/chromium-1222')
with zipfile.ZipFile(zip_path, 'r') as zf:
zf.extractall(target)
os.chmod(f'{target}/chrome-linux64/chrome', 0o755)
"
第二步:安装系统依赖
Chromium 在 Linux 下需要一堆系统库,缺一个就启动失败。好在 apt 能解决:
sudo apt-get install -y libnspr4 libnss3 libatk1.0-0t64 libatk-bridge2.0-0t64 \
libcups2t64 libdrm2 libdbus-1-3 libxkbcommon0 libxcomposite1 libxdamage1 \
libxfixes3 libxrandr2 libgbm1 libpango-1.0-0 libcairo2 libasound2t64
第三步:配置符号链接
@playwright/mcp 默认查找 /opt/google/chrome/chrome 作为浏览器路径。我们可以创建一个符号链接指向手动下载的 Chromium:
sudo mkdir -p /opt/google/chrome
sudo ln -sf ~/.cache/ms-playwright/chromium-1222/chrome-linux64/chrome /opt/google/chrome/chrome
这样就不用每次启动都传 --executable-path 参数了。当然,也可以直接传参覆盖,灵活选择。
第四步:编写包装脚本
这里有一个关键问题:@playwright/mcp 作为 MCP 服务器,需要长时间运行在后端。但直接运行 npx 命令会有几个问题:
- 多实例冲突:多次启动会导致多个 MCP 服务器进程
- crash 后不会自动重启:进程意外退出后需要手动拉起
- 超时配置:默认超时对复杂页面不够用
所以写了一个包装脚本来解决这些问题:
#!/bin/bash
# playwright-mcp-wrapper.sh
set -euo pipefail
NAME="playwright-mcp"
LOCK_DIR="/tmp/${NAME}.lock"
CHROMIUM_PATH="/home/cc/.cache/ms-playwright/chromium-1222/chrome-linux64/chrome"
MAX_RESTARTS=5
RESTART_DELAY=2
# PID 文件锁定——防止多实例冲突
if ! mkdir "$LOCK_DIR" 2>/dev/null; then
EXISTING_PID=$(cat "$LOCK_DIR/pid" 2>/dev/null || echo "unknown")
echo "[${NAME}] ERROR: Already running (pid $EXISTING_PID). Exiting." >&2
exit 1
fi
echo $$ > "$LOCK_DIR/pid"
cleanup() { rm -rf "$LOCK_DIR"; }
trap cleanup EXIT
# 子进程启动函数
start_server() {
exec npx -y "@playwright/mcp@latest" \
--headless \
--no-sandbox \
--executable-path "$CHROMIUM_PATH" \
--timeout-action 10000 \
--timeout-navigation 120000 \
--output-mode stdout \
--isolated \
"$@"
}
# Chrome 就绪检查
if [ ! -x "$CHROMIUM_PATH" ]; then
echo "[${NAME}] ERROR: Chromium not found at $CHROMIUM_PATH" >&2
exit 1
fi
# 守护循环——crash后最多自动重启5次
RESTART_COUNT=0
while true; do
echo "[${NAME}] Starting (attempt $((RESTART_COUNT + 1)))..." >&2
start_server || true
EXIT_CODE=$?
# 正常退出不重启
if [ $EXIT_CODE -eq 0 ] || [ $EXIT_CODE -eq 130 ] || [ $EXIT_CODE -eq 143 ]; then
exit 0
fi
RESTART_COUNT=$((RESTART_COUNT + 1))
if [ "$RESTART_COUNT" -ge "$MAX_RESTARTS" ]; then
echo "[${NAME}] Too many restarts, giving up." >&2
exit 1
fi
sleep "$RESTART_DELAY"
done
这个脚本的核心设计:
- PID 文件锁:通过
mkdir的原子性保证只有一个实例在运行 - exec 替代 fork:子进程直接替换父进程,避免僵尸进程
- 守护循环:非正常退出时自动重启,最多 5 次
- trap cleanup:退出时自动清理锁文件
第五步:配置 Hermes
将 Playwright MCP 添加到 Hermes 的配置文件 ~/.hermes/config.yaml:
mcp_servers:
playwright:
command: /home/cc/.local/bin/playwright-mcp-wrapper.sh
args: []
timeout: 120
connect_timeout: 60
这里有个小坑:timeout 默认值 30 秒对于浏览器操作来说太短了,导航复杂页面很容易超时。我把 timeout 调到了 120 秒,connect_timeout 设到 60 秒。
第六步:启动与连接测试
# 启动包装脚本
bash ~/.local/bin/playwright-mcp-wrapper.sh &
# 测试连接
hermes mcp test playwright
成功输出:
Testing 'playwright'...
Transport: stdio → /home/cc/.local/bin/playwright-mcp-wrapper.sh
✓ Connected (2911ms)
✓ Tools discovered: 23
23 个工具全部发现!比预想的多。具体包括:
| 类别 | 工具列表 |
|---|---|
| 导航 | browser_navigate, browser_navigate_back |
| DOM | browser_snapshot |
| 截图 | browser_take_screenshot |
| 点击交互 | browser_click, browser_hover, browser_drag |
| 输入 | browser_type, browser_fill_form, browser_select_option, browser_press_key |
| JS执行 | browser_evaluate, browser_run_code_unsafe |
| 网络 | browser_network_requests, browser_network_request |
| 控制台 | browser_console_messages |
| 对话框 | browser_handle_dialog |
| 文件 | browser_file_upload, browser_drop |
| 标签页 | browser_tabs |
| 窗口 | browser_resize |
| 等待 | browser_wait_for |
| 关闭 | browser_close |
踩坑记录
坑1:参数名与直觉不符(最坑)
这是最大的坑。@playwright/mcp 的某些工具参数名和直观理解不一样,用错了就报 validation error。
browser_evaluate:
# ❌ 错误——参数是 expression?不是!
session.call_tool("browser_evaluate", {"expression": "document.title"})
# ✅ 正确——参数是 function,必须传箭头函数
session.call_tool("browser_evaluate", {"function": "() => { return document.title; }"})
browser_run_code_unsafe:
# ❌ 错误——普通函数不行
session.call_tool("browser_run_code_unsafe", {"code": "return page.title()"})
# ✅ 正确——必须传 async 函数,接收 page 参数
session.call_tool("browser_run_code_unsafe", {
"code": "async (page) => { return await page.title(); }"
})
browser_tabs:
# ❌ 错误——没有 "switch" 这个 action
session.call_tool("browser_tabs", {"action": "switch", "index": 0})
# ✅ 正确——用 "select"
session.call_tool("browser_tabs", {"action": "select", "index": 0})
browser_network_request:
# ❌ 错误——index 不是 0-based
session.call_tool("browser_network_request", {"index": 0})
# ✅ 正确——1-based
session.call_tool("browser_network_request", {"index": 1})
这类错误的调试方法是直接用 list_tools() 打印每个工具的完整 inputSchema:
tools = await session.list_tools()
for tool in tools.tools:
print(tool.name, json.dumps(tool.inputSchema, indent=2))
坑2:文件上传需要模态状态
browser_file_upload 这个工具必须在文件选择器弹出后才能使用,不能像 Playwright 原生 API 那样直接 set_input_files()。
# ❌ 直接调用报错:"can only be used when there is related modal state present"
session.call_tool("browser_file_upload", {"paths": ["/tmp/file.txt"]})
# 需要用 run_code_unsafe 替代
session.call_tool("browser_run_code_unsafe", {
"code": """async (page) => {
const fi = page.locator('input[type=file]');
const [fileChooser] = await Promise.all([
page.waitForEvent('filechooser'),
fi.click()
]);
await fileChooser.setFiles('/tmp/file.txt');
return 'done';
}"""
})
而且上传后如果状态没清理,后续工具调用会报 “does not handle the modal state”,需要通过关闭页面或重新导航来重置。
坑3:选择器 strict mode
使用 text= 选择器时,如果匹配到多个元素会报 strict mode violation:
Error: strict mode violation: locator('text=暴力破解') resolved to 2 elements
解决方案:用更精确的选择器,如 role=link[name="精准文本"] 或直接在 snapshot 中取 ref。
另外,snapshot 中的 @eN ref 编号只在当次 snapshot 有效,跨操作使用会报 “Ref not found”。
坑4:example.com 页面已更新
测试时发现 example.com 的链接文本已经从 “More information…” 变成了 “Learn more”。这么经典的页面也会悄无声息地改版,写测试用例的时候要注意。
坑5:连接稳定性
长时间运行或连续快速调用后,MCP 连接可能进入 unreachable 状态。恢复方法:
hermes mcp test playwright
如果还不行,需要先杀掉旧进程、清理 PID 锁文件,再重新启动:
ps aux | grep playwright-mcp | grep -v grep | awk '{print $2}' | xargs kill
rm -rf /tmp/playwright-mcp.lock
bash ~/.local/bin/playwright-mcp-wrapper.sh &
hermes mcp test playwright
全功能测试结果
最终用 Python MCP 客户端写了一个完整的测试脚本,对全部 23 个工具做了 40 项功能测试。结果:
📊 测试总结
总测试项: 40
✅ 通过: 39
❌ 失败: 1
通过率: 97%
唯一失败的是 browser_file_upload 的模态状态问题(坑2),属于工具的设计限制,有明确的替代方案。
测试覆盖的功能点
| 功能 | 测试内容 | 结果 |
|---|---|---|
| 页面导航 | example.com + 多级子页面 | ✅ |
| 无障碍树 | 获取YAML格式快照(含ref) | ✅ |
| 全页截图 | fullPage模式, 876KB | ✅ |
| 元素点击 | 文本选择器点击链接 | ✅ |
| 页面后退 | navigate_back | ✅ |
| 窗口缩放 | 800×600 / 375×667 / 1920×1080 | ✅ |
| JS执行 | document.title / 链接统计 / Cookie / localStorage | ✅ |
| 输入框 | 目标输入文本 | ✅ |
| 表单填充 | 多字段填充 | ✅ |
| 下拉选择 | select option values | ✅ |
| 键盘事件 | Tab / Enter | ✅ |
| 元素悬停 | hover交互 | ✅ |
| 控制台 | info / error 级别过滤 | ✅ |
| 网络请求 | 列表 + 单请求详情(状态码/headers) | ✅ |
| 多标签 | 新建 / 列表 / 选中 / 关闭 | ✅ |
| 等待机制 | 文本等待 / 时间等待 | ✅ |
| 拖放 | 元素间拖放 | ✅ |
| 对话框 | alert自动接受 | ✅ |
| MIME拖放 | data对象拖放 | ✅ |
| 代码注入 | page.title / locator.count / 多步操作 / 截图 | ✅ |
| 页面关闭 | 资源释放 | ✅ |
最终配置清单
总结一下,一个可用的 Playwright MCP 集成需要以下组件:
~/.cache/ms-playwright/chromium-1222/chrome-linux64/chrome ← Chromium二进制 (276MB)
/opt/google/chrome/chrome → .../chrome-linux64/chrome ← 符号链接
~/.local/bin/playwright-mcp-wrapper.sh ← 包装脚本(含PID锁+守护)
~/.hermes/config.yaml → mcp_servers.playwright ← Hermes配置
启动流程:
bash ~/.local/bin/playwright-mcp-wrapper.sh & # 后台启动
hermes mcp test playwright # 验证连接
结语
@playwright/mcp 为 AI Agent 提供了完整的浏览器操控能力,23 个工具覆盖了从导航、交互到网络分析的全部需求。虽然有一些小坑(参数名、文件上传状态),但核心功能稳定可用,是 AI 自动化流程中浏览器能力的优秀选择。
最大的感受:MCP 协议让工具与 AI 模型的解耦变得简单——不需要为每个 AI 框架单独适配,一次集成,到处可用。
如果你也在折腾 MCP 相关的集成,欢迎交流。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)