Skip to main content
Glama
doucej

playwright-mcp-server

by doucej

playwright-mcp-server

CI License: MIT Node.js >=18

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.

Related MCP server: Playwright MCP

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 recoveryensureInitialized() checks browser.isConnected() and page.isClosed() before every call. If the browser has crashed it relaunches and restores from the last persisted state.


License

MIT

A
license - permissive license
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/doucej/playwright-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server