@playwright-repl/mcp 0.18.1 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # @playwright-repl/mcp
2
2
 
3
- MCP server that lets AI agents (Claude Desktop, Claude Code, or any MCP client) control your real Chrome browser through the **Dramaturg** Chrome extension.
3
+ MCP server that lets AI agents (Claude Desktop, Claude Code, or any MCP client) automate a browser using playwright-repl keyword commands.
4
+
5
+ Two modes:
6
+ - **Bridge mode** (default) — controls your real Chrome browser through the **Dramaturg** Chrome extension
7
+ - **Standalone mode** (`--standalone`) — launches its own browser via Playwright, no extension needed
4
8
 
5
9
  ## Why
6
10
 
@@ -31,6 +35,8 @@ It consists of two parts working together: **Dramaturg** (a Chrome extension tha
31
35
 
32
36
  ## Architecture
33
37
 
38
+ ### Bridge mode (default)
39
+
34
40
  ```
35
41
  Claude Desktop / Claude Code (or any MCP client)
36
42
  ↕ MCP (stdio)
@@ -41,6 +47,18 @@ Chrome extension (offscreen document → service worker)
41
47
  Playwright running in your real Chrome session
42
48
  ```
43
49
 
50
+ ### Standalone mode (`--standalone`)
51
+
52
+ ```
53
+ Claude Desktop / Claude Code (or any MCP client)
54
+ ↕ MCP (stdio)
55
+ playwright-repl MCP server
56
+ ↕ Engine.run() (in-process)
57
+ Playwright BrowserServerBackend
58
+ ↕ CDP
59
+ Browser (launched by Playwright)
60
+ ```
61
+
44
62
  ## Setup
45
63
 
46
64
  ### 1. Install the MCP server
@@ -49,12 +67,23 @@ Playwright running in your real Chrome session
49
67
  npm install -g @playwright-repl/mcp
50
68
  ```
51
69
 
52
- ### 2. Install Dramaturg (Chrome extension)
70
+ ### 2. Choose a mode
71
+
72
+ #### Bridge mode (default) — use your real Chrome session
73
+
74
+ Install Dramaturg (Chrome extension):
53
75
 
54
76
  Load `packages/extension/dist/` as an unpacked extension in Chrome (`chrome://extensions` → Enable Developer mode → Load unpacked).
55
77
 
56
78
  Or install from the [Chrome Web Store](https://chromewebstore.google.com/detail/dramaturg/ppbkmncnmjkfppilnmplpokdfagobipa).
57
79
 
80
+ #### Standalone mode — no extension needed
81
+
82
+ Add `--standalone` to the MCP server command. The server launches its own browser via Playwright.
83
+
84
+ - Default: headless. Add `--headed` to show the browser window.
85
+ - Only `.pw` keyword commands are supported (no raw Playwright API or JavaScript expressions). Use `run-code` or `eval` keywords within `.pw` mode for Playwright API / JS.
86
+
58
87
  ### 3. Configure your MCP client
59
88
 
60
89
  **Claude Desktop** — add to `claude_desktop_config.json`:
@@ -62,6 +91,8 @@ Or install from the [Chrome Web Store](https://chromewebstore.google.com/detail/
62
91
  - Windows: `%APPDATA%\Claude\claude_desktop_config.json`
63
92
  - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
64
93
 
94
+ Bridge mode:
95
+
65
96
  ```json
66
97
  {
67
98
  "mcpServers": {
@@ -72,17 +103,36 @@ Or install from the [Chrome Web Store](https://chromewebstore.google.com/detail/
72
103
  }
73
104
  ```
74
105
 
106
+ Standalone mode:
107
+
108
+ ```json
109
+ {
110
+ "mcpServers": {
111
+ "playwright-repl": {
112
+ "command": "playwright-repl-mcp",
113
+ "args": ["--standalone", "--headed"]
114
+ }
115
+ }
116
+ }
117
+ ```
118
+
75
119
  Restart Claude Desktop after saving.
76
120
 
77
121
  **Claude Code** — run once in a terminal:
78
122
 
79
123
  ```bash
124
+ # Bridge mode
80
125
  claude mcp add playwright-repl playwright-repl-mcp
126
+
127
+ # Standalone mode
128
+ claude mcp add playwright-repl playwright-repl-mcp -- --standalone --headed
81
129
  ```
82
130
 
83
131
  ### 4. Connect
84
132
 
85
- The extension connects to the MCP server automatically — no need to open the side panel. Just make sure Chrome is running with the Dramaturg extension installed.
133
+ **Bridge mode:** The extension connects to the MCP server automatically — no need to open the side panel. Just make sure Chrome is running with the Dramaturg extension installed.
134
+
135
+ **Standalone mode:** The browser launches automatically on the first command. No extension needed.
86
136
 
87
137
  ## Dramaturg — The Extension
88
138
 
@@ -113,10 +163,10 @@ When the extension is connected, the MCP server logs `Extension connected` to st
113
163
 
114
164
  ## Tool: `run_command`
115
165
 
116
- One tool, two input modes:
117
-
118
166
  ### Keyword commands (`.pw` syntax)
119
167
 
168
+ Both modes support `.pw` keyword commands:
169
+
120
170
  ```
121
171
  snapshot # accessibility tree — always start here
122
172
  goto https://example.com # navigate
@@ -125,13 +175,14 @@ fill "Email" user@example.com # fill a form field
125
175
  press Enter # key press
126
176
  verify-text Welcome # assert text is visible
127
177
  screenshot # capture page (returned as image to AI)
128
- scroll-down # scroll
129
178
  check "Remember me" # check a checkbox
130
179
  select "Country" "United States" # select dropdown option
131
180
  localstorage-list # list localStorage
132
181
  ```
133
182
 
134
- ### Playwright API / JavaScript
183
+ ### Playwright API / JavaScript (bridge mode only)
184
+
185
+ In bridge mode, `run_command` also accepts raw Playwright expressions and JavaScript:
135
186
 
136
187
  ```
137
188
  await page.url()
@@ -142,13 +193,16 @@ await page.evaluate(() => document.title)
142
193
  await page.evaluate(() => document.querySelectorAll('a').length)
143
194
  ```
144
195
 
196
+ > In standalone mode, use the `run-code` or `eval` keywords to run Playwright API / JavaScript:
197
+ > `run-code await page.url()` or `eval document.title`.
198
+
145
199
  ## Tool: `run_script`
146
200
 
147
- Batch execution for multi-line scripts. Two language modes:
201
+ Batch execution for multi-line scripts.
148
202
 
149
203
  ### Keyword script (`language="pw"`)
150
204
 
151
- Splits by line, runs each command sequentially, returns ✓/✗ per line:
205
+ Splits by line, runs each command sequentially, returns ✓/✗ per line. Lines starting with `#` are skipped. Stops on first error.
152
206
 
153
207
  ```
154
208
  goto https://demo.playwright.dev/todomvc/
@@ -157,7 +211,9 @@ press Enter
157
211
  verify-text "Buy groceries"
158
212
  ```
159
213
 
160
- ### JavaScript (`language="javascript"`)
214
+ > In standalone mode, `run_script` only accepts `.pw` keyword scripts (no `language` parameter needed).
215
+
216
+ ### JavaScript (`language="javascript"`) — bridge mode only
161
217
 
162
218
  Runs the entire block as one evaluation — use for Playwright API with assertions:
163
219
 
@@ -168,20 +224,6 @@ await page.keyboard.press('Enter');
168
224
  await expect(page.getByText('Buy groceries')).toBeVisible();
169
225
  ```
170
226
 
171
- ## Prompt: `generate-test`
172
-
173
- A prompt template that guides the AI to generate a passing Playwright test from a described scenario.
174
-
175
- **Args:**
176
- - `steps` (required) — describe the test scenario, e.g. "log in with email/password, verify the dashboard loads". Also accepts pasted `.pw` scripts.
177
- - `url` (optional) — URL to navigate to first.
178
-
179
- **In Claude Desktop:** click "+" → select `generate-test` → fill in the form → send.
180
-
181
- **In Claude Code:** `/mcp__playwright-repl__generate-test`
182
-
183
- The AI navigates the page, takes snapshots, writes Playwright assertions, runs them via `run_script(language="javascript")`, and iterates until all pass.
184
-
185
227
  ## AI Agents
186
228
 
187
229
  The MCP package includes four ready-to-use AI agents in `packages/mcp/agents/`:
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Bridge runner — connects to Chrome via the Dramaturg extension over WebSocket.
3
+ */
4
+ import type { RunnerModule, SnapshotCache } from './types.js';
5
+ export declare const descriptions: {
6
+ readonly runCommandInput: "A keyword command ('snapshot', 'goto https://example.com', 'click Submit', 'fill \"Email\" user@example.com') or a Playwright expression ('await page.url()')";
7
+ readonly runCommand: "Run a command in the connected Chrome browser. Supports two input modes:\n\n1. KEYWORD (.pw) — playwright-repl commands:\n snapshot, goto <url>, click <text>, fill <label> <value>, press <key>,\n verify-text <text>, verify-no-text <text>, screenshot,\n check <label>, select <label> <value>, localstorage-list, localstorage-clear\n\n2. PLAYWRIGHT — Playwright API (page.* / crxApp.*):\n await page.url(), await page.title(),\n await page.locator('button').count(),\n await page.evaluate(() => document.title)\n\nUpdate commands (click, fill, goto, press, hover, select, check, uncheck, etc.) automatically include a snapshot of the page after the action. You do NOT need to call snapshot separately after these commands.\n\nUse snapshot only for initial exploration or after read-only commands. Use screenshot to visually verify the current state.\n\nIMPORTANT: Before writing .pw commands, run 'help' to get the full list of available commands. Only use commands that appear in the help output. Do not invent commands.";
8
+ readonly runScript: "Run a multi-line script, returning combined pass/fail results.\nUseful for replaying a known script without per-step round trips.\nPrefer run_command for AI-driven exploration where you need to observe and adapt after each step.\n\nlanguage='pw': each line is a .pw keyword command, run sequentially. Lines starting with # are skipped. Stops on first error.\nlanguage='javascript': the entire script is run as a single JavaScript/Playwright block.\n\nIMPORTANT: Only use commands listed by 'help'. Run run_command('help') first if unsure which commands are available.";
9
+ readonly scriptOnly: false;
10
+ };
11
+ export declare function createBridgeRunner(argv: string[], snapshotCache: SnapshotCache): Promise<RunnerModule>;
12
+ //# sourceMappingURL=bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAG9D,eAAO,MAAM,YAAY;;;;;CAgCf,CAAC;AAEX,wBAAsB,kBAAkB,CACpC,IAAI,EAAE,MAAM,EAAE,EACd,aAAa,EAAE,aAAa,GAC7B,OAAO,CAAC,YAAY,CAAC,CA0DvB"}
package/dist/bridge.js ADDED
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Bridge runner — connects to Chrome via the Dramaturg extension over WebSocket.
3
+ */
4
+ import { BridgeServer, UPDATE_COMMANDS, parseInput } from '@playwright-repl/core';
5
+ import { logEvent } from './logger.js';
6
+ export const descriptions = {
7
+ runCommandInput: `A keyword command ('snapshot', 'goto https://example.com', 'click Submit', \
8
+ 'fill "Email" user@example.com') or a Playwright expression ('await page.url()')`,
9
+ runCommand: `Run a command in the connected Chrome browser. Supports two input modes:
10
+
11
+ 1. KEYWORD (.pw) — playwright-repl commands:
12
+ snapshot, goto <url>, click <text>, fill <label> <value>, press <key>,
13
+ verify-text <text>, verify-no-text <text>, screenshot,
14
+ check <label>, select <label> <value>, localstorage-list, localstorage-clear
15
+
16
+ 2. PLAYWRIGHT — Playwright API (page.* / crxApp.*):
17
+ await page.url(), await page.title(),
18
+ await page.locator('button').count(),
19
+ await page.evaluate(() => document.title)
20
+
21
+ Update commands (click, fill, goto, press, hover, select, check, uncheck, etc.) automatically include a snapshot of the page after the action. You do NOT need to call snapshot separately after these commands.
22
+
23
+ Use snapshot only for initial exploration or after read-only commands. Use screenshot to visually verify the current state.
24
+
25
+ IMPORTANT: Before writing .pw commands, run 'help' to get the full list of available commands. Only use commands that appear in the help output. Do not invent commands.`,
26
+ runScript: `Run a multi-line script, returning combined pass/fail results.
27
+ Useful for replaying a known script without per-step round trips.
28
+ Prefer run_command for AI-driven exploration where you need to observe and adapt after each step.
29
+
30
+ language='pw': each line is a .pw keyword command, run sequentially. Lines starting with # are skipped. Stops on first error.
31
+ language='javascript': the entire script is run as a single JavaScript/Playwright block.
32
+
33
+ IMPORTANT: Only use commands listed by 'help'. Run run_command('help') first if unsure which commands are available.`,
34
+ scriptOnly: false,
35
+ };
36
+ export async function createBridgeRunner(argv, snapshotCache) {
37
+ const portIdx = argv.indexOf('--port');
38
+ const port = portIdx !== -1
39
+ ? parseInt(argv[portIdx + 1])
40
+ : (process.env.BRIDGE_PORT ? parseInt(process.env.BRIDGE_PORT) : 9876);
41
+ const srv = new BridgeServer();
42
+ try {
43
+ await srv.start(port);
44
+ }
45
+ catch (err) {
46
+ if (err?.code === 'EADDRINUSE') {
47
+ console.error(`Error: port ${port} is already in use. Another playwright-repl bridge or MCP inspector may be running. Stop it and restart Claude Desktop.`);
48
+ process.exit(1);
49
+ }
50
+ throw err;
51
+ }
52
+ console.error(`playwright-repl bridge listening on ws://localhost:${port}`);
53
+ logEvent(`Bridge listening on ws://localhost:${port}`);
54
+ srv.onConnect(() => { console.error('Extension connected'); logEvent('Extension connected'); });
55
+ srv.onDisconnect(() => { console.error('Extension disconnected'); logEvent('Extension disconnected'); });
56
+ return {
57
+ descriptions,
58
+ runner: {
59
+ async runCommand(command) {
60
+ if (!srv.connected) {
61
+ return { text: 'Browser not connected. Open Chrome with the playwright-repl extension — it connects automatically.', isError: true };
62
+ }
63
+ // Determine command name for snapshot logic
64
+ const parsed = parseInput(command);
65
+ const cmdName = parsed?._[0];
66
+ const isUpdate = cmdName !== undefined && UPDATE_COMMANDS.has(cmdName);
67
+ // Request snapshot in the same round-trip for update commands
68
+ const result = await srv.run(command, isUpdate ? { includeSnapshot: true } : undefined);
69
+ if (result.isError)
70
+ return result;
71
+ // Cache snapshot (from explicit snapshot command or appended by handler)
72
+ if (result.text) {
73
+ const snapMatch = result.text.match(/### Snapshot\n([\s\S]+)$/);
74
+ if (snapMatch) {
75
+ snapshotCache.value = { url: '', snapshotString: snapMatch[1].trim() };
76
+ }
77
+ else if (cmdName === 'snapshot') {
78
+ snapshotCache.value = { url: '', snapshotString: result.text.trim() };
79
+ }
80
+ }
81
+ return result;
82
+ },
83
+ async runScript(script, language) {
84
+ if (!srv.connected) {
85
+ return { text: 'Browser not connected. Open Chrome with the playwright-repl extension — it connects automatically.', isError: true };
86
+ }
87
+ return srv.runScript(script, language);
88
+ },
89
+ },
90
+ };
91
+ }
92
+ //# sourceMappingURL=bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.js","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -2,71 +2,44 @@
2
2
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
3
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
4
  import { z } from 'zod';
5
- import { BridgeServer, COMMANDS, CATEGORIES, refToLocator } from '@playwright-repl/core';
5
+ import { COMMANDS, CATEGORIES, refToLocator } from '@playwright-repl/core';
6
6
  import pkg from '../package.json' with { type: 'json' };
7
+ import { createBridgeRunner } from './bridge.js';
8
+ import { createStandaloneRunner } from './standalone.js';
9
+ import { logStartup, logEvent, logToolCall, logToolResult, logError, LOG_FILE } from './logger.js';
7
10
  const argv = process.argv.slice(2);
8
- const portIdx = argv.indexOf('--port');
9
- const port = portIdx !== -1
10
- ? parseInt(argv[portIdx + 1])
11
- : (process.env.BRIDGE_PORT ? parseInt(process.env.BRIDGE_PORT) : 9876);
12
- const srv = new BridgeServer();
13
- let lastSnapshot = null;
14
- try {
15
- await srv.start(port);
16
- }
17
- catch (err) {
18
- if (err?.code === 'EADDRINUSE') {
19
- console.error(`Error: port ${port} is already in use. Another playwright-repl bridge or MCP inspector may be running. Stop it and restart Claude Desktop.`);
20
- process.exit(1);
21
- }
22
- throw err;
23
- }
24
- console.error(`playwright-repl bridge listening on ws://localhost:${port}`);
25
- srv.onConnect(() => console.error('Extension connected'));
26
- srv.onDisconnect(() => console.error('Extension disconnected'));
27
- const RUN_COMMAND_INPUT_DESCRIPTION = `\
28
- A keyword command ('snapshot', 'goto https://example.com', 'click Submit', \
29
- 'fill "Email" user@example.com'), a Playwright expression \
30
- ('await page.url()'), or a JavaScript expression ('document.title')`;
31
- const RUN_COMMAND_DESCRIPTION = `\
32
- Run a command in the connected Chrome browser. Supports three input modes:
33
-
34
- 1. KEYWORD (.pw) — playwright-repl commands:
35
- snapshot, goto <url>, click <text>, fill <label> <value>, press <key>,
36
- verify-text <text>, verify-no-text <text>, screenshot, scroll-down,
37
- check <label>, select <label> <value>, localstorage-list, localstorage-clear
38
-
39
- 2. PLAYWRIGHT — Playwright API (page.* / crxApp.*):
40
- await page.url(), await page.title(),
41
- await page.locator('button').count()
42
-
43
- 3. JAVASCRIPT — any JS expression evaluated in the browser:
44
- document.title, window.location.href,
45
- document.querySelectorAll('a').length
46
-
47
- Use snapshot to understand the page structure before interacting. Use screenshot to visually verify the current state.
48
-
49
- IMPORTANT: Before writing .pw commands, run 'help' to get the full list of available commands. Only use commands that appear in the help output. Do not invent commands.`;
11
+ const standalone = argv.includes('--standalone');
12
+ const headed = argv.includes('--headed');
13
+ const snapshotCache = { value: null };
14
+ // ─── Create runner ───────────────────────────────────────────────────────────
15
+ const { runner, descriptions } = standalone
16
+ ? createStandaloneRunner(headed, snapshotCache)
17
+ : await createBridgeRunner(argv, snapshotCache);
18
+ logStartup(standalone ? 'standalone' : 'bridge', `log → ${LOG_FILE}`);
19
+ // ─── MCP server ──────────────────────────────────────────────────────────────
50
20
  const server = new McpServer({ name: 'playwright-repl', version: pkg.version });
51
21
  server.registerTool('run_command', {
52
- description: RUN_COMMAND_DESCRIPTION,
22
+ description: descriptions.runCommand,
53
23
  inputSchema: {
54
- command: z.string().describe(RUN_COMMAND_INPUT_DESCRIPTION),
24
+ command: z.string().describe(descriptions.runCommandInput),
55
25
  },
56
26
  }, async ({ command }) => {
27
+ const start = Date.now();
28
+ logToolCall('run_command', { command });
57
29
  const trimmed = command.trim().toLowerCase();
58
30
  if (trimmed === 'help') {
59
31
  const lines = Object.entries(CATEGORIES)
60
32
  .map(([cat, cmds]) => ` ${cat}: ${cmds.join(', ')}`)
61
33
  .join('\n');
34
+ logToolResult('run_command', false, 'help', Date.now() - start);
62
35
  return { content: [{ type: 'text', text: `Available commands:\n${lines}\n\nType "help <command>" for details.` }] };
63
36
  }
64
37
  if (trimmed.startsWith('locator ')) {
65
38
  const ref = command.trim().slice(8).trim();
66
- if (!lastSnapshot) {
39
+ if (!snapshotCache.value) {
67
40
  return { content: [{ type: 'text', text: 'No snapshot cached. Run "snapshot" first.' }], isError: true };
68
41
  }
69
- const locator = refToLocator(lastSnapshot.snapshotString, ref);
42
+ const locator = refToLocator(snapshotCache.value.snapshotString, ref);
70
43
  if (!locator) {
71
44
  return { content: [{ type: 'text', text: `Ref "${ref}" not found in last snapshot. Run "snapshot" to refresh.` }], isError: true };
72
45
  }
@@ -88,95 +61,55 @@ server.registerTool('run_command', {
88
61
  }
89
62
  return { content: [{ type: 'text', text: parts.join('\n') }] };
90
63
  }
91
- if (!srv.connected) {
64
+ try {
65
+ const result = await runner.runCommand(command);
66
+ logToolResult('run_command', !!result.isError, result.text, Date.now() - start);
67
+ if (result.image) {
68
+ const [header, data] = result.image.split(',');
69
+ const mimeType = (header.match(/data:(.*);base64/) ?? [])[1] ?? 'image/png';
70
+ return { content: [{ type: 'image', data, mimeType }] };
71
+ }
92
72
  return {
93
- content: [{ type: 'text', text: 'Browser not connected. Open Chrome with the playwright-repl extension — it connects automatically.' }],
94
- isError: true,
73
+ content: [{ type: 'text', text: result.text || 'Done' }],
74
+ isError: result.isError,
95
75
  };
96
76
  }
97
- const result = await srv.run(command);
98
- // Cache snapshot for locator command
99
- // Bridge mode: snapshot returns raw YAML (no ### headers)
100
- if (trimmed.startsWith('snapshot') && result.text && !result.isError) {
101
- lastSnapshot = { url: '', snapshotString: result.text.trim() };
77
+ catch (err) {
78
+ logError('run_command', err);
79
+ throw err;
102
80
  }
103
- if (result.image) {
104
- const [header, data] = result.image.split(',');
105
- const mimeType = (header.match(/data:(.*);base64/) ?? [])[1] ?? 'image/png';
106
- return { content: [{ type: 'image', data, mimeType }] };
107
- }
108
- return {
109
- content: [{ type: 'text', text: result.text || 'Done' }],
110
- isError: result.isError,
111
- };
112
81
  });
113
- const RUN_SCRIPT_DESCRIPTION = `\
114
- Run a multi-line script, returning combined pass/fail results.
115
- Useful for replaying a known script without per-step round trips.
116
- Prefer run_command for AI-driven exploration where you need to observe and adapt after each step.
117
-
118
- language='pw': each line is a .pw keyword command, run sequentially. Lines starting with # are skipped. Stops on first error.
119
- language='javascript': the entire script is run as a single JavaScript/Playwright block.
120
-
121
- IMPORTANT: Only use commands listed by 'help'. Run run_command('help') first if unsure which commands are available.`;
122
82
  server.registerTool('run_script', {
123
- description: RUN_SCRIPT_DESCRIPTION,
124
- inputSchema: {
125
- script: z.string().describe('The script to execute'),
126
- language: z.enum(['pw', 'javascript']).describe("'pw' for keyword commands (one per line), 'javascript' for a JS/Playwright block"),
127
- },
128
- }, async ({ script, language }) => {
129
- if (!srv.connected) {
83
+ description: descriptions.runScript,
84
+ inputSchema: descriptions.scriptOnly
85
+ ? { script: z.string().describe('The .pw keyword script to execute (one command per line)') }
86
+ : {
87
+ script: z.string().describe('The script to execute'),
88
+ language: z.enum(['pw', 'javascript']).describe("'pw' for keyword commands (one per line), 'javascript' for a JS/Playwright block"),
89
+ },
90
+ }, async (params) => {
91
+ const start = Date.now();
92
+ const script = params.script;
93
+ const language = params.language || 'pw';
94
+ logToolCall('run_script', { language, script });
95
+ try {
96
+ const result = await runner.runScript(script, language);
97
+ logToolResult('run_script', !!result.isError, result.text, Date.now() - start);
130
98
  return {
131
- content: [{ type: 'text', text: 'Browser not connected. Open Chrome with the playwright-repl extension — it connects automatically.' }],
132
- isError: true,
99
+ content: [{ type: 'text', text: result.text || 'Done' }],
100
+ isError: result.isError,
133
101
  };
134
102
  }
135
- const result = await srv.runScript(script, language);
136
- return {
137
- content: [{ type: 'text', text: result.text || 'Done' }],
138
- isError: result.isError,
139
- };
103
+ catch (err) {
104
+ logError('run_script', err);
105
+ throw err;
106
+ }
140
107
  });
141
- const GENERATE_TEST_PROMPT = (steps, url) => `\
142
- Generate a passing Playwright test for the following scenario:
143
- ${steps}
144
-
145
- Workflow:
146
- 0. Run run_command('help') to see all available keyword commands. Only use commands from this list — do not invent commands.
147
- 1. ${url ? `Navigate to ${url} using run_command('goto ${url}').` : 'Navigate to the target URL using run_command.'}
148
- 2. Take a snapshot using run_command('snapshot') to understand the page structure.
149
- 3. Interact with the page as needed (click, fill, press) using run_command.
150
- 4. After each interaction, take another snapshot to verify the state before asserting.
151
- 5. Write assertions using \`expect\`.
152
-
153
- Example pattern:
154
- await page.goto('https://example.com');
155
- await expect(page).toHaveTitle('Example', { exact: true });
156
- await expect(page.getByRole('heading', { name: 'Welcome' })).toBeVisible();
157
- await page.getByRole('link', { name: 'Get started', exact: true }).click();
158
- await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible();
159
-
160
- Code constraints:
161
- - Use only \`page\` and \`expect\` — available as globals, do NOT import them
162
- - Plain \`await\` statements only — no \`import\`, no \`test()\` wrapper, no \`describe()\`
163
- - Use \`exact: true\` when a locator text might match multiple elements
164
-
165
- Once you have the code, run it with run_script(language="javascript").
166
- If any assertion fails, read the error, fix the code, and run again until all pass.
167
- Show the final passing code.`;
168
- server.registerPrompt('generate-test', {
169
- description: 'Generate a passing Playwright test from a described scenario',
170
- argsSchema: {
171
- steps: z.string().describe('Describe the test scenario, e.g. "log in with email/password, verify the dashboard loads"'),
172
- url: z.string().optional().describe('URL to navigate to first (optional)'),
173
- },
174
- }, ({ steps, url }) => ({
175
- messages: [{
176
- role: 'user',
177
- content: { type: 'text', text: GENERATE_TEST_PROMPT(steps, url) },
178
- }],
179
- }));
108
+ server.server.oninitialized = () => {
109
+ const client = server.server.getClientVersion();
110
+ if (client)
111
+ logEvent(`Client: ${client.name} ${client.version}`);
112
+ };
180
113
  const transport = new StdioServerTransport();
181
114
  await server.connect(transport);
182
115
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * File-based logger for the MCP server.
3
+ *
4
+ * Writes to ~/.playwright-repl/mcp.log so tool calls are visible
5
+ * regardless of whether the host (Claude Desktop, Claude Code, etc.)
6
+ * captures stderr.
7
+ */
8
+ declare const LOG_FILE: string;
9
+ export declare function logStartup(mode: string, detail: string): void;
10
+ export declare function logEvent(event: string): void;
11
+ export declare function logToolCall(tool: string, input: Record<string, unknown>): void;
12
+ export declare function logToolResult(tool: string, isError: boolean, text: string | undefined, durationMs: number): void;
13
+ export declare function logError(context: string, err: unknown): void;
14
+ export { LOG_FILE };
15
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,QAAA,MAAM,QAAQ,QAA2B,CAAC;AAoC1C,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAE7D;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAE5C;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAQ9E;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAIhH;AAED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI,CAG5D;AAED,OAAO,EAAE,QAAQ,EAAE,CAAC"}
package/dist/logger.js ADDED
@@ -0,0 +1,69 @@
1
+ /**
2
+ * File-based logger for the MCP server.
3
+ *
4
+ * Writes to ~/.playwright-repl/mcp.log so tool calls are visible
5
+ * regardless of whether the host (Claude Desktop, Claude Code, etc.)
6
+ * captures stderr.
7
+ */
8
+ import { mkdirSync, appendFileSync } from 'node:fs';
9
+ import { join } from 'node:path';
10
+ import { homedir } from 'node:os';
11
+ const LOG_DIR = join(homedir(), '.playwright-repl');
12
+ const LOG_FILE = join(LOG_DIR, 'mcp.log');
13
+ /** Maximum characters kept from a result text in the log. */
14
+ const MAX_RESULT_LENGTH = 200;
15
+ let enabled = true;
16
+ try {
17
+ mkdirSync(LOG_DIR, { recursive: true });
18
+ }
19
+ catch {
20
+ enabled = false;
21
+ }
22
+ function ts() {
23
+ const d = new Date();
24
+ const pad = (n) => String(n).padStart(2, '0');
25
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
26
+ }
27
+ function write(level, message) {
28
+ if (!enabled)
29
+ return;
30
+ try {
31
+ appendFileSync(LOG_FILE, `${ts()} [${level}] ${message}\n`);
32
+ }
33
+ catch {
34
+ // Silently ignore — logging should never break the server.
35
+ }
36
+ }
37
+ /** Truncate a string for log readability. */
38
+ function truncate(text, max = MAX_RESULT_LENGTH) {
39
+ if (text.length <= max)
40
+ return text;
41
+ return text.slice(0, max) + `... (${text.length} chars)`;
42
+ }
43
+ // ─── Public API ──────────────────────────────────────────────────────────────
44
+ export function logStartup(mode, detail) {
45
+ write('info', `Server started [${mode}] ${detail}`);
46
+ }
47
+ export function logEvent(event) {
48
+ write('info', event);
49
+ }
50
+ export function logToolCall(tool, input) {
51
+ const args = Object.entries(input)
52
+ .map(([k, v]) => {
53
+ const s = typeof v === 'string' ? v : JSON.stringify(v);
54
+ return `${k}=${truncate(s, 120)}`;
55
+ })
56
+ .join(' ');
57
+ write('info', `→ ${tool}(${args})`);
58
+ }
59
+ export function logToolResult(tool, isError, text, durationMs) {
60
+ const status = isError ? 'ERROR' : 'OK';
61
+ const summary = text ? truncate(text.replace(/\n/g, '\\n')) : '(empty)';
62
+ write('info', `← ${tool} [${status}] ${durationMs}ms ${summary}`);
63
+ }
64
+ export function logError(context, err) {
65
+ const msg = err instanceof Error ? err.message : String(err);
66
+ write('error', `${context}: ${msg}`);
67
+ }
68
+ export { LOG_FILE };
69
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Standalone runner — launches a browser via Engine (in-process Playwright).
3
+ */
4
+ import type { RunnerModule, SnapshotCache } from './types.js';
5
+ export declare const descriptions: {
6
+ readonly runCommandInput: "A keyword command ('snapshot', 'goto https://example.com', 'click Submit', 'fill \"Email\" user@example.com')";
7
+ readonly runCommand: "Run a command in the browser. Supports KEYWORD (.pw) — playwright-repl commands:\n snapshot, goto <url>, click <text>, fill <label> <value>, press <key>,\n verify-text <text>, verify-no-text <text>, screenshot,\n check <label>, select <label> <value>, localstorage-list, localstorage-clear\n\nUpdate commands (click, fill, goto, press, hover, select, check, uncheck, etc.) automatically include a snapshot of the page after the action. You do NOT need to call snapshot separately after these commands.\n\nUse snapshot only for initial exploration or after read-only commands. Use screenshot to visually verify the current state.\n\nIMPORTANT: Before writing .pw commands, run 'help' to get the full list of available commands. Only use commands that appear in the help output. Do not invent commands.";
8
+ readonly runScript: "Run a multi-line .pw keyword script, returning combined pass/fail results.\nEach line is a .pw keyword command, run sequentially. Lines starting with # are skipped. Stops on first error.\nUseful for replaying a known script without per-step round trips.\n\nIMPORTANT: Only use commands listed by 'help'. Run run_command('help') first if unsure which commands are available.";
9
+ readonly scriptOnly: true;
10
+ };
11
+ export declare function createStandaloneRunner(headed: boolean, snapshotCache: SnapshotCache): RunnerModule;
12
+ //# sourceMappingURL=standalone.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standalone.d.ts","sourceRoot":"","sources":["../src/standalone.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAI9D,eAAO,MAAM,YAAY;;;;;CAsBf,CAAC;AAEX,wBAAgB,sBAAsB,CAClC,MAAM,EAAE,OAAO,EACf,aAAa,EAAE,aAAa,GAC7B,YAAY,CA8Dd"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Standalone runner — launches a browser via Engine (in-process Playwright).
3
+ */
4
+ import { Engine, parseInput, resolveArgs, filterResponse } from '@playwright-repl/core';
5
+ const INCLUDE_SNAPSHOT = { includeSnapshot: true };
6
+ export const descriptions = {
7
+ runCommandInput: `A keyword command ('snapshot', 'goto https://example.com', 'click Submit', \
8
+ 'fill "Email" user@example.com')`,
9
+ runCommand: `Run a command in the browser. Supports KEYWORD (.pw) — playwright-repl commands:
10
+ snapshot, goto <url>, click <text>, fill <label> <value>, press <key>,
11
+ verify-text <text>, verify-no-text <text>, screenshot,
12
+ check <label>, select <label> <value>, localstorage-list, localstorage-clear
13
+
14
+ Update commands (click, fill, goto, press, hover, select, check, uncheck, etc.) automatically include a snapshot of the page after the action. You do NOT need to call snapshot separately after these commands.
15
+
16
+ Use snapshot only for initial exploration or after read-only commands. Use screenshot to visually verify the current state.
17
+
18
+ IMPORTANT: Before writing .pw commands, run 'help' to get the full list of available commands. Only use commands that appear in the help output. Do not invent commands.`,
19
+ runScript: `Run a multi-line .pw keyword script, returning combined pass/fail results.
20
+ Each line is a .pw keyword command, run sequentially. Lines starting with # are skipped. Stops on first error.
21
+ Useful for replaying a known script without per-step round trips.
22
+
23
+ IMPORTANT: Only use commands listed by 'help'. Run run_command('help') first if unsure which commands are available.`,
24
+ scriptOnly: true,
25
+ };
26
+ export function createStandaloneRunner(headed, snapshotCache) {
27
+ let engine = null;
28
+ let starting = null;
29
+ function ensureEngine() {
30
+ if (engine)
31
+ return Promise.resolve(engine);
32
+ if (!starting) {
33
+ const e = new Engine();
34
+ starting = e.start({ headed }).then(() => {
35
+ engine = e;
36
+ console.error(`playwright-repl standalone engine started (${headed ? 'headed' : 'headless'})`);
37
+ return e;
38
+ });
39
+ }
40
+ return starting;
41
+ }
42
+ async function runSingleCommand(command) {
43
+ const e = await ensureEngine();
44
+ const args = parseInput(command);
45
+ if (!args)
46
+ return { text: `Unknown command: ${command}`, isError: true };
47
+ const cmdName = args._[0];
48
+ const resolved = resolveArgs(args);
49
+ const result = await e.run(resolved);
50
+ // Cache snapshot for locator command — strip YAML code fences
51
+ if (result.text && !result.isError) {
52
+ const snapshotMatch = result.text.match(/### Snapshot\n([\s\S]*?)(?=\n### |$)/);
53
+ if (snapshotMatch) {
54
+ const raw = snapshotMatch[1].trim();
55
+ const yamlBody = raw.replace(/^```(?:yaml)?\n?/, '').replace(/\n?```$/, '');
56
+ snapshotCache.value = { url: '', snapshotString: yamlBody };
57
+ }
58
+ }
59
+ // Filter verbose Playwright response sections — keep snapshots for MCP
60
+ if (result.text)
61
+ result.text = filterResponse(result.text, cmdName, INCLUDE_SNAPSHOT);
62
+ return result;
63
+ }
64
+ return {
65
+ descriptions,
66
+ runner: {
67
+ runCommand: runSingleCommand,
68
+ async runScript(script, language) {
69
+ if (language === 'javascript') {
70
+ return { text: 'JavaScript mode is not supported in standalone mode. Use language="pw" with run-code or eval keywords.', isError: true };
71
+ }
72
+ const lines = script.split('\n').filter(l => l.trim() && !l.trim().startsWith('#'));
73
+ const results = [];
74
+ for (const line of lines) {
75
+ const result = await runSingleCommand(line.trim());
76
+ const status = result.isError ? '✗' : '✓';
77
+ results.push(`${status} ${line.trim()}${result.isError && result.text ? ` — ${result.text}` : ''}`);
78
+ if (result.isError) {
79
+ return { text: results.join('\n'), isError: true };
80
+ }
81
+ }
82
+ return { text: results.join('\n'), isError: false };
83
+ },
84
+ },
85
+ };
86
+ }
87
+ //# sourceMappingURL=standalone.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standalone.js","sourceRoot":"","sources":["../src/standalone.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAIxF,MAAM,gBAAgB,GAAG,EAAE,eAAe,EAAE,IAAI,EAAW,CAAC;AAE5D,MAAM,CAAC,MAAM,YAAY,GAAG;IACxB,eAAe,EAAE;iCACY;IAE7B,UAAU,EAAE;;;;;;;;;yKASyJ;IAErK,SAAS,EAAE;;;;qHAIsG;IAEjH,UAAU,EAAE,IAAI;CACV,CAAC;AAEX,MAAM,UAAU,sBAAsB,CAClC,MAAe,EACf,aAA4B;IAE5B,IAAI,MAAM,GAAkB,IAAI,CAAC;IACjC,IAAI,QAAQ,GAA2B,IAAI,CAAC;IAE5C,SAAS,YAAY;QACjB,IAAI,MAAM;YAAE,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC3C,IAAI,CAAC,QAAQ,EAAE,CAAC;YACZ,MAAM,CAAC,GAAG,IAAI,MAAM,EAAE,CAAC;YACvB,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE;gBACrC,MAAM,GAAG,CAAC,CAAC;gBACX,OAAO,CAAC,KAAK,CAAC,8CAA8C,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC;gBAC/F,OAAO,CAAC,CAAC;YACb,CAAC,CAAC,CAAC;QACP,CAAC;QACD,OAAO,QAAQ,CAAC;IACpB,CAAC;IAED,KAAK,UAAU,gBAAgB,CAAC,OAAe;QAC3C,MAAM,CAAC,GAAG,MAAM,YAAY,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;QACjC,IAAI,CAAC,IAAI;YAAE,OAAO,EAAE,IAAI,EAAE,oBAAoB,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QACzE,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1B,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QACnC,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAErC,8DAA8D;QAC9D,IAAI,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACjC,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,sCAAsC,CAAC,CAAC;YAChF,IAAI,aAAa,EAAE,CAAC;gBAChB,MAAM,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;gBACpC,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;gBAC5E,aAAa,CAAC,KAAK,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC;YAChE,CAAC;QACL,CAAC;QAED,uEAAuE;QACvE,IAAI,MAAM,CAAC,IAAI;YAAE,MAAM,CAAC,IAAI,GAAG,cAAc,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,gBAAgB,CAAC,CAAC;QACtF,OAAO,MAAM,CAAC;IAClB,CAAC;IAED,OAAO;QACH,YAAY;QACZ,MAAM,EAAE;YACJ,UAAU,EAAE,gBAAgB;YAC5B,KAAK,CAAC,SAAS,CAAC,MAAc,EAAE,QAA6B;gBACzD,IAAI,QAAQ,KAAK,YAAY,EAAE,CAAC;oBAC5B,OAAO,EAAE,IAAI,EAAE,wGAAwG,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;gBAC7I,CAAC;gBACD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;gBACpF,MAAM,OAAO,GAAa,EAAE,CAAC;gBAC7B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;oBACvB,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;oBACnD,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;oBAC1C,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,IAAI,CAAC,IAAI,EAAE,GAAG,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;oBACpG,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;wBACjB,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;oBACvD,CAAC;gBACL,CAAC;gBACD,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;YACxD,CAAC;SACJ;KACJ,CAAC;AACN,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { EngineResult } from '@playwright-repl/core';
2
+ export interface Runner {
3
+ runCommand(command: string): Promise<EngineResult>;
4
+ runScript(script: string, language: 'pw' | 'javascript'): Promise<EngineResult>;
5
+ }
6
+ export interface RunnerDescriptions {
7
+ runCommandInput: string;
8
+ runCommand: string;
9
+ runScript: string;
10
+ /** When true, run_script only accepts 'script' param (no 'language'). */
11
+ scriptOnly: boolean;
12
+ }
13
+ export interface RunnerModule {
14
+ runner: Runner;
15
+ descriptions: RunnerDescriptions;
16
+ }
17
+ export interface SnapshotCache {
18
+ value: {
19
+ url: string;
20
+ snapshotString: string;
21
+ } | null;
22
+ }
23
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,MAAM,WAAW,MAAM;IACnB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IACnD,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,GAAG,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CACnF;AAED,MAAM,WAAW,kBAAkB;IAC/B,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,UAAU,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,kBAAkB,CAAC;CACpC;AAED,MAAM,WAAW,aAAa;IAC1B,KAAK,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CACzD"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playwright-repl/mcp",
3
- "version": "0.18.1",
3
+ "version": "0.20.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "playwright-repl-mcp": "./dist/index.js"
@@ -19,5 +19,9 @@
19
19
  "@modelcontextprotocol/sdk": "^1.10.0",
20
20
  "@playwright-repl/core": "workspace:*",
21
21
  "zod": "^3.24.0"
22
+ },
23
+ "peerDependencies": {
24
+ "playwright": "1.59.0-alpha-2026-02-21",
25
+ "playwright-core": "1.59.0-alpha-2026-02-21"
22
26
  }
23
27
  }