format
This commit is contained in:
@@ -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,15 +122,15 @@ 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
|
||||||
|
|
||||||
@@ -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>"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user