Playwright MCP Server 实战:让 Agent 拥有浏览器能力

系统讲解 Playwright MCP Server 的三种部署模式(本地 / 远程 / 云端)、browser_snapshot 无障碍树机制、工具调用模式、安全边界(URL 白名单 / 操作审计 / 数据外泄检测)与典型应用场景。

AgentList · 2026年7月1日
MCPPlaywrightBrowser AgentWeb Automationbrowser-automation

2024-2025 年,Model Context Protocol(MCP)成为 Agent 工具调用的"事实标准"协议。在所有 MCP Server 中,Playwright 是落地最广、价值最高的之一——它让 LLM Agent 第一次获得了"打开浏览器、操作页面、提取信息"的通用能力。本文从工程实战出发,系统讲解 Playwright MCP 的部署模式、调用模式、安全边界和典型应用场景。

为什么 Browser 是 Agent 的"超级工具"

LLM Agent 解决"信息获取"问题的方式有三种:API、文件、网页。API 受限于服务方提供什么;文件受限于你能拿到什么;只有网页是"开放互联网"——任何公开信息理论上都可通过浏览器访问。

典型场景

  • 客服 Agent:根据用户订单号自动查询物流
  • 数据采集 Agent:从多个 SaaS 平台抓取数据汇总
  • 自动化测试 Agent:验证 UI 流程
  • 竞品分析 Agent:自动抓取竞品页面变化

但浏览器操作对 LLM 来说有三大挑战:

  1. 状态化:网页是 SPA、JS 渲染的,传统的 HTTP 抓取拿不到内容
  2. 多步性:登录、表单填写、点击、滚动——往往是 5-20 步的链式操作
  3. 动态结构:同一个网站的不同页面 DOM 结构不同,无法硬编码

Playwright MCP 把这些复杂性封装成一个 MCP Server,让 Agent 通过简单的工具调用就能操作浏览器。

Playwright MCP 部署模式

Playwright MCP Server 有三种部署模式:

模式 1:本地进程(Local Process) Agent 进程和 Playwright 进程在同一台机器,通过 stdio 通信。

// Claude Desktop / Cursor config
{
  "mcpServers": {
    "playwright": {
      "command": "npx",
      "args": ["-y", "@microsoft/playwright-mcp"]
    }
  }
}

优点:最简单,无需服务器 缺点:浏览器实例与 Agent 绑定,无法共享 适合:个人开发、单一用户

模式 2:远程服务(Remote Server) Playwright 部署在远程机器,Agent 通过 HTTP/stdio 连接。

# 在远程机器上启动
playwright-mcp --port 8080 --headless
// Agent 通过 SSE 连接
{
  "mcpServers": {
    "playwright": {
      "url": "http://playwright.internal:8080/sse",
      "headers": { "Authorization": "Bearer xxx" }
    }
  }
}

优点:浏览器实例可被多个 Agent 共享、支持远程访问 缺点:需要部署、需要认证、需要考虑网络延迟 适合:团队部署、生产环境

模式 3:云端 Browser(Cloud Browser) Playwright 连接到 Browserless / Steel / Browserbase 等云端浏览器服务。

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.connect("wss://chrome.browserless.io?token=xxx")
    page = browser.new_page()
    page.goto("https://example.com")

优点:免维护、抗检测 缺点:成本高 适合:大规模数据采集、需要高匿名的场景

Playwright MCP 工具集

Playwright MCP 默认提供以下工具:

工具名 作用 关键参数
browser_navigate 导航到 URL url
browser_snapshot 获取页面无障碍快照 -
browser_click 点击元素 element, ref
browser_type 在输入框输入文本 element, ref, text
browser_hover 悬停元素 element, ref
browser_select_option 选择下拉项 element, ref, values
browser_screenshot 截屏 fullPage
browser_evaluate 执行 JS function
browser_wait_for 等待条件 text, time
browser_console_messages 获取 console onlyErrors
browser_close 关闭浏览器 -

典型调用序列(登录场景):

# Agent 的工具调用序列
1. browser_navigate(url="https://app.example.com/login")
2. browser_snapshot()  → 返回 page snapshot
3. browser_type(element="email input", ref="e15", text="user@example.com")
4. browser_type(element="password input", ref="e18", text="xxx")
5. browser_click(element="login button", ref="e22")
6. browser_wait_for(text="Dashboard")
7. browser_snapshot()  → 登录后页面 snapshot

browser_snapshot:核心机制

browser_snapshotPlaywright MCP 与传统浏览器自动化的关键差异。它返回**无障碍树(Accessibility Tree)**而非完整 DOM:

# browser_snapshot 返回格式示例
- role: "heading"
  name: "Sign in to your account"
  level: 2
- role: "textbox"
  name: "Email address"
  ref: "e15"
- role: "textbox"
  name: "Password"
  ref: "e18"
  type: "password"
- role: "button"
  name: "Sign in"
  ref: "e22"
- role: "link"
  name: "Forgot password?"
  ref: "e25"

无障碍树 vs 完整 DOM

  • 无障碍树只包含语义化元素(heading、button、input),过滤掉 div/span 等装饰元素
  • token 数比完整 DOM 少 10-100 倍
  • LLM 处理效率高
  • 包含稳定的 ref 标识符,Agent 通过 ref 操作元素

关键设计:无障碍树依赖页面的 ARIA 标签和语义化 HTML。如果网站是 div+CSS 堆出来的,没有 ARIA,无障碍树会很稀疏,Agent 操作能力受限

实际案例:物流查询 Agent

构建一个"输入订单号,返回物流信息"的 Agent:

from mcp import Client

async def query_logistics(order_id: str) -> dict:
    client = Client("playwright-mcp-server")
    
    # 1. 打开物流查询页面
    await client.call_tool("browser_navigate", {
        "url": "https://logistics.example.com/track"
    })
    
    # 2. 等待输入框出现
    await client.call_tool("browser_wait_for", {
        "text": "Enter order number"
    })
    
    # 3. 获取页面快照
    snapshot = await client.call_tool("browser_snapshot", {})
    input_ref = find_input_ref(snapshot, "Order number")
    
    # 4. 输入订单号
    await client.call_tool("browser_type", {
        "element": "Order number input",
        "ref": input_ref,
        "text": order_id
    })
    
    # 5. 点击查询
    snapshot = await client.call_tool("browser_snapshot", {})
    button_ref = find_button_ref(snapshot, "Track")
    await client.call_tool("browser_click", {
        "element": "Track button",
        "ref": button_ref
    })
    
    # 6. 等待结果显示
    await client.call_tool("browser_wait_for", {
        "text": "Current status"
    })
    
    # 7. 提取结果
    snapshot = await client.call_tool("browser_snapshot", {})
    result = parse_logistics_result(snapshot)
    
    await client.call_tool("browser_close", {})
    return result

性能优化

  • 复用 browser 实例(不每次打开新浏览器)
  • 缓存无障碍树(短时间内 DOM 不变就不重读)
  • 并行执行无依赖的工具调用

安全边界

Browser 是 LLM Agent 最危险的能力之一——一旦被注入,可能造成数据泄露、恶意操作。必须严格设计安全边界:

第一层:URL 白名单

ALLOWED_DOMAINS = ["example.com", "internal.example.com"]

def navigate(url: str):
    parsed = urlparse(url)
    if parsed.netloc not in ALLOWED_DOMAINS:
        raise SecurityError(f"Domain not allowed: {parsed.netloc}")
    return browser_navigate(url)

第二层:操作审计

class AuditLogger:
    def log(self, action: dict):
        if action["type"] == "browser_click":
            self.send_to_audit({
                "action": "browser_click",
                "url": action["url"],
                "element": action["element"],
                "user": current_user,
                "timestamp": now(),
            })

第三层:敏感操作二次确认

SENSITIVE_ACTIONS = {"browser_evaluate", "browser_navigate"}  # JS 执行、跨域导航

def require_approval(action: dict) -> bool:
    if action["type"] in SENSITIVE_ACTIONS:
        return user_confirm_prompt(
            f"Agent wants to {action['type']} {action.get('url', '')}"
        )
    return True

第四层:数据外泄检测

def detect_data_exfiltration(text: str) -> bool:
    # 检测 email、API key、SSN 等敏感模式
    patterns = [
        r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b",
        r"sk-[A-Za-z0-9]{20,}",
        r"\b\d{3}-\d{2}-\d{4}\b",  # SSN
    ]
    for pattern in patterns:
        if re.search(pattern, text):
            return True
    return False

性能基准

操作 延迟
启动浏览器 2-5s
打开新页面 0.5-2s
browser_navigate 0.3-1s
browser_snapshot 0.1-0.5s
browser_click 0.1-0.3s
browser_screenshot 0.5-2s
整个登录流程 5-15s
完整数据采集流程 30-120s

优化方向

  • 页面预热:常用页面预先加载
  • 快照缓存:同一页面的 snapshot 短时间内不重读
  • 浏览器池:多个 browser 实例并行处理
  • 失败重试:网络失败时自动重试 1-2 次

失败模式

失败模式 表现 处理
元素 ref 失效 点击无反应 重新 snapshot 取最新 ref
页面加载超时 browser_wait_for 超时 调整超时 + 重试
反爬虫拦截 页面返回 CAPTCHA 接 2Captcha / 切换代理
登录态失效 跳到登录页 重新走登录流程
元素被遮挡 点击被覆盖 滚动 + 等待 + 重试
网站改版 snapshot 结构变化 重新设计 selector 策略

实施路径

第 1 周:本地部署 Playwright MCP,配置到 Claude Desktop / Cursor。第 2 周:选 1-2 个内部网站做 PoC,验证基本工具调用。第 3 周:实施安全边界(URL 白名单、操作审计、敏感操作二次确认)。第 4 周:构建可复用的工具调用模板(登录、查询、采集)。第 5 周:部署到生产环境,建立监控和日志。第 6 周:建立"网站改版应对预案"(snapshot diff 检测、regression 测试)。

总结

Playwright MCP 把"打开浏览器、操作页面"封装成一个简单的 MCP Server,让 LLM Agent 第一次拥有访问开放互联网的能力。这是 2024-2025 年最实用的 Agent 能力扩展之一,应用场景覆盖客服、数据采集、自动化测试、竞品分析。

但 Browser 是 LLM Agent 最危险的能力,必须严格设计 URL 白名单、操作审计、敏感操作确认、数据外泄检测等安全机制。无障碍树的引入让 LLM 操作浏览器变得可行,但依赖网站的 ARIA 标签——对 div+CSS 堆出来的网站仍然有限制。

参考工具:Microsoft Playwright MCP(微软官方的 Playwright MCP Server)、Browser-Use(用 LLM 直接控制浏览器的 Python 库)、Playwright MCP Python(Python 实现的 Playwright MCP)、executeautomation MCP Playwright(另一个 Playwright MCP 实现)和 Notion MCP Server(另一个高价值 MCP Server,可作对比)覆盖了 Playwright MCP 的核心节点。