playwriter

playwriter

Connects to your running Chrome browser, enabling AI agents to automate it via Playwright API with existing logins and extensions.

Category
Visit Server

README

<div align='center'> <br/> <picture> <source media="(prefers-color-scheme: dark)" srcset="banner-dark.png" /> <source media="(prefers-color-scheme: light)" srcset="banner.png" /> <img src="banner.png" alt="Playwriter - For browser automation MCP" width="400" height="278" /> </picture> <br/> <br/> <p>Let your agents control your own Chrome, via CLI or MCP. Your logins, extensions, cookies — already there.</p> <br/> </div>

Other browser MCPs spawn a fresh Chrome — no logins, no extensions, instantly flagged by bot detectors, double the memory. Playwriter connects to your running browser instead. One Chrome extension, full Playwright API, everything you're already logged into.

Installation

  1. Install Extension from Chrome Web Store

  2. Click extension icon on a tab → turns green when connected

  3. Install the CLI and start automating the browser:

    npm i -g playwriter
    playwriter -s 1 -e 'await page.goto("https://example.com")'
    
  4. Install the skill so your agent knows how to use Playwriter:

    npx -y skills add remorses/playwriter
    

Quick Start

playwriter browser start  # starts Chrome for Testing/Chromium with bundled Playwriter extension
playwriter session new  # creates stateful sandbox, outputs session id (e.g. 1)
playwriter -s 1 -e 'await page.goto("https://example.com")'
playwriter -s 1 -e 'console.log(await snapshot({ page }))'
playwriter -s 1 -e 'await page.locator("aria-ref=e5").click()'

Tip: Always use single quotes for -e to prevent bash from interpreting $, backticks, and \ in your JS code. Use double quotes for strings inside the JS.

CLI Usage

Each session has isolated state. Browser tabs are shared across sessions.

# Browser management
playwriter browser start             # auto-finds Chrome for Testing or Chromium, with recording flags enabled
playwriter browser start /path/to/browser-binary

# Session management
playwriter session new              # creates stateful sandbox, outputs id (e.g. 1)
playwriter session list             # show sessions + state keys
playwriter session reset <id>       # fix connection issues

# Execute (always use -s)
playwriter -s 1 -e 'await page.goto("https://example.com")'
playwriter -s 1 -e 'await page.click("button")'
playwriter -s 1 -e 'console.log(await page.title())'

Create your own page to avoid interference from other agents:

playwriter -s 1 -e 'state.myPage = await context.newPage(); await state.myPage.goto("https://example.com")'

Multiline:

playwriter -s 1 -e $'
const title = await page.title();
console.log({ title, url: page.url() });
'

Examples

Variables in scope: page, context, state (persists between calls), require, and Node.js globals.

Persist data in state:

playwriter -e "state.users = await page.$$eval('.user', els => els.map(e => e.textContent))"
playwriter -e "console.log(state.users)"

Intercept network requests:

playwriter -e "state.requests = []; page.on('response', r => { if (r.url().includes('/api/')) state.requests.push(r.url()) })"
playwriter -e "await Promise.all([page.waitForResponse(r => r.url().includes('/api/')), page.click('button')])"
playwriter -e "console.log(state.requests)"

Set breakpoints and debug:

playwriter -e "state.cdp = await getCDPSession({ page }); state.dbg = createDebugger({ cdp: state.cdp }); await state.dbg.enable()"
playwriter -e "state.scripts = await state.dbg.listScripts({ search: 'app' }); console.log(state.scripts.map(s => s.url))"
playwriter -e "await state.dbg.setBreakpoint({ file: state.scripts[0].url, line: 42 })"

Live edit page code:

playwriter -e "state.cdp = await getCDPSession({ page }); state.editor = createEditor({ cdp: state.cdp }); await state.editor.enable()"
playwriter -e "await state.editor.edit({ url: 'https://example.com/app.js', oldString: 'const DEBUG = false', newString: 'const DEBUG = true' })"

Screenshot with labels:

playwriter -e "await screenshotWithAccessibilityLabels({ page })"

MCP Setup

Using the CLI with the skill (step 4 above) is the recommended approach. For direct MCP server configuration, see MCP.md.

Visual Labels

Vimium-style labels for AI agents to identify elements:

await screenshotWithAccessibilityLabels({ page })
// Returns screenshot + accessibility snapshot with aria-ref selectors
await page.locator('aria-ref=e5').click()

Color-coded: yellow=links, orange=buttons, coral=inputs, pink=checkboxes, peach=sliders, salmon=menus, amber=tabs.

Comparison

vs Playwright MCP

Playwright MCP Playwriter
Browser Spawns new Chrome Uses your Chrome
Extensions None Your existing ones
Login state Fresh Already logged in
Bot detection Always detected Can bypass (disconnect extension)
Collaboration Separate window Same browser as user

Note: Playwriter video recording is 100x more efficient than Playwright video recording, which sends base64 images for every frame.

Playwright CLI Playwriter
Browser Spawns new browser Uses your Chrome
Login state Fresh Already logged in
Extensions None Your existing ones
Captchas Always blocked Bypass (disconnect extension)
Collaboration Separate window Same browser as user
Capabilities Limited command set Anything Playwright can do
Raw CDP access No Yes
Video recording File-based tracing Native tab capture (30–60fps)

vs BrowserMCP

BrowserMCP Playwriter
Tools 12+ dedicated tools 1 execute tool
API Limited actions Full Playwright
Context usage High (tool schemas) Low
LLM knowledge Must learn tools Already knows Playwright

vs Antigravity (Jetski)

Jetski Playwriter
Tools 17+ tools 1 tool
Subagent Spawns for each browser task Direct execution
Latency High (agent overhead) Low

vs Claude Browser Extension

Claude Extension Playwriter
Agent support Claude only Any MCP client
Windows WSL No Yes
Context method Screenshots (100KB+) A11y snapshots (5-20KB)
Playwright API No Full
Debugger/breakpoints No Yes
Live code editing No Yes
Network interception Limited Full
Raw CDP access No Yes

vs Built-in Chrome CDP (--remote-debugging-port)

Built-in CDP Playwriter
Setup Restart Chrome with special flags Click extension icon
Confirmation dialog Shows automation infobar agents can't dismiss No blocking dialog
Autonomous agents Interrupted by debug banners Fully autonomous
User disruption Banners appear mid-workflow Silent — no interruption
Existing session Must relaunch Chrome (lose state) Uses your running browser

Chrome's --remote-debugging-port flag shows a persistent "controlled by automated software" banner that agents cannot dismiss. It pops up in the middle of your workflow whenever you're using the browser. Playwriter runs silently — agents work autonomously without any confirmation dialogs, so you're never interrupted.

Architecture

+---------------------+     +-------------------+     +-----------------+
|   BROWSER           |     |   LOCALHOST       |     |   MCP CLIENT    |
|                     |     |                   |     |                 |
|  +---------------+  |     | WebSocket Server  |     |  +-----------+  |
|  |   Extension   |<--------->  :19988         |     |  | AI Agent  |  |
|  +-------+-------+  | WS  |                   |     |  +-----------+  |
|          |          |     |  /extension       |     |        |        |
|    chrome.debugger  |     |       |           |     |        v        |
|          v          |     |       v           |     |  +-----------+  |
|  +---------------+  |     |  /cdp/:id <--------------> |  execute  |  |
|  | Tab 1 (green) |  |     +-------------------+  WS |  +-----------+  |
|  | Tab 2 (green) |  |                               |        |        |
|  | Tab 3 (gray)  |  |     Tab 3 not controlled      |  Playwright API |
+---------------------+     (no extension click)      +-----------------+

Remote Access

Control Chrome on a remote machine over the internet using traforo tunnels:

On host:

npx -y traforo -p 19988 -t my-machine -- npx -y playwriter serve --token <secret>

From remote:

export PLAYWRITER_HOST=https://my-machine-tunnel.traforo.dev
export PLAYWRITER_TOKEN=<secret>
playwriter -s 1 -e 'await page.goto("https://example.com")'

Also works on a LAN without traforo (PLAYWRITER_HOST=192.168.1.10). Full guide with use cases (remote Mac mini, user support, multi-machine control): docs/remote-access.md

Security

  • Local only: WebSocket server on localhost:19988
  • Origin validation: Only our extension IDs allowed (browsers can't spoof Origin)
  • Explicit consent: Only tabs where you clicked the extension icon
  • Visible automation: Chrome shows automation banner on controlled tabs
  • No remote access: Malicious websites cannot connect

Playwright API

Connect programmatically (without CLI):

import { chromium } from 'playwright-core'
import { startPlayWriterCDPRelayServer, getCdpUrl } from 'playwriter'

const server = await startPlayWriterCDPRelayServer()
const browser = await chromium.connectOverCDP(getCdpUrl())
const page = browser.contexts()[0].pages()[0]

await page.goto('https://example.com')
await page.screenshot({ path: 'screenshot.png' })
// Don't call browser.close() - it closes the user's Chrome
server.close()

Or connect to a running server:

npx -y playwriter serve --host 127.0.0.1
const browser = await chromium.connectOverCDP('http://127.0.0.1:19988')

Troubleshooting

View relay server logs to debug issues:

playwriter logfile  # prints the log file path
# typically: ~/.playwriter/relay-server.log

The relay log contains extension, MCP and WebSocket server logs. A separate CDP JSONL log is also created alongside it (see playwriter logfile). Both are recreated on each server start.

Example: summarize CDP traffic counts by direction + method:

jq -r '.direction + "\t" + (.message.method // "response")' ~/.playwriter/cdp.jsonl | uniq -c

Support

If Playwriter is useful to you, consider sponsoring the project.

Known Issues

  • If all pages return about:blank, restart Chrome (Chrome bug in chrome.debugger API)
  • Browser may switch to light mode on connect (Playwright issue)

Recommended Servers

playwright-mcp

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.

Official
Featured
TypeScript
Magic Component Platform (MCP)

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.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

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.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

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.

Official
Featured
TypeScript
Kagi MCP Server

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.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

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.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured