This commit is contained in:
Tommy D. Rossi
2026-01-06 12:32:32 +01:00
parent 9c1eadb536
commit d7056292cd
+45 -54
View File
@@ -41,9 +41,7 @@
"mcpServers": { "mcpServers": {
"playwriter": { "playwriter": {
"command": "npx", "command": "npx",
"args": [ "args": ["playwriter@latest"]
"playwriter@latest"
]
} }
} }
} }
@@ -62,6 +60,7 @@
3. Click the extension icon - it will turn green when connected 3. Click the extension icon - it will turn green when connected
Once enabled on one or more tabs, your AI assistant can: Once enabled on one or more tabs, your AI assistant can:
- Control all enabled tabs through the `execute` tool - Control all enabled tabs through the `execute` tool
- Switch between tabs using playwright's context and page APIs - Switch between tabs using playwright's context and page APIs
- Create new tabs programmatically - Create new tabs programmatically
@@ -77,10 +76,8 @@ You can use playwriter programmatically with playwright-core:
import { chromium } from 'playwright-core' import { chromium } from 'playwright-core'
import { startPlayWriterCDPRelayServer, getCdpUrl } from 'playwriter' import { startPlayWriterCDPRelayServer, getCdpUrl } from 'playwriter'
const server = await startPlayWriterCDPRelayServer() const server = await startPlayWriterCDPRelayServer()
const browser = await chromium.connectOverCDP(getCdpUrl()) const browser = await chromium.connectOverCDP(getCdpUrl())
const context = browser.contexts()[0] const context = browser.contexts()[0]
@@ -116,6 +113,7 @@ await screenshotWithAccessibilityLabels({ page })
The function automatically shows labels, takes a screenshot, hides labels, and includes both the image and accessibility snapshot in the MCP response. Screenshots are saved to `./tmp/` with unique filenames. The function automatically shows labels, takes a screenshot, hides labels, and includes both the image and accessibility snapshot in the MCP response. Screenshots are saved to `./tmp/` with unique filenames.
**Features:** **Features:**
- **Role filtering** - Only shows labels for interactive elements (buttons, links, inputs, etc.) - **Role filtering** - Only shows labels for interactive elements (buttons, links, inputs, etc.)
- **Visibility detection** - Skips elements covered by modals or overlays - **Visibility detection** - Skips elements covered by modals or overlays
- **Overlap prevention** - Skips labels that would overlap with already-placed ones - **Overlap prevention** - Skips labels that would overlap with already-placed ones
@@ -124,19 +122,19 @@ The function automatically shows labels, takes a screenshot, hides labels, and i
**Color legend:** **Color legend:**
| Color | Element Types | | Color | Element Types |
|-------|---------------| | ------ | ------------------------------------------ |
| Yellow | Links | | Yellow | Links |
| Orange | Buttons | | Orange | Buttons |
| Coral | Text inputs (textbox, combobox, searchbox) | | Coral | Text inputs (textbox, combobox, searchbox) |
| Pink | Checkboxes, radios, switches | | Pink | Checkboxes, radios, switches |
| Peach | Sliders | | Peach | Sliders |
| Salmon | Menu items | | Salmon | Menu items |
| Amber | Tabs, options | | Amber | Tabs, options |
### Environment Variables ### Environment Variables
#### `PLAYWRITER_AUTO_ENABLE` #### `PLAYWRITER_AUTO_ENABLE`
When set, the MCP will automatically create an initial tab when a Playwright client connects and no tabs are currently enabled. This is useful for fully automated workflows where you don't want to manually click the extension icon. When set, the MCP will automatically create an initial tab when a Playwright client connects and no tabs are currently enabled. This is useful for fully automated workflows where you don't want to manually click the extension icon.
@@ -229,10 +227,10 @@ Claude Code has a built-in Chrome extension ("Claude in Chrome") that uses Nativ
Claude's extension sends screenshots to the LLM for visual understanding, then uses DOM inspection for interactions. Playwriter uses accessibility snapshots instead - structured text describing interactive elements with their roles, names, and states: Claude's extension sends screenshots to the LLM for visual understanding, then uses DOM inspection for interactions. Playwriter uses accessibility snapshots instead - structured text describing interactive elements with their roles, names, and states:
| Approach | What's sent to LLM | Typical size | | Approach | What's sent to LLM | Typical size |
|----------|-------------------|--------------| | ------------------ | ----------------------- | -------------------- |
| Claude's extension | Screenshots | 100KB-1MB+ per image | | Claude's extension | Screenshots | 100KB-1MB+ per image |
| Playwriter | Accessibility snapshots | 5-20KB text | | Playwriter | Accessibility snapshots | 5-20KB text |
**Full Playwright API vs Step-by-Step Navigation:** **Full Playwright API vs Step-by-Step Navigation:**
@@ -240,12 +238,9 @@ Claude's extension navigates pages step-by-step with screenshots. Playwriter exp
```js ```js
// Complex interactions in one call // Complex interactions in one call
await page.locator('tr').filter({ hasText: 'John' }).locator('button').click(); await page.locator('tr').filter({ hasText: 'John' }).locator('button').click()
const [download] = await Promise.all([ const [download] = await Promise.all([page.waitForEvent('download'), page.click('button.export')])
page.waitForEvent('download'), await download.saveAs('/tmp/report.pdf')
page.click('button.export')
]);
await download.saveAs('/tmp/report.pdf');
``` ```
**Advanced DevTools Features:** **Advanced DevTools Features:**
@@ -254,49 +249,49 @@ Playwriter provides capabilities Claude's extension doesn't have:
```js ```js
// Set breakpoints and inspect variables at runtime // Set breakpoints and inspect variables at runtime
const dbg = createDebugger({ cdp }); const dbg = createDebugger({ cdp })
await dbg.setBreakpoint({ file: 'app.js', line: 42 }); await dbg.setBreakpoint({ file: 'app.js', line: 42 })
// when paused: await dbg.inspectLocalVariables() // when paused: await dbg.inspectLocalVariables()
// Live-edit page scripts without reload // Live-edit page scripts without reload
const editor = createEditor({ cdp }); const editor = createEditor({ cdp })
await editor.edit({ url: 'app.js', oldString: 'DEBUG=false', newString: 'DEBUG=true' }); await editor.edit({ url: 'app.js', oldString: 'DEBUG=false', newString: 'DEBUG=true' })
// Inspect CSS like DevTools Styles panel // Inspect CSS like DevTools Styles panel
const styles = await getStylesForLocator({ locator: page.locator('.btn'), cdp }); const styles = await getStylesForLocator({ locator: page.locator('.btn'), cdp })
// Intercept network requests for API reverse-engineering // Intercept network requests for API reverse-engineering
page.on('response', async res => { page.on('response', async (res) => {
if (res.url().includes('/api/')) state.responses.push(await res.json()); if (res.url().includes('/api/')) state.responses.push(await res.json())
}); })
// Find React component source locations // Find React component source locations
const source = await getReactSource({ locator: page.locator('aria-ref=e5') }); const source = await getReactSource({ locator: page.locator('aria-ref=e5') })
// => { fileName: 'Button.tsx', lineNumber: 42 } // => { fileName: 'Button.tsx', lineNumber: 42 }
``` ```
**User Collaboration Features:** **User Collaboration Features:**
- **Right-click → "Copy Playwriter Element Reference"** - Pin any element and reference it as `globalThis.playwriterPinnedElem1` in your automation code - **Right-click → "Copy Playwriter Element Reference"** - Pin any element and reference it as `globalThis.playwriterPinnedElem1` in your automation code
- **Vimium-style visual labels** - `screenshotWithAccessibilityLabels()` captures screenshots with clickable labels on all interactive elements - **Vimium-style visual labels** - `screenshotWithAccessibilityLabels()` captures screenshots with clickable labels on all interactive elements
- **Tab group organization** - Connected tabs are grouped together with a green "playwriter" label - **Tab group organization** - Connected tabs are grouped together with a green "playwriter" label
- **Bypass automation detection** - Disconnect the extension temporarily to pass bot detection (e.g., Google login), then reconnect - **Bypass automation detection** - Disconnect the extension temporarily to pass bot detection (e.g., Google login), then reconnect
**Summary:** **Summary:**
| Feature | Claude's Extension | Playwriter | | Feature | Claude's Extension | Playwriter |
|---------|-------------------|------------| | ----------------------- | ------------------ | ---------------------------- |
| Windows WSL | No | Yes | | Windows WSL | No | Yes |
| Works with any agent | No (Claude only) | Yes (any MCP client) | | Works with any agent | No (Claude only) | Yes (any MCP client) |
| Context method | Screenshots | A11y snapshots (90% smaller) | | Context method | Screenshots | A11y snapshots (90% smaller) |
| Full Playwright API | No | Yes | | Full Playwright API | No | Yes |
| Debugger (breakpoints) | No | Yes | | Debugger (breakpoints) | No | Yes |
| Live code editing | No | Yes | | Live code editing | No | Yes |
| CSS inspection | No | Yes | | CSS inspection | No | Yes |
| Network interception | Limited | Full | | Network interception | Limited | Full |
| React source finding | No | Yes | | React source finding | No | Yes |
| Right-click pin element | No | Yes | | Right-click pin element | No | Yes |
| Raw CDP access | No | Yes | | Raw CDP access | No | Yes |
## Architecture ## Architecture
@@ -346,11 +341,7 @@ Configure your MCP client with the host and token. You can pass them as CLI argu
"mcpServers": { "mcpServers": {
"playwriter": { "playwriter": {
"command": "npx", "command": "npx",
"args": [ "args": ["playwriter@latest", "--host", "host.docker.internal", "--token", "<secret>"]
"playwriter@latest",
"--host", "host.docker.internal",
"--token", "<secret>"
]
} }
} }
} }
@@ -377,7 +368,7 @@ Use `host.docker.internal` for devcontainers, or your host's IP for VMs/SSH.
## Known Issues ## Known Issues
- If all pages urls return `about:blank` in every MCP session restart your Chrome browser. This seems to be a Chrome bug that sometimes happen. It is some hidden state in `chrome.debugger` Extensions API. Restarting the extension worker does not fix it. - If all pages urls return `about:blank` in every MCP session restart your Chrome browser. This seems to be a Chrome bug that sometimes happen. It is some hidden state in `chrome.debugger` Extensions API. Restarting the extension worker does not fix it.
- When connecting the MCP to a page, the browser may switch to light mode. This happens because Playwright, via CDP, automatically sends an "emulate media" command on start. If you'd like to see this behavior changed, you can upvote the related issue [here](https://github.com/microsoft/playwright/issues/37627). - When connecting the MCP to a page, the browser may switch to light mode. This happens because Playwright, via CDP, automatically sends an "emulate media" command on start. If you'd like to see this behavior changed, you can upvote the related issue [here](https://github.com/microsoft/playwright/issues/37627).
## Security ## Security