playwright-mcp-server
Stateful MCP server wrapping Playwright for browser automation. Provides tools to navigate, interact, and extract data from web pages via a persistent browser session.
README
playwright-mcp-server
A stateful MCP server that wraps Playwright for browser automation.
- Stateful — single Chromium browser/context/page shared across all tool calls
- Persistent — session state (cookies, localStorage, active URL) survives server restarts
- Resilient — structured MCP error results on every tool call; browser crash auto-recovery
- Tested — 49 unit tests, no browser required to run the test suite
Quick start
git clone https://github.com/doucej/playwright-mcp-server.git
cd playwright-mcp-server
npm install
npx playwright install chromium
npm run build
npm start
# [playwright-mcp] Server ready — listening on stdio.
MCP client configuration
opencode (~/.config/opencode/opencode.json)
{
"mcp": {
"playwright": {
"type": "local",
"command": ["node", "/path/to/playwright-mcp-server/dist/server.js"],
"enabled": true
}
}
}
Claude Desktop (claude_desktop_config.json)
{
"mcpServers": {
"playwright": {
"command": "node",
"args": ["/path/to/playwright-mcp-server/dist/server.js"]
}
}
}
Hermes Agent (config.yaml)
mcp_servers:
playwright:
command: "node"
args: ["/path/to/playwright-mcp-server/dist/server.js"]
env:
STATE_DIR: "/path/to/playwright-mcp-server/state"
timeout: 120
connect_timeout: 30
Available tools
| Tool | Required args | Optional args | Description |
|---|---|---|---|
open_page |
url |
— | Navigate to a URL |
go_back |
— | — | Browser back |
go_forward |
— | — | Browser forward |
reload |
— | — | Reload current page |
get_url |
— | — | Return current URL |
click |
selector |
— | Click element |
type |
selector, text |
delay |
Type into element (appends) |
fill |
selector, value |
— | Fill input (replaces existing value) |
press |
selector, key |
— | Press keyboard key |
hover |
selector |
— | Hover over element |
select_option |
selector, value |
— | Select <select> option |
get_text |
selector |
— | Get visible text content |
get_attribute |
selector, attribute |
— | Get element attribute value |
get_html |
— | selector |
Get inner HTML (default: body) |
evaluate |
script |
— | Execute JavaScript, return JSON |
wait_for_selector |
selector |
timeout, state |
Wait for element |
screenshot |
— | full_page, selector |
Capture PNG screenshot |
save_state |
— | — | Persist session to disk |
load_state |
— | — | Restore session from disk |
State persistence
save_state (and graceful shutdown via SIGINT/SIGTERM) writes state/session.json containing Playwright's storageState (cookies + localStorage for all visited origins) plus the current page URL.
On the next startup the session is rehydrated automatically — storageState is passed to newContext() and the page navigates back to the saved URL.
Override the state directory:
STATE_DIR=/tmp/my-session npm start
Project structure
src/
server.ts MCP server entrypoint — stdio transport, startup, graceful shutdown
session.ts PlaywrightSession — lazy init, crash recovery, state save/restore
persistence.ts Read/write state/session.json; STATE_DIR override
tools.ts All 19 tool definitions + handlers; Zod validation; handle() wrapper
types.ts SessionState / StorageState interfaces
tests/
persistence.test.ts 7 unit tests — no browser required
session.test.ts 11 unit tests — Playwright mocked with vi.hoisted
tools.test.ts 31 unit tests — session mocked inline
state/ Created at runtime; holds session.json (git-ignored)
dist/ TypeScript compile output (git-ignored)
Development
npm run dev # run with tsx (no compile step)
npm test # run test suite (49 tests, no browser needed)
npm run typecheck # type-check without emitting
npm run build # compile TypeScript → dist/
Architecture
Singleton session — the MCP server is a long-running stdio process; a single PlaywrightSession instance guarantees exactly one browser across all tool dispatches.
Error isolation — every tool handler runs inside the handle() wrapper (src/tools.ts). Both Zod validation errors and Playwright runtime exceptions are caught and returned as { isError: true } MCP results. The server never throws.
Crash recovery — ensureInitialized() checks browser.isConnected() and page.isClosed() before every call. If the browser has crashed it relaunches and restores from the last persisted state.
License
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.