Playwright代理服务

一个为Playwright设计的MCP代理服务,提供持久化存储、会话管理和优化响应处理功能。

  • by noomz
  • Python
  • 本地部署
  • 更新于
在 GitHub 查看 noomz/playwright-mcp-proxy

使用说明

剧作家MCP代理

Playwright的模型上下文协议(MCP)代理,增加了持久存储、会话管理和优化的响应处理。

建筑

代理由两个基于UV的Python组件组成:

  1. MCP客户端 -向上游客户端公开Playwright工具的stdio MCP服务器
  2. HTTP服务器 -管理Playwright MCP子流程,处理会话,并将数据持久化到SQLite
MCP Client --> MCP Client --> HTTP Server --> Playwright MCP
(upstream)     (proxy)        (FastAPI)       (subprocess)
                                   |
                                   v
                                SQLite

特性

  • 持久存储:使用UUID引用存储在SQLite中的所有请求和响应
  • 会话管理:可以独立创建和管理的基于UUID的浏览器会话
  • 优化响应:返回元数据+ref_id,而不是完整的有效载荷
  • 内容检索:通过ref_id获取页面快照和控制台日志
  • 基于差异的内容 (第二阶段): get_content 仅返回自上次读取以来的更改
    • 基于哈希的变化检测
    • 跨服务器重启的游标持久性
    • reset_cursor 获取完整内容的参数
  • 会话恢复 (第7阶段):会话在服务器重启后仍然存在
    • 每30秒自动进行一次状态快照(URL、Cookie、本地存储、会话存储、视口)
    • 启动时孤立会话检测
    • 恢复会话并完全恢复状态
    • 分类:可恢复、过时或已关闭(基于快照年龄)
  • 子流程管理:Playwright MCP的自动健康监测和重启

安装

本地开发安装

# Install in editable mode with UV
uv pip install -e .

# Or install with dev dependencies
uv pip install -e ".[dev]"

全局安装(建议用于生产)

选项1:UV工具安装(推荐)

作为全局UV工具安装-命令将在系统范围内可用:

# Install from the current directory
uv tool install .

# Or install from a Git repository
uv tool install git+https://github.com/yourusername/playwright-mcp-proxy.git

# Commands are now available globally:
playwright-proxy-server
playwright-proxy-client

稍后更新:

uv tool upgrade playwright-mcp-proxy

要卸载,请执行以下操作:

uv tool uninstall playwright-mcp-proxy

选项2:全系统UV管道安装

# Install to UV's global Python environment
uv pip install --system .

快速开始

1.启动HTTP服务器

# Using the installed script
playwright-proxy-server

# Or using Python module
python -m playwright_mcp_proxy.server

服务器将:

  • 在以下位置初始化SQLite数据库 ./proxy.db
  • 生成剧作家MCP子流程
  • 倾听 http://localhost:34501

2.配置MCP客户端

添加到您的MCP客户端配置中(例如,Claude Desktop、VS Code):

如果全局安装(uv工具安装):

{
  "mcpServers": {
    "playwright-proxy": {
      "command": "playwright-proxy-client"
    }
  }
}

如果使用本地开发安装:

{
  "mcpServers": {
    "playwright-proxy": {
      "command": "uv",
      "args": [
        "run",
        "--directory",
        "/path/to/playwright-mcp-proxy",
        "playwright-proxy-client"
      ]
    }
  }
}

或者直接使用Python模块:

{
  "mcpServers": {
    "playwright-proxy": {
      "command": "python",
      "args": [
        "-m",
        "playwright_mcp_proxy.client"
      ]
    }
  }
}

3.从MCP客户端使用

# Create a new browser session
> create_new_session()
Created session: abc-123-def-456

# Navigate and take snapshot
> browser_navigate(url="https://example.com")
Request completed successfully
Ref ID: xyz-789
Page snapshot available. Use get_content('xyz-789')

# Get the page content
> get_content(ref_id="xyz-789")
[Full accessibility tree snapshot]

# Search within content
> get_content(ref_id="xyz-789", search_for="Example Domain")
- heading "Example Domain"

会话恢复(第7阶段)

会话现在可以在服务器重启后存活!代理每30秒自动捕获一次浏览器状态,并可以在崩溃或重新启动后恢复会话。

运作原理

  1. 自动快照:当会话处于活动状态时,服务器会捕获:

    • 当前URL
    • Cookie
    • 本地存储
    • 会话存储
    • 视口大小
  2. 启动检测:服务器重新启动时,它会自动:

    • 检测孤立会话(在关闭前标记为“活动”)
    • 根据快照年龄对它们进行分类:
      • 可恢复的:最近快照(<24h)-可以安全恢复
      • 陈腐的:旧快照(>24小时)-可能无法可靠工作
      • 关闭:没有可用的快照-无法恢复
  3. 手动简历:用户可以通过工具列出和恢复会话

用法示例

# Before restart - working in a session
> browser_navigate(url="https://example.com")
> browser_type(element="search box", ref="e1", text="test query")
# Server crashes or restarts...

# After restart - resume your session
> list_sessions(state="recoverable")
Session ID: abc-123-def-456
State: recoverable
URL: https://example.com
Snapshot age: 45 seconds

> resume_session(session_id="abc-123-def-456")
✓ Session resumed successfully
Restored URL: https://example.com
# Your browser is back at the same page with all state restored!

HTTP API

您还可以直接使用HTTP端点:

# List recoverable sessions
curl http://localhost:34501/sessions?state=recoverable

# Resume a session
curl -X POST http://localhost:34501/sessions/abc-123-def-456/resume

配置

  • SESSION_SNAPSHOT_INTERVAL:快照的频率(默认值:30秒)
  • MAX_SESSION_AGE:可恢复会话的最大期限(默认值:24小时)
  • MAX_SESSION_SNAPSHOTS:要保留多少个快照(默认值:10)
  • AUTO_REHYDRATE:启动时自动恢复会话(默认值:false)

局限性

  • 通过以下方式捕获的Cookie document.cookie (无httpOnly/secure标志)
  • 无法捕获JavaScript堆、正在运行的计时器或挂起的请求
  • 如果只恢复存储而不恢复完整上下文,某些站点可能会中断
  • 已捕获但当前未恢复的视口大小(剧作家限制)

配置

通过环境变量进行配置(前缀: PLAYWRIGHT_PROXY_):

# Server
PLAYWRIGHT_PROXY_SERVER_HOST=localhost
PLAYWRIGHT_PROXY_SERVER_PORT=34501

# Database
PLAYWRIGHT_PROXY_DATABASE_PATH=./proxy.db

# Playwright
PLAYWRIGHT_PROXY_PLAYWRIGHT_BROWSER=chrome  # chrome, firefox, webkit
PLAYWRIGHT_PROXY_PLAYWRIGHT_HEADLESS=false

# Subprocess Management
PLAYWRIGHT_PROXY_HEALTH_CHECK_INTERVAL=30
PLAYWRIGHT_PROXY_MAX_RESTART_ATTEMPTS=3
PLAYWRIGHT_PROXY_RESTART_WINDOW=300
PLAYWRIGHT_PROXY_SHUTDOWN_TIMEOUT=5

# Session Recovery (Phase 7)
PLAYWRIGHT_PROXY_SESSION_SNAPSHOT_INTERVAL=30    # Seconds between snapshots
PLAYWRIGHT_PROXY_MAX_SESSION_AGE=86400           # Max age (24h) for recoverable sessions
PLAYWRIGHT_PROXY_AUTO_REHYDRATE=false            # Auto-resume sessions on startup
PLAYWRIGHT_PROXY_MAX_SESSION_SNAPSHOTS=10        # Keep last N snapshots per session

# Logging
PLAYWRIGHT_PROXY_LOG_LEVEL=INFO

或者创建一个 .env 文件(参见 .env.example).

可用工具

会话管理

  • create_new_session() -创建新的浏览器会话,返回 session_id
  • list_sessions(state?) -列出所有会话,可选择按状态筛选(第7阶段)
  • resume_session(session_id) -重启后恢复可恢复/过时的会话(第7阶段)

内容检索

  • get_content(ref_id, search_for?, reset_cursor?) -从以前的请求中获取页面快照
    • 第2阶段:仅返回自上次读取以来的更改(基于哈希的差异)
    • 使用 reset_cursor=true 获取完整内容并重置差异跟踪
    • 空响应表示未检测到任何更改
  • get_console_content(ref_id, level?) -获取控制台日志(按debug/info/warn/error筛选)

剧作家工具(代理)

所有标准Playwright MCP工具均可用:

  • browser_navigate(url)
  • browser_snapshot()
  • browser_click(element, ref)
  • browser_type(element, ref, text, submit?)
  • browser_console_messages(onlyErrors?)
  • browser_close()
  • …以及更多

响应返回元数据+ref_id,而不是完整内容。

数据库模式

  • 会话 -浏览器会话(UUID、状态、时间戳、恢复字段)
  • 请求: -所有代理请求(ref_id、工具、参数)
  • 回应 -作为blob的完整响应(结果、页面快照、控制台日志)
  • console_logs -标准化控制台条目(级别、消息、位置)
  • 差异 -用于第2阶段差异支持
  • 会话快照 -第7阶段状态快照(URL、Cookie、存储、视口)

性能比较:代理与直接编剧MCP

用以下方式测量 uv run pytest tests/test_comparison.py -v -s -m integration --将代理与直接Playwright MCP子流程通信进行比较的7种场景。

简单导航(example.com)

操作 路径 延迟(毫秒) 有效负载(字节) 估计值。代币
导航 代理 67 301 -
快照(元数据) 代理 6 300 75
get_content 代理 2 440 102
导航 直接 1711 - -
快照(完整) 直接 3 787 181

代理元数据响应:300B,而直接完整快照:787B

Diff抑制(重复读取)

操作 路径 延迟(毫秒) 有效负载(字节) 估计值。代币
第一次读取(完整) 代理 2 440 102
第二次读取(diff=空) 代理 2 14 1
第三次读取(重置=完全) 代理 2 440 102
第一张快照 直接 3 469 102
第二张快照 直接 5 787 181
第三张快照 直接 3 502 110

3读代币节省:节省188个代币(48%) --代理205 vs直接393

多页导航

操作 路径 延迟(毫秒) 有效负载(字节) 估计值。代币
导航(example.com) 代理 303 - -
快照(example.com) 代理 1013 300 75
get_content(example.com) 代理 4 440 102
导航(www.iana.org) 代理 1307 - -
快照(www.iana.org) 代理 1021 300 75
get_content(www.iana.org) 代理 3 7274 1749
get_content(第1页重播) 代理 3 411 102
导航(example.com) 直接 1508 - -
快照(example.com) 直接 3 469 102
导航(www.iana.org) 直接 6434 - -
快照(www.iana.org) 直接 7 7024 1677

代理持久性:导航到第2页后检索到第1页内容(3ms) Direct没有持久性——导航后前一页内容消失

内容搜索筛选

操作 路径 延迟(毫秒) 有效负载(字节) 估计值。代币
完整内容 代理 2 440 102
已筛选(search_for) 代理 5 97 19
快照(已满,无筛选器) 直接 4 469 102

search_for减少:有效负载减少81%,节省约83个令牌 直接MCP没有搜索过滤——总是返回完整的可访问性树

错误处理

操作 路径 延迟(毫秒) 有效负载(字节)
无效的URL导航 代理 623 283
无效的URL导航 直接 1106 -

两条路径都能优雅地处理错误,而不会崩溃

复杂页面:谷歌搜索

操作 路径 延迟(毫秒) 有效负载(字节) 估计值。代币
导航(google.com) 代理 1449 - -
类型搜索查询 代理 3064 - -
按Enter 代理 1179 - -
快照结果(元数据) 代理 2043 300 75
get_content(第一次读取) 代理 3 78262 17676
get_content(第二次读取,差异) 代理 2 14 1
get_content(search_for) 代理 3 13711 3285
导航(google.com) 直接 1877 - -
类型搜索查询 直接 2547 - -
按Enter键 直接 2198 - -
快照结果(完整) 直接 15 3181 772
快照(第二,再次完整) 直接 6 3181 772

搜索“剧作家”减少:81% --3285个代币vs 17676个完整代币

复杂页面:YouTube搜索

操作 路径 延迟(毫秒) 有效负载(字节) 估计值。代币
导航(youtube.com) 代理 1618 - -
快照主页 代理 79 2502 625
类型搜索查询 代理 32 - -
按Enter 代理 1047 - -
快照结果(元数据) 代理 22 300 75
get_content(第一次读取) 代理 3 2929 703
get_content(第二次读取,差异) 代理 2 14 1
get_content(search_for) 代理 3 175 40
导航(youtube.com) 直接 2201 - -
快照主页 直接 14 1956 460
类型搜索查询 直接 26 - -
按Enter键 直接键 1019 - -
快照结果(完整) 直接 15 1956 460
快照(第二张,再次完整) 直接 13 1956 460

2读差异节省:216个代币(23%) --代理704与直接920 整页:703个标记|筛选的“剧作家”:40个标记|抑制差异:1个标记

Chrome扩展路径(Chrome中的claude)

通过以下方式测量 mcp__claude-in-chrome__* 针对具有热缓存的实时Chrome浏览器的工具。这些代表了第三种自动化路径——使用现有的Chrome实例,而不是生成无头浏览器。

简单导航(example.com)

操作 路径 延迟(毫秒) 有效负载(字节) 估计值。代币
导航 代理 67 301 -
get_content 代理 2 440 102
导航 直接 1711 - -
快照(完整) 直接 3 787 181
导航 chrome 403 - -
read_page chrome 3200 213 53
get_page_text chrome 3400 183 46

Chrome导航速度很快(热缓存),但read_page的扩展往返开销约为3s

谷歌搜索

操作 路径 延迟(毫秒) 有效负载(字节) 估计值。代币
导航(主页) 代理 1449 - -
get_content(第一次读取) 代理 3 78262 17676
get_content(第二次读取,差异) 代理 2 14 1
快照结果(完整) 直接 15 3181 772
导航(主页) chrome 1375 - -
read_page(主页) chrome 1400 1800 450
导航(搜索结果) chrome 3921 - -
read_page(搜索结果) chrome 15800 12000 3000

谷歌搜索结果上的Chrome read_page:15.8s/12KB,由于深度为2的复杂DOM

YouTube搜索

操作 路径 延迟(毫秒) 有效负载(字节) 估计值。代币
get_content(第一次读取) 代理 3 2929 703
get_content(第二次读取,差异) 代理 2 14 1
快照结果(完整) 直接 15 1956 460
导航(主页) chrome 3708 - -
read_page(主页) chrome 2200 11000 2750
导航(搜索结果) chrome 1246 - -
read_page(搜索结果) chrome 1300 10000 2500

YouTube SPA路由使后续导航速度更快(1.2秒);chrome read_page有效载荷很大(10-11KB)

三路路径比较总结

路径 导航延迟 内容延迟 令牌效率 持久性
代理 中等(67-1449ms) 快速(通过ref_id为2-5ms) 最佳(差异:重复1个标记) 完整的SQLite历史记录
直接的 慢速(1508-6434ms) 快速(3-15ms) 中等(每次满载) 无
铬 快速中等(403-3921ms) 慢速(1300-15800ms) 可变(53-3000个令牌) 无

代理在代币效率上获胜 --差异抑制和搜索过滤将令牌减少了48-83%。 Chrome在导航速度上获胜 --温缓存避免了浏览器冷启动。 内容检索速度上的直接胜利 --无需延长往返开销。

主要代理优势

特性 描述
仅元数据响应 初始工具调用返回ref_id,而不是完整内容——小而固定大小的有效载荷
差异抑制 重复读取在内容不变时返回空值——节省大量代币
search_for筛选 仅检索匹配的行--减少目标查找的负载
持久性(SQLite) 存储的所有快照--通过ref_id检索任何历史页面
会话管理 具有生命周期跟踪和错误审计跟踪的命名会话

运行比较测试

# Start the proxy server first
uv run playwright-proxy-server

# Run the proxy vs direct comparison suite (requires internet access)
uv run pytest tests/test_comparison.py -v -s -m integration

# Run the chrome path comparison report (uses pre-recorded measurements)
uv run pytest tests/test_chrome_comparison.py -v -s -m chrome_comparison

发展

运行测试

# Run all tests
uv run pytest

# Run with coverage
uv run pytest --cov=playwright_mcp_proxy

# Run specific test file
uv run pytest tests/test_database.py

代码格式化

# Check formatting
uv run ruff check .

# Auto-fix issues
uv run ruff check --fix .

手动测试

# Terminal 1: Start server
uv run playwright-proxy-server

# Terminal 2: Start client (stdio)
uv run playwright-proxy-client

# Terminal 3: Send MCP requests via stdio
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' | uv run playwright-proxy-client

路线图

第一阶段(完成)

  • [x] 核心基础设施
  • [x] SQLite持久性
  • [x] 具有会话管理的HTTP服务器
  • [x] 带工具代理的MCP客户端
  • [x] 子流程生命周期管理
  • [x] 基础测试(6次测试)

第2阶段(完成)

  • [x] 基于Diff的内容检索
  • [x] get_content 仅返回自上次读取以来的更改
  • [x] reset_cursor 参数
  • [x] 基于哈希的变化检测
  • [x] 跨服务器重启的游标持久性
  • [x] 综合测试(8项附加测试)

第7阶段(完成)

  • [x] 会话状态持久性(URL、Cookie、本地存储、会话存储、视口)
  • [x] 每30秒自动定期快照
  • [x] 启动时孤立会话检测
  • [x] 会话分类(可恢复/过时/已关闭)
  • [x] 通过会话补液重新启动恢复
  • [x] list_sessions(state) 工具
  • [x] resume_session(session_id) 工具
  • [x] 综合测试(共16项测试)

第8阶段(完成)

  • [x] Chrome扩展路径(claude in Chrome)测量基础设施
  • [x] 3个场景的预先记录测量值(例如.com、谷歌搜索、YouTube搜索)
  • [x] 三向性能比较表(代理、直接和chrome)
  • [x] chrome_comparison pytest标记用于选择测试

故障排除

服务器无法启动

  • 检查端口34501是否可用
  • 验证是否安装了Node.js(适用于Playwright MCP)
  • 检查控制台输出中的日志

子进程不断重新启动

  • 检查 ~/.npm 有 @playwright/mcp@latest 安装
  • 验证浏览器二进制文件是否已安装
  • 查看stderr日志

MCP客户端无法连接

  • 确保HTTP服务器在本地主机34501上运行
  • 检查防火墙设置
  • 验证MCP客户端中的配置

许可证

麻省理工学院

贡献

欢迎投稿!拜托:

  1. 遵循现有的代码样式(Ruff)
  2. 为新功能添加测试
  3. 更新文档
  4. 确保向后兼容性

贡献领域:

  • 会话恢复的集成测试
  • 性能优化
  • 额外的浏览器状态捕获(iframe支持、web workers等)
  • 强化补液策略
  • Bug修复和改进

看 CLAUDE.md 了解架构细节和开发模式。