Real-time global intelligence dashboard. AI-powered news aggregation, geopolitical monitoring, and infrastructure tracking in a unified situational awareness interface
Chrome extension and MCP for Playwright control
Playwriter connects an agent to your running Chrome through a local extension and relay server. You can run Playwright code from the CLI, connect through MCP, or use the browser session from other tools without losing your existing tabs, logins, or extensions.
Builders who want their agent to work inside their real Chrome session instead of a fresh browser.
You can have an agent browse, click, inspect, and debug in the same browser state you already use.
What it does
Use your running Chrome
Connects to the browser you already have open, so your logins, cookies, and extensions stay available.
Stateful sessions
Creates isolated sessions that keep state between commands while sharing browser tabs.
CLI Playwright execution
Runs Playwright snippets with `playwriter -s <id> -e '...'` and supports page, context, and persistent state.
MCP access
Works as an MCP server so compatible agents can control the browser through standard tool calls.
Visual labels for elements
Adds accessibility labels to screenshots so agents can click elements with `aria-ref` selectors.
Remote browser control
Supports connecting to a browser on another machine with `PLAYWRITER_HOST` and `PLAYWRITER_TOKEN`.
How to get it
- 1Install the CLI and start automating the browser
npm i -g playwriter playwriter -s 1 -e 'await page.goto("https://example.com")' - 2Install the skill so your agent knows how to use Playwriter
npx -y skills add https://playwriter.dev
- 3Run
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()' - 4Create 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")' - 5Multiline
playwriter -s 1 -e $' const title = await page.title(); console.log({ title, url: page.url() }); '
README

Let your agents control your own Chrome, via CLI or MCP. Your logins, extensions, cookies — already there.
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
-
Install Extension from Chrome Web Store
-
Click extension icon on a tab → turns green when connected
-
Install the CLI and start automating the browser:
npm i -g playwriter playwriter -s 1 -e 'await page.goto("https://example.com")' -
Install the skill so your agent knows how to use Playwriter:
npx -y skills add https://playwriter.dev
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
-eto 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, importModule, native import(), and Node.js globals. Relative imports resolve from the session working directory.
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 })"
Live stream a tab to X Live / Twitch (RTMP, runs 24/7):
playwriter -s 1 -e "await page.goto('https://example.com')"
playwriter stream start -s 1 --rtmp rtmp://va.pscp.tv:80/x/<stream-key>
playwriter stream status -s 1
playwriter stream stop -s 1
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-portflag 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)
- Controlled tab scope: Tabs are controlled after an extension click. By default, Playwriter also creates a controlled
about:blanktab when a client connects with no controlled tabs. SetPLAYWRITER_AUTO_ENABLE=falseto require a manual click. - 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 inchrome.debuggerAPI) - Browser may switch to light mode on connect (Playwright issue)
Sponsor — Bloome
Building agents that drive the browser with Playwriter? Bloome gives them a home for your whole team — an AI-agent IM platform where agents are real members of the chat. Run them in the cloud, have them hand off tasks and collaborate, and share working agents with your team. Zero setup, on web and mobile.
Files in the repo
- .changeset
- .github
- db
- docs
- extension
- playwriter
- public
- scripts
- skills
- slop
- website
- .agentignore
- .gitignore
- .gitmodules
- .prettierignore
- .prettierrc.json
- AGENTS.md
- banner-dark.png
- banner-source.jpeg
- banner.png
- bloome-home.png
- CONTRIBUTING.md
- LICENSE
- MCP.md
- MEMORY.md
- package.json
- playwright
- PLAYWRITER_AGENTS.md
- pnpm-lock.yaml
- pnpm-workspace.yaml
- README.md
- tsconfig.base.json
Discussion (0)
Ask about usage, or say what you built with itSign in to join the discussion.
No comments yet. Be the first to say what this is good for.
More connectors
High-performance code intelligence MCP server. Indexes codebases into a persistent knowledge graph — average repo in milliseconds. 158 languages, sub-ms queries, 99% fewer tokens. Single static binary, zero dependencies.

Universal provider proxy for OpenAI Codex & Claude Code — use any LLM (Claude, Gemini, Grok, DeepSeek, Ollama…) with Codex CLI, App, SDK, and Claude Code
Git-native persistent memory for AI coding agents. Implements Google OKF v0.2 with sub-300µs in-memory BM25 search, embedded MCP server, and progressive disclosure. Slashes token bloat by 80% with zero external databases or dependencies. Built in pure Go.
Local-first code intelligence graph for MCP and CLI. Builds a persistent map of your codebase so AI coding tools read only what matters, with benchmarked context reductions on reviews and large-repo workflows.
Stop your AI from making things up — it proposes, deterministic tools decide, every claim checked against ground truth with evidence. Grounded facts and context survive resets. Reverse engineering is the proving ground. MCP server + CLI.