{
  "markdown": "<div align='center'>\n    <br/>\n    <picture>\n        <source media=\"(prefers-color-scheme: dark)\" srcset=\"banner-dark.png\" />\n        <source media=\"(prefers-color-scheme: light)\" srcset=\"banner.png\" />\n    <img src=\"banner.png\" alt=\"Playwriter - For browser automation MCP\" width=\"400\" height=\"278\" />\n    </picture>\n    <br/>\n    <br/>\n    <p>Let your agents control your own Chrome, via CLI or MCP. Your logins, extensions, cookies — already there.</p>\n    <br/>\n</div>\n\nOther 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.\n\n## Installation\n\n1. [**Install Extension**](https://chromewebstore.google.com/detail/playwriter-mcp/jfeammnjpkecdekppnclgkkffahnhfhe) from Chrome Web Store\n\n2. Click extension icon on a tab → turns green when connected\n\n3. Install the CLI and start automating the browser:\n\n   ```bash\n   npm i -g playwriter\n   playwriter -s 1 -e 'await page.goto(\"https://example.com\")'\n   ```\n\n4. Install the skill so your agent knows how to use Playwriter:\n   ```bash\n   npx -y skills add https://playwriter.dev\n   ```\n\n## Quick Start\n\n```bash\nplaywriter browser start  # starts Chrome for Testing/Chromium with bundled Playwriter extension\nplaywriter session new  # creates stateful sandbox, outputs session id (e.g. 1)\nplaywriter -s 1 -e 'await page.goto(\"https://example.com\")'\nplaywriter -s 1 -e 'console.log(await snapshot({ page }))'\nplaywriter -s 1 -e 'await page.locator(\"aria-ref=e5\").click()'\n```\n\n> **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.\n\n## CLI Usage\n\nEach session has **isolated state**. Browser tabs are **shared** across sessions.\n\n```bash\n# Browser management\nplaywriter browser start             # auto-finds Chrome for Testing or Chromium, with recording flags enabled\nplaywriter browser start /path/to/browser-binary\n\n# Session management\nplaywriter session new              # creates stateful sandbox, outputs id (e.g. 1)\nplaywriter session list             # show sessions + state keys\nplaywriter session reset <id>       # fix connection issues\n\n# Execute (always use -s)\nplaywriter -s 1 -e 'await page.goto(\"https://example.com\")'\nplaywriter -s 1 -e 'await page.click(\"button\")'\nplaywriter -s 1 -e 'console.log(await page.title())'\n```\n\nCreate your own page to avoid interference from other agents:\n\n```bash\nplaywriter -s 1 -e 'state.myPage = await context.newPage(); await state.myPage.goto(\"https://example.com\")'\n```\n\nMultiline:\n\n```bash\nplaywriter -s 1 -e $'\nconst title = await page.title();\nconsole.log({ title, url: page.url() });\n'\n```\n\n## Examples\n\nVariables in scope: `page`, `context`, `state` (persists between calls), `require`, `importModule`, native `import()`, and Node.js globals. Relative imports resolve from the session working directory.\n\n**Persist data in state:**\n\n```bash\nplaywriter -e \"state.users = await page.$$eval('.user', els => els.map(e => e.textContent))\"\nplaywriter -e \"console.log(state.users)\"\n```\n\n**Intercept network requests:**\n\n```bash\nplaywriter -e \"state.requests = []; page.on('response', r => { if (r.url().includes('/api/')) state.requests.push(r.url()) })\"\nplaywriter -e \"await Promise.all([page.waitForResponse(r => r.url().includes('/api/')), page.click('button')])\"\nplaywriter -e \"console.log(state.requests)\"\n```\n\n**Set breakpoints and debug:**\n\n```bash\nplaywriter -e \"state.cdp = await getCDPSession({ page }); state.dbg = createDebugger({ cdp: state.cdp }); await state.dbg.enable()\"\nplaywriter -e \"state.scripts = await state.dbg.listScripts({ search: 'app' }); console.log(state.scripts.map(s => s.url))\"\nplaywriter -e \"await state.dbg.setBreakpoint({ file: state.scripts[0].url, line: 42 })\"\n```\n\n**Live edit page code:**\n\n```bash\nplaywriter -e \"state.cdp = await getCDPSession({ page }); state.editor = createEditor({ cdp: state.cdp }); await state.editor.enable()\"\nplaywriter -e \"await state.editor.edit({ url: 'https://example.com/app.js', oldString: 'const DEBUG = false', newString: 'const DEBUG = true' })\"\n```\n\n**Screenshot with labels:**\n\n```bash\nplaywriter -e \"await screenshotWithAccessibilityLabels({ page })\"\n```\n\n**Live stream a tab to X Live / Twitch (RTMP, runs 24/7):**\n\n```bash\nplaywriter -s 1 -e \"await page.goto('https://example.com')\"\nplaywriter stream start -s 1 --rtmp rtmp://va.pscp.tv:80/x/<stream-key>\nplaywriter stream status -s 1\nplaywriter stream stop -s 1\n```\n\n## MCP Setup\n\nUsing the CLI with the skill (step 4 above) is the recommended approach. For direct MCP server configuration, see [MCP.md](./MCP.md).\n\n## Visual Labels\n\nVimium-style labels for AI agents to identify elements:\n\n```javascript\nawait screenshotWithAccessibilityLabels({ page })\n// Returns screenshot + accessibility snapshot with aria-ref selectors\nawait page.locator('aria-ref=e5').click()\n```\n\nColor-coded: yellow=links, orange=buttons, coral=inputs, pink=checkboxes, peach=sliders, salmon=menus, amber=tabs.\n\n## Comparison\n\n### vs Playwright MCP\n\n|               | Playwright MCP    | Playwriter                        |\n| ------------- | ----------------- | --------------------------------- |\n| Browser       | Spawns new Chrome | **Uses your Chrome**              |\n| Extensions    | None              | Your existing ones                |\n| Login state   | Fresh             | Already logged in                 |\n| Bot detection | Always detected   | Can bypass (disconnect extension) |\n| Collaboration | Separate window   | Same browser as user              |\n\n> **Note:** Playwriter video recording is **100x more efficient than Playwright video recording**, which sends **base64 images for every frame**.\n\n|                 | Playwright CLI      | Playwriter                    |\n| --------------- | ------------------- | ----------------------------- |\n| Browser         | Spawns new browser  | **Uses your Chrome**          |\n| Login state     | Fresh               | Already logged in             |\n| Extensions      | None                | Your existing ones            |\n| Captchas        | Always blocked      | Bypass (disconnect extension) |\n| Collaboration   | Separate window     | Same browser as user          |\n| Capabilities    | Limited command set | Anything Playwright can do    |\n| Raw CDP access  | No                  | Yes                           |\n| Video recording | File-based tracing  | Native tab capture (30–60fps) |\n\n### vs BrowserMCP\n\n|               | BrowserMCP          | Playwriter               |\n| ------------- | ------------------- | ------------------------ |\n| Tools         | 12+ dedicated tools | 1 `execute` tool         |\n| API           | Limited actions     | Full Playwright          |\n| Context usage | High (tool schemas) | Low                      |\n| LLM knowledge | Must learn tools    | Already knows Playwright |\n\n### vs Antigravity (Jetski)\n\n|          | Jetski                       | Playwriter       |\n| -------- | ---------------------------- | ---------------- |\n| Tools    | 17+ tools                    | 1 tool           |\n| Subagent | Spawns for each browser task | Direct execution |\n| Latency  | High (agent overhead)        | Low              |\n\n### vs Claude Browser Extension\n\n|                      | Claude Extension     | Playwriter              |\n| -------------------- | -------------------- | ----------------------- |\n| Agent support        | Claude only          | Any MCP client          |\n| Windows WSL          | No                   | Yes                     |\n| Context method       | Screenshots (100KB+) | A11y snapshots (5-20KB) |\n| Playwright API       | No                   | Full                    |\n| Debugger/breakpoints | No                   | Yes                     |\n| Live code editing    | No                   | Yes                     |\n| Network interception | Limited              | Full                    |\n| Raw CDP access       | No                   | Yes                     |\n\n### vs Built-in Chrome CDP (`--remote-debugging-port`)\n\n|                       | Built-in CDP                          | Playwriter                   |\n| --------------------- | ------------------------------------- | ---------------------------- |\n| Setup                 | Restart Chrome with special flags     | Click extension icon         |\n| Confirmation dialog   | Shows automation infobar agents can't dismiss | No blocking dialog   |\n| Autonomous agents     | Interrupted by debug banners          | Fully autonomous             |\n| User disruption       | Banners appear mid-workflow           | Silent — no interruption     |\n| Existing session      | Must relaunch Chrome (lose state)     | Uses your running browser    |\n\n> 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.\n\n## Architecture\n\n```\n+---------------------+     +-------------------+     +-----------------+\n|   BROWSER           |     |   LOCALHOST       |     |   MCP CLIENT    |\n|                     |     |                   |     |                 |\n|  +---------------+  |     | WebSocket Server  |     |  +-----------+  |\n|  |   Extension   |<--------->  :19988         |     |  | AI Agent  |  |\n|  +-------+-------+  | WS  |                   |     |  +-----------+  |\n|          |          |     |  /extension       |     |        |        |\n|    chrome.debugger  |     |       |           |     |        v        |\n|          v          |     |       v           |     |  +-----------+  |\n|  +---------------+  |     |  /cdp/:id <--------------> |  execute  |  |\n|  | Tab 1 (green) |  |     +-------------------+  WS |  +-----------+  |\n|  | Tab 2 (green) |  |                               |        |        |\n|  | Tab 3 (gray)  |  |     Tab 3 not controlled      |  Playwright API |\n+---------------------+     (no extension click)      +-----------------+\n```\n\n## Remote Access\n\nControl Chrome on a remote machine over the internet using [traforo](https://traforo.dev) tunnels:\n\n**On host:**\n\n```bash\nnpx -y traforo -p 19988 -t my-machine -- npx -y playwriter serve --token <secret>\n```\n\n**From remote:**\n\n```bash\nexport PLAYWRITER_HOST=https://my-machine-tunnel.traforo.dev\nexport PLAYWRITER_TOKEN=<secret>\nplaywriter -s 1 -e 'await page.goto(\"https://example.com\")'\n```\n\nAlso 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](./docs/remote-access.md)\n\n## Security\n\n- **Local only**: WebSocket server on `localhost:19988`\n- **Origin validation**: Only our extension IDs allowed (browsers can't spoof Origin)\n- **Controlled tab scope**: Tabs are controlled after an extension click. By default, Playwriter also creates a controlled `about:blank` tab when a client connects with no controlled tabs. Set `PLAYWRITER_AUTO_ENABLE=false` to require a manual click.\n- **Visible automation**: Chrome shows automation banner on controlled tabs\n- **No remote access**: Malicious websites cannot connect\n\n## Playwright API\n\nConnect programmatically (without CLI):\n\n```typescript\nimport { chromium } from 'playwright-core'\nimport { startPlayWriterCDPRelayServer, getCdpUrl } from 'playwriter'\n\nconst server = await startPlayWriterCDPRelayServer()\nconst browser = await chromium.connectOverCDP(getCdpUrl())\nconst page = browser.contexts()[0].pages()[0]\n\nawait page.goto('https://example.com')\nawait page.screenshot({ path: 'screenshot.png' })\n// Don't call browser.close() - it closes the user's Chrome\nserver.close()\n```\n\nOr connect to a running server:\n\n```bash\nnpx -y playwriter serve --host 127.0.0.1\n```\n\n```typescript\nconst browser = await chromium.connectOverCDP('http://127.0.0.1:19988')\n```\n\n## Troubleshooting\n\nView relay server logs to debug issues:\n\n```bash\nplaywriter logfile  # prints the log file path\n# typically: ~/.playwriter/relay-server.log\n```\n\nThe 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.\n\nExample: summarize CDP traffic counts by direction + method:\n\n```bash\njq -r '.direction + \"\\t\" + (.message.method // \"response\")' ~/.playwriter/cdp.jsonl | uniq -c\n```\n\n## Support\n\nIf Playwriter is useful to you, consider [sponsoring the project](https://github.com/sponsors/remorses).\n\n## Known Issues\n\n- If all pages return `about:blank`, restart Chrome (Chrome bug in `chrome.debugger` API)\n- Browser may switch to light mode on connect ([Playwright issue](https://github.com/microsoft/playwright/issues/37627))\n\n## Sponsor — Bloome\n\n<div align=\"center\">\n<a href=\"https://bloome.im/app?utm_medium=github&utm_source=remorses-playwriter-ivor-202607\">\n  <img src=\"bloome-home.png\" alt=\"Bloome\" width=\"480\" />\n</a>\n</div>\n\nBuilding agents that drive the browser with Playwriter? [**Bloome**](https://bloome.im/app?utm_medium=github&utm_source=remorses-playwriter-ivor-202607) 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.\n",
  "bytes": 13567,
  "sha": "cc1607e249f69a740f4388a3653406d43137a56c200241dbfa293550d9cfbea1",
  "repo_slug": "remorses/playwriter",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/skl_remorses_playwriter_playwriter_7b668fe2/readme"
}