Skip to main content
Glama
ekuznetski

Better Playwright MCP

by ekuznetski

Better Playwright MCP

Token-efficient Playwright MCP with getOutline and searchSnapshot - saves ~95% tokens vs full snapshots.

This is a fork of livoras/better-playwright-mcp with critical fixes applied.

Token Savings

Method

Lines

Characters

Savings

Official Playwright MCP (full snapshot)

673

~42,000

-

Better Playwright (getOutline)

48

~2,100

95%

Related MCP server: Enhanced Browser MCP Server

Fixes Applied

1. Snapshot Bug Fix

The original package's page._snapshotForAI() returns { full: "..." } object instead of string in newer Playwright versions, causing snapshot.split is not a function error.

// Before (broken):
const snapshot = await pageInfo.page._snapshotForAI();

// After (fixed):
const rawSnapshot = await pageInfo.page._snapshotForAI();
const snapshot = typeof rawSnapshot === 'string' ? rawSnapshot : rawSnapshot?.full ?? '';

2. Missing MCP Server

The original npm package references dist/mcp-server.js which doesn't exist. This fork includes a proper MCP wrapper (index.mjs).

3. MCP SDK API Compatibility

The MCP SDK (v1.0+) changed from string-based to schema-based request handlers. This fork uses the new API:

// Old API (broken with MCP SDK v1.0+):
server.setRequestHandler('tools/list', async () => {...});
server.setRequestHandler('tools/call', async (req) => {...});

// New API (fixed):
import {
  ListToolsRequestSchema,
  CallToolRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';

server.setRequestHandler(ListToolsRequestSchema, async () => {...});
server.setRequestHandler(CallToolRequestSchema, async (req) => {...});

Requirements

  • Node.js >= 18.0.0

Installation

npm install -g github:ekuznetski/better-playwright-mcp

Per-Project Installation

# npm
npm install github:ekuznetski/better-playwright-mcp

# pnpm
pnpm add github:ekuznetski/better-playwright-mcp

# yarn
yarn add github:ekuznetski/better-playwright-mcp

Setup

Step 1: Start HTTP Server

The HTTP server must be running for the MCP to work.

# If installed globally
better-playwright-server

# If installed per-project
./node_modules/.bin/better-playwright-server

# Or with npx
npx better-playwright-server

By default, the server runs on port 3102. Set PORT environment variable to change.

Step 2: Configure MCP

Claude Code (Project Config)

Create .mcp.json in your project root:

{
  "mcpServers": {
    "better-playwright": {
      "command": "better-playwright-mcp",
      "env": {
        "BETTER_PLAYWRIGHT_URL": "http://localhost:3102"
      }
    }
  }
}

Claude Code (Global Config)

Add to ~/.claude.json:

{
  "mcpServers": {
    "better-playwright": {
      "command": "better-playwright-mcp",
      "env": {
        "BETTER_PLAYWRIGHT_URL": "http://localhost:3102"
      }
    }
  }
}

Available Tools

Tool

Description

create_page

Create a new browser page and navigate to URL. Returns pageId.

get_outline

Get compressed page structure (max ~200 lines). 95% token savings.

search_snapshot

Search page content with regex. Returns only matching lines (max 100).

click

Click element by ref ID (e.g., "e5", "e12").

type_text

Type text into element by ref ID.

hover

Hover over element by ref ID.

navigate

Navigate to URL.

screenshot

Take screenshot of page.

press_key

Press keyboard key (Enter, Tab, Escape, etc.).

scroll_to_top

Scroll to top of page.

scroll_to_bottom

Scroll to bottom of page.

get_console_messages

Get browser console logs and JS errors. Supports clear: true to flush buffer.

list_pages

List all open browser pages.

close_page

Close browser page.

Usage Guide for AI Agents

1. create_page(name: "app", url: "http://localhost:3000")
   -> Returns pageId: "abc-123"

2. get_outline(pageId: "abc-123")
   -> Returns compressed page structure with element refs like [ref=e5]
   -> Use this to understand layout and find clickable elements

3. search_snapshot(pageId: "abc-123", pattern: "Submit|Login")
   -> Returns only lines matching the pattern — much faster than get_outline for targeted search

4. click(pageId: "abc-123", ref: "e5")
   -> Clicks element. Always get ref from get_outline or search_snapshot first.

5. get_console_messages(pageId: "abc-123", clear: true)
   -> Check for JS errors or logs after interaction. Pass clear: true to flush buffer.

When to use each tool

  • get_outline — first step to understand page structure, max ~200 lines

  • search_snapshot — when you know what text/element to find, much more token-efficient

  • get_console_messages — after page load or interactions to catch JS errors, debug logs

  • screenshot — only when visual layout matters; costs tokens due to image

  • press_key — for Enter (submit form), Escape (close modal), Tab (focus next field)

Debugging frontend issues

# After clicking a button or navigating, always check console:
get_console_messages(pageId: "abc-123")

# Expected output:
# [12:34:56.123] [LOG] Component mounted
# [12:34:56.200] [ERROR] Uncaught TypeError: Cannot read property 'id' of undefined

# To avoid accumulating old logs, clear after reading:
get_console_messages(pageId: "abc-123", clear: true)

Finding elements

Refs in get_outline look like [ref=e5]. Use the ref directly in click, type_text, hover.

# Find a specific button:
search_snapshot(pageId: "abc-123", pattern: "Submit")
-> button "Submit" [ref=e42]

click(pageId: "abc-123", ref: "e42")

Running HTTP Server as Background Service

macOS (launchd)

cat > ~/Library/LaunchAgents/com.better-playwright.plist << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.better-playwright</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/local/bin/better-playwright-server</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
</dict>
</plist>
EOF

launchctl load ~/Library/LaunchAgents/com.better-playwright.plist

Linux (systemd)

mkdir -p ~/.config/systemd/user

cat > ~/.config/systemd/user/better-playwright.service << 'EOF'
[Unit]
Description=Better Playwright HTTP Server

[Service]
ExecStart=/usr/local/bin/better-playwright-server
Restart=always

[Install]
WantedBy=default.target
EOF

systemctl --user enable better-playwright
systemctl --user start better-playwright

Environment Variables

Variable

Default

Description

PORT

3102

HTTP server port

BETTER_PLAYWRIGHT_URL

http://localhost:3102

URL for MCP to connect to HTTP server

Troubleshooting

MCP not connecting / "Schema is missing a method literal"

If you see this error when Claude Code tries to connect:

Error: Schema is missing a method literal

This means the MCP SDK API changed. Ensure you have the latest version of this package:

npm update -g github:ekuznetski/better-playwright-mcp

The fix uses schema-based handlers instead of string-based ones (see "Fixes Applied" section above).

Server not starting

# Check if port is in use
lsof -i :3102

# Kill existing process
lsof -ti :3102 | xargs kill -9

ripgrep binary missing

If you see /bin/sh: .../rg: No such file or directory:

cd node_modules/@vscode/ripgrep && npm run postinstall

Connection refused

Make sure the HTTP server is running before using the MCP tools:

better-playwright-server

The server should output: Better Playwright HTTP server running on port 3102

License

MIT

Credits

A
license - permissive license
-
quality - not tested
D
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/ekuznetski/better-playwright-mcp'

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