Skip to main content
Glama
Browser Pool MCP
by hy-2005

🖥️ Browser Pool MCP

Dynamic browser pool MCP server — every agent gets its own isolated browser.

License: MIT Node.js MCP

English | 中文文档

A Model Context Protocol server that wraps Microsoft's @playwright/mcp and turns it into a dynamic browser pool: each unique caller gets its own isolated Chrome instance, so multiple Claude Code sessions, subagents, or any MCP clients can run browser automation concurrently without conflicts.

✨ Features

  • 🧩 Per-agent isolation — each unique _agent_id gets its own browser. No more "Browser is already in use" errors when multiple agents hit the browser at once.

  • 🚀 On-demand spawning — browsers are lazily started on first use (local CLI, ~1s), never pre-allocated.

  • 🧹 Auto-cleanup — idle instances are killed after 30 min; orphaned instances from crashed wrappers are cleaned on startup.

  • 🛡️ Never kills busy agents — when the pool is full, the wrapper evicts only idle instances; if all are busy it waits, so a running agent's browser is never yanked away.

  • 🔁 Backward compatible — calling tools without _agent_id shares one "default" browser, preserving original single-session behavior.

  • 🌐 Full Playwright toolset — all @playwright/mcp tools (navigate, click, type, screenshot, evaluate, tabs, network…), no feature loss.

Related MCP server: hanzi-browse

🧠 Architecture

Claude Code main session ──stdio──> playwright-pool-mcp (this server)
                                        │
                                        ├── agent "A" ──SSE──> @playwright/mcp :9000 ──> Chrome A
                                        ├── agent "B" ──SSE──> @playwright/mcp :9001 ──> Chrome B
                                        ├── agent "C" ──SSE──> @playwright/mcp :9002 ──> Chrome C
                                        └── "default" ──SSE──> @playwright/mcp :9003 ──> Chrome D

The wrapper does not control Chrome directly. It spawns @playwright/mcp instances on demand and proxies MCP tool calls to them, adding per-agent routing.

🚀 Quick Start

Prerequisites

  • Node.js ≥ 18

  • npm

Install

git clone <your-repo-url> playwright-pool-mcp
cd playwright-pool-mcp
npm install
# First-time Chromium download (if needed):
npx playwright install chromium

Configure Claude Code

User-level global config (~/.claude.json):

{
  "mcpServers": {
    "playwright-pool": {
      "type": "stdio",
      "command": "node",
      "args": ["/path/to/playwright-pool-mcp/playwright-pool-script.js"],
      "env": {
        "BROWSER_POOL_BASE_PORT": "9000",
        "BROWSER_POOL_MAX_INSTANCES": "10",
        "BROWSER_POOL_GLOBAL_MAX": "12",
        "BROWSER_POOL_INSTANCE_TIMEOUT_MS": "1800000",
        "BROWSER_POOL_DEBUG": "0"
      }
    }
  }
}

Restart Claude Code. Tools appear as mcp__playwright-pool__*.

💡 Usage

Mode 1: Shared (backward compatible)

Omit _agent_id — all calls share one "default" browser:

mcp__playwright-pool__browser_navigate(url="https://example.com")
mcp__playwright-pool__browser_snapshot()

Pass _agent_id — each unique value gets its own browser:

subagent A: mcp__playwright-pool__browser_navigate(url="https://news.ycombinator.com", _agent_id="agent-a")
subagent B: mcp__playwright-pool__browser_navigate(url="https://github.com/trending",   _agent_id="agent-b")
subagent C: mcp__playwright-pool__browser_navigate(url="https://www.baidu.com",         _agent_id="agent-c")

⚠️ Important: pass the same _agent_id across all calls from one agent to keep its browser state (cookies, login, navigation). Different agents use different values.

Subagent template

Create .claude/agents/web-worker.md:

---
name: web-worker
description: Run browser tasks (scraping / research / testing / automation)
tools: mcp__playwright-pool__*, Read, Bash
---

Use mcp__playwright-pool__* tools to operate the browser.

**Key rule: pass the `_agent_id` parameter on every browser tool call.**
The main conversation may dispatch multiple web-workers concurrently; different
workers must use different `_agent_id` values so each gets its own isolated browser.

Examples:
- mcp__playwright-pool__browser_navigate(url="...", _agent_id="worker-1")
- mcp__playwright-pool__browser_evaluate(function="...", _agent_id="worker-1")

Use the same `_agent_id` for all calls within one worker.

🛠️ Tools

All tools accept an optional _agent_id parameter. Aligned 1:1 with @playwright/mcp v0.0.78.

ℹ️ Element targeting: interaction tools use target (snapshot ref or CSS selector). The ref parameter is accepted as an alias for target (Claude Code's client maps it).

Tool

Description

browser_navigate

Navigate to a URL

browser_navigate_back

Go back

browser_snapshot

Accessibility tree (LLM-friendly)

browser_take_screenshot

Screenshot (PNG/JPEG)

browser_click

Click element

browser_type

Type text into element

browser_press_key

Press keyboard key

browser_hover

Hover element

browser_select_option

Select dropdown option

browser_drag / browser_drop

Drag & drop

browser_evaluate

Run JavaScript

browser_wait_for

Wait for condition

browser_find

Find text in page snapshot

browser_fill_form

Fill multiple form fields (JSON array of {name, target, type, value})

browser_tabs

Manage tabs

browser_resize

Resize window

browser_handle_dialog

Handle alert/confirm

browser_file_upload

Upload files

browser_console_messages

Get console logs

browser_network_requests / browser_network_request

Network activity

browser_mouse_move_xy / browser_mouse_click_xy / browser_mouse_drag_xy / browser_mouse_down / browser_mouse_up / browser_mouse_wheel

Mouse control (screen coordinates)

browser_run_code_unsafe

Run arbitrary code (dangerous)

browser_close

Close browser for this agent

pool_status

Pool status (per agent)

⚙️ Configuration

All config via environment variables in ~/.claude.json — no code changes needed:

Variable

Default

Description

BROWSER_POOL_BASE_PORT

9000

Starting port for browser instances

BROWSER_POOL_MAX_INSTANCES

10

Max concurrent browsers per wrapper (≈300MB RAM each)

BROWSER_POOL_GLOBAL_MAX

0 (disabled)

Global max browsers across ALL wrapper sessions (multiple Claude Code windows). File-lock based counter in ~/.browser-pool/. Set e.g. 12 to cap total instances across every session.

BROWSER_POOL_INSTANCE_TIMEOUT_MS

1800000

Idle timeout (30 min)

BROWSER_POOL_DEBUG

0

Set 1 for debug logging to debug.log

Changes take effect after restarting Claude Code.

💡 Global limit (BROWSER_POOL_GLOBAL_MAX): each Claude Code window runs its own wrapper, and BROWSER_POOL_MAX_INSTANCES limits only that wrapper. To cap browser instances across ALL sessions (e.g. two windows must share a total of 12), set BROWSER_POOL_GLOBAL_MAX to the same value in every session's config. Slots are counted via a lock-protected counter file (~/.browser-pool/global-count) and are released when instances close or the wrapper exits gracefully.

🔍 How It Works

  1. Any browser tool call arrives → wrapper extracts _agent_id (defaults to "default")

  2. Looks up the per-agent assignment map → reuse existing browser if alive

  3. If none: check the global limit (BROWSER_POOL_GLOBAL_MAX) → find a free port → spawn local @playwright/mcp CLI (node cli.js --port X --isolated)

  4. Mark the instance busy during the call, so eviction never kills an in-flight call

  5. Pool full? Evict only idle instances (oldest first); if all busy, wait up to 60s

  6. Idle > 30 min → auto-killed; wrapper startup scans for orphaned instances and cleans them (PPID-guarded, never kills another live session's browsers)

  7. Global slot accounting: acquired before spawn, released when the instance closes or the wrapper exits gracefully

📦 Dependencies

📄 License

MIT

Available Tools

25 tools
browser_clickC

点击元素(element = a11y 引用,ref = CSS 选择器)

Inputs: ref (required), element (required), _agent_id.

browser_closeB

关闭该 agent 的浏览器

Inputs: _agent_id.

browser_console_messagesC

获取控制台消息

Inputs: level (required), _agent_id.

browser_dragC

把元素 A 拖到元素 B

Inputs: end_selector (required), start_selector (required), _agent_id.

browser_dropB

放下被拖拽的元素

Inputs: selector (required), _agent_id.

browser_evaluateC

在页面中执行 JavaScript(函数字符串)

Inputs: function (required), _agent_id.

browser_file_uploadD

上传文件

Inputs: paths (required), _agent_id.

browser_get_urlC

获取当前页面 URL

Inputs: _agent_id.

browser_handle_dialogC

处理 alert/confirm 弹窗

Inputs: accept (required), promptText (required), _agent_id.

browser_hoverC

悬停在元素上

Inputs: ref (required), element (required), _agent_id.

browser_navigateC

导航到 URL

Inputs: url (required), _agent_id.

browser_navigate_backD

后退

Inputs: _agent_id.

browser_navigate_forwardD

前进

Inputs: _agent_id.

browser_network_requestsC

获取网络请求活动

Inputs: _agent_id.

browser_press_keyC

按下键盘按键

Inputs: key (required), _agent_id.

browser_resizeC

调整浏览器窗口大小

Inputs: width (required), height (required), _agent_id.

browser_screenshotC

截图(别名)

Inputs: fullPage (required), _agent_id.

browser_select_optionC

在下拉框中选择选项

Inputs: ref (required), values (required), element (required), _agent_id.

browser_snapshotB

获取页面可访问性快照(结构化,适合 LLM 阅读)

Inputs: _agent_id.

browser_tabsC

管理标签页(action: list|new|close|select)

Inputs: index (required), action (required), _agent_id.

browser_take_screenshotC

截图(默认 fullPage=false)

Inputs: fullPage (required), _agent_id.

browser_typeC

向元素输入文本

Inputs: ref (required), text (required), submit (required), element (required), +1 more.

browser_wait_forD

等待条件

Inputs: text (required), time (required), textGone (required), _agent_id.

pool_statusA

查看浏览器池状态(按 agent)

Inputs: no named parameters.

pool_testD

测试异步响应

Inputs: message (required).

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    C2.5/5.0

    Scored across 25 tools

    Disambiguation3/5

    Most tools have distinct purposes, but browser_take_screenshot and browser_screenshot are explicit aliases, causing ambiguity. Additionally, browser_drag and browser_drop could be confused as both relate to drag-and-drop operations.

    Naming Consistency3/5

    The majority of tools follow a browser_verb pattern (e.g., browser_navigate, browser_click), but there are exceptions like browser_snapshot, browser_console_messages, and browser_network_requests which use nouns. The pair browser_take_screenshot and browser_screenshot also breaks consistency.

    Tool Count3/5

    At 25 tools, the server is on the heavy side but still plausible for a comprehensive browser automation tool. However, the presence of a duplicate screenshot tool and a test tool (pool_test) suggests some redundancy.

    Completeness4/5

    The tool set covers a wide range of browser actions: navigation, interaction, screenshots, snapshots, tab management, dialog handling, file upload, and more. Minor gaps exist (e.g., no explicit cookie management or scrolling) but core workflows are well covered.

    Maintenance

    ActivityInactive
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      C
      quality
      D
      maintenance
      A dynamic browser pool MCP server that enables multiple concurrent, isolated Playwright sessions for Claude Code and other MCP clients. It manages on-demand instances on dynamic ports to resolve session conflicts and includes automatic cleanup of idle browsers.
      20
      1
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      MCP server providing browser automation for AI agents with context-aware playbooks and skills for complex websites.
      41 npm
      179
      PolyForm Noncommercial 1.0.0
    • A
      license
      Not graded
      quality
      C
      maintenance
      MCP server for Meshbrow that gives AI agents full browser automation capabilities with stealth anti-detection, enabling natural language control of browser sessions, data extraction, and multi-browser fleets.
      MIT