{"id":35093998,"url":"https://github.com/remorses/playwriter","last_synced_at":"2026-02-17T16:07:41.430Z","repository":{"id":324373092,"uuid":"1095948794","full_name":"remorses/playwriter","owner":"remorses","description":"Chrome extension to let agents control your browser. Runs Playwright snippets in a stateful sandbox. Available as CLI or MCP","archived":false,"fork":false,"pushed_at":"2026-02-13T23:09:09.000Z","size":10186,"stargazers_count":2813,"open_issues_count":9,"forks_count":108,"subscribers_count":12,"default_branch":"main","last_synced_at":"2026-02-14T05:34:22.402Z","etag":null,"topics":["mcp","playwright"],"latest_commit_sha":null,"homepage":"https://chromewebstore.google.com/detail/playwriter-mcp/jfeammnjpkecdekppnclgkkffahnhfhe","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/remorses.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null},"funding":{"github":"remorses"}},"created_at":"2025-11-13T18:23:26.000Z","updated_at":"2026-02-14T01:45:19.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/remorses/playwriter","commit_stats":null,"previous_names":["remorses/playwriter"],"tags_count":32,"template":false,"template_full_name":null,"purl":"pkg:github/remorses/playwriter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/remorses%2Fplaywriter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/remorses%2Fplaywriter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/remorses%2Fplaywriter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/remorses%2Fplaywriter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/remorses","download_url":"https://codeload.github.com/remorses/playwriter/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/remorses%2Fplaywriter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29549343,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-17T14:33:00.708Z","status":"ssl_error","status_checked_at":"2026-02-17T14:32:58.657Z","response_time":100,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["mcp","playwright"],"created_at":"2025-12-27T15:04:24.309Z","updated_at":"2026-02-17T16:07:36.407Z","avatar_url":"https://github.com/remorses.png","language":"HTML","funding_links":["https://github.com/sponsors/remorses"],"categories":["MCP Servers","📚 Projects (1974 total)","HTML","Browser Automation"],"sub_categories":["Browser Automation","MCP Servers"],"readme":"\u003cdiv align='center'\u003e\n    \u003cbr/\u003e\n    \u003cpicture\u003e\n        \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"banner-dark.png\" /\u003e\n        \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"banner.png\" /\u003e\n    \u003cimg src=\"banner.png\" alt=\"Playwriter - For browser automation MCP\" width=\"400\" height=\"278\" /\u003e\n    \u003c/picture\u003e\n    \u003cbr/\u003e\n    \u003cbr/\u003e\n    \u003cp\u003eControl your browser via Playwright API. Uses extension + CLI. No context bloat.\u003c/p\u003e\n    \u003cbr/\u003e\n\u003c/div\u003e\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   ```bash\n   npm i -g playwriter\n   playwriter -s 1 -e \"await page.goto('https://example.com')\"\n   ```\n\n4. Add skill to your agent:\n   ```bash\n   npx -y skills add remorses/playwriter\n   ```\n\n## Quick Start\n\n```bash\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 accessibilitySnapshot({ page }))\"\nplaywriter -s 1 -e \"await page.locator('aria-ref=e5').click()\"\n```\n\n## CLI Usage\n\nEach session has **isolated state**. Browser tabs are **shared** across sessions.\n\n```bash\n# Session management\nplaywriter session new              # creates stateful sandbox, outputs id (e.g. 1)\nplaywriter session list             # show sessions + state keys\nplaywriter session reset \u003cid\u003e       # 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`, and Node.js globals.\n\n**Persist data in state:**\n```bash\nplaywriter -e \"state.users = await page.$$eval('.user', els =\u003e els.map(e =\u003e e.textContent))\"\nplaywriter -e \"console.log(state.users)\"\n```\n\n**Intercept network requests:**\n```bash\nplaywriter -e \"state.requests = []; page.on('response', r =\u003e { if (r.url().includes('/api/')) state.requests.push(r.url()) })\"\nplaywriter -e \"await Promise.all([page.waitForResponse(r =\u003e r.url().includes('/api/')), page.click('button')])\"\nplaywriter -e \"console.log(state.requests)\"\n```\n\n**Set breakpoints and debug:**\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 =\u003e s.url))\"\nplaywriter -e \"await state.dbg.setBreakpoint({ file: state.scripts[0].url, line: 42 })\"\n```\n\n**Live edit page code:**\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```bash\nplaywriter -e \"await screenshotWithAccessibilityLabels({ page })\"\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### 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## Architecture\n\n```\n+---------------------+     +-------------------+     +-----------------+\n|   BROWSER           |     |   LOCALHOST       |     |   MCP CLIENT    |\n|                     |     |                   |     |                 |\n|  +---------------+  |     | WebSocket Server  |     |  +-----------+  |\n|  |   Extension   |\u003c---------\u003e  :19988         |     |  | AI Agent  |  |\n|  +-------+-------+  | WS  |                   |     |  +-----------+  |\n|          |          |     |  /extension       |     |        |        |\n|    chrome.debugger  |     |       |           |     |        v        |\n|          v          |     |       v           |     |  +-----------+  |\n|  +---------------+  |     |  /cdp/:id \u003c--------------\u003e |  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 CLI\n\nRun CLI from a different machine (devcontainer, VM, SSH) while Chrome runs on your host.\n\n**On host:**\n```bash\nplaywriter serve --token \u003csecret\u003e\n```\n\n**From remote:**\n```bash\nplaywriter --host 192.168.1.10 --token \u003csecret\u003e session new\nplaywriter --host 192.168.1.10 --token \u003csecret\u003e -s 1 -e \"await page.goto('https://example.com')\"\n```\n\nOr with env vars:\n```bash\nexport PLAYWRITER_HOST=192.168.1.10\nexport PLAYWRITER_TOKEN=\u003csecret\u003e\nplaywriter -s 1 -e \"await page.goto('https://example.com')\"\n```\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- **Explicit consent**: Only tabs where you clicked the extension icon\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","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fremorses%2Fplaywriter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fremorses%2Fplaywriter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fremorses%2Fplaywriter/lists"}