@playwright/mcp 0.0.10 → 0.0.13
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 +1 -4
- package/lib/context.js +26 -14
- package/lib/index.js +2 -2
- package/lib/javascript.js +54 -0
- package/lib/program.js +6 -3
- package/lib/tools/common.js +28 -1
- package/lib/tools/files.js +4 -1
- package/lib/tools/keyboard.js +5 -1
- package/lib/tools/navigate.js +15 -3
- package/lib/tools/screen.js +25 -8
- package/lib/tools/snapshot.js +85 -25
- package/lib/tools/tabs.js +21 -5
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -43,7 +43,7 @@ const urlForWebsites = `vscode:mcp/install?${encodeURIComponent(config)}`;
|
|
|
43
43
|
const urlForGithub = `https://insiders.vscode.dev/redirect?url=${encodeURIComponent(urlForWebsites)}`;
|
|
44
44
|
-->
|
|
45
45
|
|
|
46
|
-
[<img alt="Install in VS Code Insiders" src="https://img.shields.io/badge/VS_Code_Insiders-VS_Code_Insiders?style=flat-square&label=Install%20Server&color=24bfa5">](https://insiders.vscode.dev/redirect?url=vscode-insiders%3Amcp%2Finstall%3F%257B%2522name%2522%253A%2522playwright%2522%252C%2522command%2522%253A%2522npx%2522%252C%2522args%2522%253A%255B%2522-y%2522%252C%2522%2540playwright%252Fmcp%2540latest%2522%255D%257D)
|
|
46
|
+
[<img src="https://img.shields.io/badge/VS_Code-VS_Code?style=flat-square&label=Install%20Server&color=0098FF" alt="Install in VS Code">](https://insiders.vscode.dev/redirect?url=vscode%3Amcp%2Finstall%3F%257B%2522name%2522%253A%2522playwright%2522%252C%2522command%2522%253A%2522npx%2522%252C%2522args%2522%253A%255B%2522-y%2522%252C%2522%2540playwright%252Fmcp%2540latest%2522%255D%257D) [<img alt="Install in VS Code Insiders" src="https://img.shields.io/badge/VS_Code_Insiders-VS_Code_Insiders?style=flat-square&label=Install%20Server&color=24bfa5">](https://insiders.vscode.dev/redirect?url=vscode-insiders%3Amcp%2Finstall%3F%257B%2522name%2522%253A%2522playwright%2522%252C%2522command%2522%253A%2522npx%2522%252C%2522args%2522%253A%255B%2522-y%2522%252C%2522%2540playwright%252Fmcp%2540latest%2522%255D%257D)
|
|
47
47
|
|
|
48
48
|
Alternatively, you can install the Playwright MCP server using the VS Code CLI:
|
|
49
49
|
|
|
@@ -323,6 +323,3 @@ server.connect(transport);
|
|
|
323
323
|
- **browser_install**
|
|
324
324
|
- Description: Install the browser specified in the config. Call this if you get an error about the browser not being installed.
|
|
325
325
|
- Parameters: None
|
|
326
|
-
|
|
327
|
-
### Vision Mode
|
|
328
|
-
|
package/lib/context.js
CHANGED
|
@@ -52,6 +52,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
52
52
|
};
|
|
53
53
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
54
54
|
exports.Context = void 0;
|
|
55
|
+
exports.generateLocator = generateLocator;
|
|
55
56
|
const playwright = __importStar(require("playwright"));
|
|
56
57
|
const yaml_1 = __importDefault(require("yaml"));
|
|
57
58
|
const utils_1 = require("./tools/utils");
|
|
@@ -69,7 +70,7 @@ class Context {
|
|
|
69
70
|
}
|
|
70
71
|
currentTab() {
|
|
71
72
|
if (!this._currentTab)
|
|
72
|
-
throw new Error('
|
|
73
|
+
throw new Error('No current snapshot available. Capture a snapshot of navigate to a new location first.');
|
|
73
74
|
return this._currentTab;
|
|
74
75
|
}
|
|
75
76
|
async newTab() {
|
|
@@ -90,8 +91,8 @@ class Context {
|
|
|
90
91
|
}
|
|
91
92
|
async listTabs() {
|
|
92
93
|
if (!this._tabs.length)
|
|
93
|
-
return 'No tabs open';
|
|
94
|
-
const lines = ['Open tabs
|
|
94
|
+
return '### No tabs open';
|
|
95
|
+
const lines = ['### Open tabs'];
|
|
95
96
|
for (let i = 0; i < this._tabs.length; i++) {
|
|
96
97
|
const tab = this._tabs[i];
|
|
97
98
|
const title = await tab.page.title();
|
|
@@ -206,24 +207,36 @@ class Tab {
|
|
|
206
207
|
await this.page.waitForLoadState('load', { timeout: 5000 }).catch(() => { });
|
|
207
208
|
}
|
|
208
209
|
async run(callback, options) {
|
|
210
|
+
let runResult;
|
|
209
211
|
try {
|
|
210
212
|
if (!options?.noClearFileChooser)
|
|
211
213
|
this._fileChooser = undefined;
|
|
212
214
|
if (options?.waitForCompletion)
|
|
213
|
-
await (0, utils_1.waitForCompletion)(this.page, () => callback(this));
|
|
215
|
+
runResult = await (0, utils_1.waitForCompletion)(this.page, () => callback(this)) ?? undefined;
|
|
214
216
|
else
|
|
215
|
-
await callback(this);
|
|
217
|
+
runResult = await callback(this) ?? undefined;
|
|
216
218
|
}
|
|
217
219
|
finally {
|
|
218
220
|
if (options?.captureSnapshot)
|
|
219
221
|
this._snapshot = await PageSnapshot.create(this.page);
|
|
220
222
|
}
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
+
const result = [];
|
|
224
|
+
result.push(`- Ran code:
|
|
225
|
+
\`\`\`js
|
|
226
|
+
${runResult.code.join('\n')}
|
|
227
|
+
\`\`\`
|
|
228
|
+
`);
|
|
229
|
+
if (this.context.tabs().length > 1)
|
|
230
|
+
result.push(await this.context.listTabs(), '');
|
|
231
|
+
if (this._snapshot) {
|
|
232
|
+
if (this.context.tabs().length > 1)
|
|
233
|
+
result.push('### Current tab');
|
|
234
|
+
result.push(this._snapshot.text({ hasFileChooser: !!this._fileChooser }));
|
|
235
|
+
}
|
|
223
236
|
return {
|
|
224
237
|
content: [{
|
|
225
238
|
type: 'text',
|
|
226
|
-
text:
|
|
239
|
+
text: result.join('\n'),
|
|
227
240
|
}],
|
|
228
241
|
};
|
|
229
242
|
}
|
|
@@ -234,7 +247,7 @@ class Tab {
|
|
|
234
247
|
});
|
|
235
248
|
}
|
|
236
249
|
async runAndWaitWithSnapshot(callback, options) {
|
|
237
|
-
return await this.run(callback, {
|
|
250
|
+
return await this.run(tab => callback(tab.lastSnapshot()), {
|
|
238
251
|
captureSnapshot: true,
|
|
239
252
|
waitForCompletion: true,
|
|
240
253
|
...options,
|
|
@@ -267,11 +280,7 @@ class PageSnapshot {
|
|
|
267
280
|
}
|
|
268
281
|
text(options) {
|
|
269
282
|
const results = [];
|
|
270
|
-
if (options
|
|
271
|
-
results.push(options.status);
|
|
272
|
-
results.push('');
|
|
273
|
-
}
|
|
274
|
-
if (options?.hasFileChooser) {
|
|
283
|
+
if (options.hasFileChooser) {
|
|
275
284
|
results.push('- There is a file chooser visible that requires browser_file_upload to be called');
|
|
276
285
|
results.push('');
|
|
277
286
|
}
|
|
@@ -336,3 +345,6 @@ class PageSnapshot {
|
|
|
336
345
|
return frame.locator(`aria-ref=${ref}`);
|
|
337
346
|
}
|
|
338
347
|
}
|
|
348
|
+
async function generateLocator(locator) {
|
|
349
|
+
return locator._generateLocatorString();
|
|
350
|
+
}
|
package/lib/index.js
CHANGED
|
@@ -31,7 +31,7 @@ const tabs_1 = __importDefault(require("./tools/tabs"));
|
|
|
31
31
|
const screen_1 = __importDefault(require("./tools/screen"));
|
|
32
32
|
const console_1 = require("./resources/console");
|
|
33
33
|
const snapshotTools = [
|
|
34
|
-
...common_1.default,
|
|
34
|
+
...(0, common_1.default)(true),
|
|
35
35
|
...(0, files_1.default)(true),
|
|
36
36
|
...install_1.default,
|
|
37
37
|
...(0, keyboard_1.default)(true),
|
|
@@ -41,7 +41,7 @@ const snapshotTools = [
|
|
|
41
41
|
...(0, tabs_1.default)(true),
|
|
42
42
|
];
|
|
43
43
|
const screenshotTools = [
|
|
44
|
-
...common_1.default,
|
|
44
|
+
...(0, common_1.default)(false),
|
|
45
45
|
...(0, files_1.default)(false),
|
|
46
46
|
...install_1.default,
|
|
47
47
|
...(0, keyboard_1.default)(false),
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) Microsoft Corporation.
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.escapeWithQuotes = escapeWithQuotes;
|
|
19
|
+
exports.quote = quote;
|
|
20
|
+
exports.formatObject = formatObject;
|
|
21
|
+
// adapted from:
|
|
22
|
+
// - https://github.com/microsoft/playwright/blob/76ee48dc9d4034536e3ec5b2c7ce8be3b79418a8/packages/playwright-core/src/utils/isomorphic/stringUtils.ts
|
|
23
|
+
// - https://github.com/microsoft/playwright/blob/76ee48dc9d4034536e3ec5b2c7ce8be3b79418a8/packages/playwright-core/src/server/codegen/javascript.ts
|
|
24
|
+
// NOTE: this function should not be used to escape any selectors.
|
|
25
|
+
function escapeWithQuotes(text, char = '\'') {
|
|
26
|
+
const stringified = JSON.stringify(text);
|
|
27
|
+
const escapedText = stringified.substring(1, stringified.length - 1).replace(/\\"/g, '"');
|
|
28
|
+
if (char === '\'')
|
|
29
|
+
return char + escapedText.replace(/[']/g, '\\\'') + char;
|
|
30
|
+
if (char === '"')
|
|
31
|
+
return char + escapedText.replace(/["]/g, '\\"') + char;
|
|
32
|
+
if (char === '`')
|
|
33
|
+
return char + escapedText.replace(/[`]/g, '`') + char;
|
|
34
|
+
throw new Error('Invalid escape char');
|
|
35
|
+
}
|
|
36
|
+
function quote(text) {
|
|
37
|
+
return escapeWithQuotes(text, '\'');
|
|
38
|
+
}
|
|
39
|
+
function formatObject(value, indent = ' ') {
|
|
40
|
+
if (typeof value === 'string')
|
|
41
|
+
return quote(value);
|
|
42
|
+
if (Array.isArray(value))
|
|
43
|
+
return `[${value.map(o => formatObject(o)).join(', ')}]`;
|
|
44
|
+
if (typeof value === 'object') {
|
|
45
|
+
const keys = Object.keys(value).filter(key => value[key] !== undefined).sort();
|
|
46
|
+
if (!keys.length)
|
|
47
|
+
return '{}';
|
|
48
|
+
const tokens = [];
|
|
49
|
+
for (const key of keys)
|
|
50
|
+
tokens.push(`${key}: ${formatObject(value[key])}`);
|
|
51
|
+
return `{\n${indent}${tokens.join(`,\n${indent}`)}\n}`;
|
|
52
|
+
}
|
|
53
|
+
return String(value);
|
|
54
|
+
}
|
package/lib/program.js
CHANGED
|
@@ -69,7 +69,7 @@ commander_1.program
|
|
|
69
69
|
channel = 'chrome';
|
|
70
70
|
}
|
|
71
71
|
const launchOptions = {
|
|
72
|
-
headless: !!options.headless,
|
|
72
|
+
headless: !!(options.headless ?? (os_1.default.platform() === 'linux' && !process.env.DISPLAY)),
|
|
73
73
|
channel,
|
|
74
74
|
executablePath: options.executablePath,
|
|
75
75
|
};
|
|
@@ -92,11 +92,14 @@ commander_1.program
|
|
|
92
92
|
}
|
|
93
93
|
});
|
|
94
94
|
function setupExitWatchdog(serverList) {
|
|
95
|
-
|
|
95
|
+
const handleExit = async () => {
|
|
96
96
|
setTimeout(() => process.exit(0), 15000);
|
|
97
97
|
await serverList.closeAll();
|
|
98
98
|
process.exit(0);
|
|
99
|
-
}
|
|
99
|
+
};
|
|
100
|
+
process.stdin.on('close', handleExit);
|
|
101
|
+
process.on('SIGINT', handleExit);
|
|
102
|
+
process.on('SIGTERM', handleExit);
|
|
100
103
|
}
|
|
101
104
|
commander_1.program.parse(process.argv);
|
|
102
105
|
async function createUserDataDir(browserName) {
|
package/lib/tools/common.js
CHANGED
|
@@ -56,7 +56,34 @@ const close = {
|
|
|
56
56
|
};
|
|
57
57
|
},
|
|
58
58
|
};
|
|
59
|
-
|
|
59
|
+
const resizeSchema = zod_1.z.object({
|
|
60
|
+
width: zod_1.z.number().describe('Width of the browser window'),
|
|
61
|
+
height: zod_1.z.number().describe('Height of the browser window'),
|
|
62
|
+
});
|
|
63
|
+
const resize = captureSnapshot => ({
|
|
64
|
+
capability: 'core',
|
|
65
|
+
schema: {
|
|
66
|
+
name: 'browser_resize',
|
|
67
|
+
description: 'Resize the browser window',
|
|
68
|
+
inputSchema: (0, zod_to_json_schema_1.zodToJsonSchema)(resizeSchema),
|
|
69
|
+
},
|
|
70
|
+
handle: async (context, params) => {
|
|
71
|
+
const validatedParams = resizeSchema.parse(params);
|
|
72
|
+
const tab = context.currentTab();
|
|
73
|
+
return await tab.run(async (tab) => {
|
|
74
|
+
await tab.page.setViewportSize({ width: validatedParams.width, height: validatedParams.height });
|
|
75
|
+
const code = [
|
|
76
|
+
`// Resize browser window to ${validatedParams.width}x${validatedParams.height}`,
|
|
77
|
+
`await page.setViewportSize({ width: ${validatedParams.width}, height: ${validatedParams.height} });`
|
|
78
|
+
];
|
|
79
|
+
return { code };
|
|
80
|
+
}, {
|
|
81
|
+
captureSnapshot,
|
|
82
|
+
});
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
exports.default = (captureSnapshot) => [
|
|
60
86
|
close,
|
|
61
87
|
wait,
|
|
88
|
+
resize(captureSnapshot)
|
|
62
89
|
];
|
package/lib/tools/files.js
CHANGED
|
@@ -32,8 +32,11 @@ const uploadFile = captureSnapshot => ({
|
|
|
32
32
|
const tab = context.currentTab();
|
|
33
33
|
return await tab.runAndWait(async () => {
|
|
34
34
|
await tab.submitFileChooser(validatedParams.paths);
|
|
35
|
+
const code = [
|
|
36
|
+
`// <internal code to chose files ${validatedParams.paths.join(', ')}`,
|
|
37
|
+
];
|
|
38
|
+
return { code };
|
|
35
39
|
}, {
|
|
36
|
-
status: `Chose files ${validatedParams.paths.join(', ')}`,
|
|
37
40
|
captureSnapshot,
|
|
38
41
|
noClearFileChooser: true,
|
|
39
42
|
});
|
package/lib/tools/keyboard.js
CHANGED
|
@@ -34,8 +34,12 @@ const pressKey = captureSnapshot => ({
|
|
|
34
34
|
const validatedParams = pressKeySchema.parse(params);
|
|
35
35
|
return await context.currentTab().runAndWait(async (tab) => {
|
|
36
36
|
await tab.page.keyboard.press(validatedParams.key);
|
|
37
|
+
const code = [
|
|
38
|
+
`// Press ${validatedParams.key}`,
|
|
39
|
+
`await page.keyboard.press('${validatedParams.key}');`,
|
|
40
|
+
];
|
|
41
|
+
return { code };
|
|
37
42
|
}, {
|
|
38
|
-
status: `Pressed key ${validatedParams.key}`,
|
|
39
43
|
captureSnapshot,
|
|
40
44
|
});
|
|
41
45
|
},
|
package/lib/tools/navigate.js
CHANGED
|
@@ -32,8 +32,12 @@ const navigate = captureSnapshot => ({
|
|
|
32
32
|
const currentTab = await context.ensureTab();
|
|
33
33
|
return await currentTab.run(async (tab) => {
|
|
34
34
|
await tab.navigate(validatedParams.url);
|
|
35
|
+
const code = [
|
|
36
|
+
`// Navigate to ${validatedParams.url}`,
|
|
37
|
+
`await page.goto('${validatedParams.url}');`,
|
|
38
|
+
];
|
|
39
|
+
return { code };
|
|
35
40
|
}, {
|
|
36
|
-
status: `Navigated to ${validatedParams.url}`,
|
|
37
41
|
captureSnapshot,
|
|
38
42
|
});
|
|
39
43
|
},
|
|
@@ -49,8 +53,12 @@ const goBack = snapshot => ({
|
|
|
49
53
|
handle: async (context) => {
|
|
50
54
|
return await context.currentTab().runAndWait(async (tab) => {
|
|
51
55
|
await tab.page.goBack();
|
|
56
|
+
const code = [
|
|
57
|
+
`// Navigate back`,
|
|
58
|
+
`await page.goBack();`,
|
|
59
|
+
];
|
|
60
|
+
return { code };
|
|
52
61
|
}, {
|
|
53
|
-
status: 'Navigated back',
|
|
54
62
|
captureSnapshot: snapshot,
|
|
55
63
|
});
|
|
56
64
|
},
|
|
@@ -66,8 +74,12 @@ const goForward = snapshot => ({
|
|
|
66
74
|
handle: async (context) => {
|
|
67
75
|
return await context.currentTab().runAndWait(async (tab) => {
|
|
68
76
|
await tab.page.goForward();
|
|
77
|
+
const code = [
|
|
78
|
+
`// Navigate forward`,
|
|
79
|
+
`await page.goForward();`,
|
|
80
|
+
];
|
|
81
|
+
return { code };
|
|
69
82
|
}, {
|
|
70
|
-
status: 'Navigated forward',
|
|
71
83
|
captureSnapshot: snapshot,
|
|
72
84
|
});
|
|
73
85
|
},
|
package/lib/tools/screen.js
CHANGED
|
@@ -25,7 +25,7 @@ const screenshot = {
|
|
|
25
25
|
inputSchema: (0, zod_to_json_schema_1.zodToJsonSchema)(zod_1.z.object({})),
|
|
26
26
|
},
|
|
27
27
|
handle: async (context) => {
|
|
28
|
-
const tab = context.
|
|
28
|
+
const tab = await context.ensureTab();
|
|
29
29
|
const screenshot = await tab.page.screenshot({ type: 'jpeg', quality: 50, scale: 'css' });
|
|
30
30
|
return {
|
|
31
31
|
content: [{ type: 'image', data: screenshot.toString('base64'), mimeType: 'image/jpeg' }],
|
|
@@ -69,11 +69,16 @@ const click = {
|
|
|
69
69
|
handle: async (context, params) => {
|
|
70
70
|
return await context.currentTab().runAndWait(async (tab) => {
|
|
71
71
|
const validatedParams = clickSchema.parse(params);
|
|
72
|
+
const code = [
|
|
73
|
+
`// Click mouse at coordinates (${validatedParams.x}, ${validatedParams.y})`,
|
|
74
|
+
`await page.mouse.move(${validatedParams.x}, ${validatedParams.y});`,
|
|
75
|
+
`await page.mouse.down();`,
|
|
76
|
+
`await page.mouse.up();`,
|
|
77
|
+
];
|
|
72
78
|
await tab.page.mouse.move(validatedParams.x, validatedParams.y);
|
|
73
79
|
await tab.page.mouse.down();
|
|
74
80
|
await tab.page.mouse.up();
|
|
75
|
-
|
|
76
|
-
status: 'Clicked mouse',
|
|
81
|
+
return { code };
|
|
77
82
|
});
|
|
78
83
|
},
|
|
79
84
|
};
|
|
@@ -97,8 +102,14 @@ const drag = {
|
|
|
97
102
|
await tab.page.mouse.down();
|
|
98
103
|
await tab.page.mouse.move(validatedParams.endX, validatedParams.endY);
|
|
99
104
|
await tab.page.mouse.up();
|
|
100
|
-
|
|
101
|
-
|
|
105
|
+
const code = [
|
|
106
|
+
`// Drag mouse from (${validatedParams.startX}, ${validatedParams.startY}) to (${validatedParams.endX}, ${validatedParams.endY})`,
|
|
107
|
+
`await page.mouse.move(${validatedParams.startX}, ${validatedParams.startY});`,
|
|
108
|
+
`await page.mouse.down();`,
|
|
109
|
+
`await page.mouse.move(${validatedParams.endX}, ${validatedParams.endY});`,
|
|
110
|
+
`await page.mouse.up();`,
|
|
111
|
+
];
|
|
112
|
+
return { code };
|
|
102
113
|
});
|
|
103
114
|
},
|
|
104
115
|
};
|
|
@@ -116,11 +127,17 @@ const type = {
|
|
|
116
127
|
handle: async (context, params) => {
|
|
117
128
|
const validatedParams = typeSchema.parse(params);
|
|
118
129
|
return await context.currentTab().runAndWait(async (tab) => {
|
|
130
|
+
const code = [
|
|
131
|
+
`// Type ${validatedParams.text}`,
|
|
132
|
+
`await page.keyboard.type('${validatedParams.text}');`,
|
|
133
|
+
];
|
|
119
134
|
await tab.page.keyboard.type(validatedParams.text);
|
|
120
|
-
if (validatedParams.submit)
|
|
135
|
+
if (validatedParams.submit) {
|
|
136
|
+
code.push(`// Submit text`);
|
|
137
|
+
code.push(`await page.keyboard.press('Enter');`);
|
|
121
138
|
await tab.page.keyboard.press('Enter');
|
|
122
|
-
|
|
123
|
-
|
|
139
|
+
}
|
|
140
|
+
return { code };
|
|
124
141
|
});
|
|
125
142
|
},
|
|
126
143
|
};
|
package/lib/tools/snapshot.js
CHANGED
|
@@ -14,12 +14,47 @@
|
|
|
14
14
|
* See the License for the specific language governing permissions and
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
20
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
21
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
22
|
+
}
|
|
23
|
+
Object.defineProperty(o, k2, desc);
|
|
24
|
+
}) : (function(o, m, k, k2) {
|
|
25
|
+
if (k2 === undefined) k2 = k;
|
|
26
|
+
o[k2] = m[k];
|
|
27
|
+
}));
|
|
28
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
29
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
30
|
+
}) : function(o, v) {
|
|
31
|
+
o["default"] = v;
|
|
32
|
+
});
|
|
33
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
34
|
+
var ownKeys = function(o) {
|
|
35
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
36
|
+
var ar = [];
|
|
37
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
38
|
+
return ar;
|
|
39
|
+
};
|
|
40
|
+
return ownKeys(o);
|
|
41
|
+
};
|
|
42
|
+
return function (mod) {
|
|
43
|
+
if (mod && mod.__esModule) return mod;
|
|
44
|
+
var result = {};
|
|
45
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
46
|
+
__setModuleDefault(result, mod);
|
|
47
|
+
return result;
|
|
48
|
+
};
|
|
49
|
+
})();
|
|
17
50
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
18
51
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
19
52
|
};
|
|
20
53
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
54
|
const zod_1 = require("zod");
|
|
22
55
|
const zod_to_json_schema_1 = __importDefault(require("zod-to-json-schema"));
|
|
56
|
+
const context_1 = require("../context");
|
|
57
|
+
const javascript = __importStar(require("../javascript"));
|
|
23
58
|
const snapshot = {
|
|
24
59
|
capability: 'core',
|
|
25
60
|
schema: {
|
|
@@ -28,7 +63,11 @@ const snapshot = {
|
|
|
28
63
|
inputSchema: (0, zod_to_json_schema_1.default)(zod_1.z.object({})),
|
|
29
64
|
},
|
|
30
65
|
handle: async (context) => {
|
|
31
|
-
|
|
66
|
+
const tab = await context.ensureTab();
|
|
67
|
+
return await tab.run(async () => {
|
|
68
|
+
const code = [`// <internal code to capture accessibility snapshot>`];
|
|
69
|
+
return { code };
|
|
70
|
+
}, { captureSnapshot: true });
|
|
32
71
|
},
|
|
33
72
|
};
|
|
34
73
|
const elementSchema = zod_1.z.object({
|
|
@@ -44,11 +83,14 @@ const click = {
|
|
|
44
83
|
},
|
|
45
84
|
handle: async (context, params) => {
|
|
46
85
|
const validatedParams = elementSchema.parse(params);
|
|
47
|
-
return await context.currentTab().runAndWaitWithSnapshot(async (
|
|
48
|
-
const locator =
|
|
86
|
+
return await context.currentTab().runAndWaitWithSnapshot(async (snapshot) => {
|
|
87
|
+
const locator = snapshot.refLocator(validatedParams.ref);
|
|
88
|
+
const code = [
|
|
89
|
+
`// Click ${validatedParams.element}`,
|
|
90
|
+
`await page.${await (0, context_1.generateLocator)(locator)}.click();`
|
|
91
|
+
];
|
|
49
92
|
await locator.click();
|
|
50
|
-
|
|
51
|
-
status: `Clicked "${validatedParams.element}"`,
|
|
93
|
+
return { code };
|
|
52
94
|
});
|
|
53
95
|
},
|
|
54
96
|
};
|
|
@@ -67,12 +109,15 @@ const drag = {
|
|
|
67
109
|
},
|
|
68
110
|
handle: async (context, params) => {
|
|
69
111
|
const validatedParams = dragSchema.parse(params);
|
|
70
|
-
return await context.currentTab().runAndWaitWithSnapshot(async (
|
|
71
|
-
const startLocator =
|
|
72
|
-
const endLocator =
|
|
112
|
+
return await context.currentTab().runAndWaitWithSnapshot(async (snapshot) => {
|
|
113
|
+
const startLocator = snapshot.refLocator(validatedParams.startRef);
|
|
114
|
+
const endLocator = snapshot.refLocator(validatedParams.endRef);
|
|
115
|
+
const code = [
|
|
116
|
+
`// Drag ${validatedParams.startElement} to ${validatedParams.endElement}`,
|
|
117
|
+
`await page.${await (0, context_1.generateLocator)(startLocator)}.dragTo(page.${await (0, context_1.generateLocator)(endLocator)});`
|
|
118
|
+
];
|
|
73
119
|
await startLocator.dragTo(endLocator);
|
|
74
|
-
|
|
75
|
-
status: `Dragged "${validatedParams.startElement}" to "${validatedParams.endElement}"`,
|
|
120
|
+
return { code };
|
|
76
121
|
});
|
|
77
122
|
},
|
|
78
123
|
};
|
|
@@ -85,11 +130,14 @@ const hover = {
|
|
|
85
130
|
},
|
|
86
131
|
handle: async (context, params) => {
|
|
87
132
|
const validatedParams = elementSchema.parse(params);
|
|
88
|
-
return await context.currentTab().runAndWaitWithSnapshot(async (
|
|
89
|
-
const locator =
|
|
133
|
+
return await context.currentTab().runAndWaitWithSnapshot(async (snapshot) => {
|
|
134
|
+
const locator = snapshot.refLocator(validatedParams.ref);
|
|
135
|
+
const code = [
|
|
136
|
+
`// Hover over ${validatedParams.element}`,
|
|
137
|
+
`await page.${await (0, context_1.generateLocator)(locator)}.hover();`
|
|
138
|
+
];
|
|
90
139
|
await locator.hover();
|
|
91
|
-
|
|
92
|
-
status: `Hovered over "${validatedParams.element}"`,
|
|
140
|
+
return { code };
|
|
93
141
|
});
|
|
94
142
|
},
|
|
95
143
|
};
|
|
@@ -107,16 +155,25 @@ const type = {
|
|
|
107
155
|
},
|
|
108
156
|
handle: async (context, params) => {
|
|
109
157
|
const validatedParams = typeSchema.parse(params);
|
|
110
|
-
return await context.currentTab().runAndWaitWithSnapshot(async (
|
|
111
|
-
const locator =
|
|
112
|
-
|
|
158
|
+
return await context.currentTab().runAndWaitWithSnapshot(async (snapshot) => {
|
|
159
|
+
const locator = snapshot.refLocator(validatedParams.ref);
|
|
160
|
+
const code = [];
|
|
161
|
+
if (validatedParams.slowly) {
|
|
162
|
+
code.push(`// Press "${validatedParams.text}" sequentially into "${validatedParams.element}"`);
|
|
163
|
+
code.push(`await page.${await (0, context_1.generateLocator)(locator)}.pressSequentially(${javascript.quote(validatedParams.text)});`);
|
|
113
164
|
await locator.pressSequentially(validatedParams.text);
|
|
114
|
-
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
code.push(`// Fill "${validatedParams.text}" into "${validatedParams.element}"`);
|
|
168
|
+
code.push(`await page.${await (0, context_1.generateLocator)(locator)}.fill(${javascript.quote(validatedParams.text)});`);
|
|
115
169
|
await locator.fill(validatedParams.text);
|
|
116
|
-
|
|
170
|
+
}
|
|
171
|
+
if (validatedParams.submit) {
|
|
172
|
+
code.push(`// Submit text`);
|
|
173
|
+
code.push(`await page.${await (0, context_1.generateLocator)(locator)}.press('Enter');`);
|
|
117
174
|
await locator.press('Enter');
|
|
118
|
-
|
|
119
|
-
|
|
175
|
+
}
|
|
176
|
+
return { code };
|
|
120
177
|
});
|
|
121
178
|
},
|
|
122
179
|
};
|
|
@@ -132,11 +189,14 @@ const selectOption = {
|
|
|
132
189
|
},
|
|
133
190
|
handle: async (context, params) => {
|
|
134
191
|
const validatedParams = selectOptionSchema.parse(params);
|
|
135
|
-
return await context.currentTab().runAndWaitWithSnapshot(async (
|
|
136
|
-
const locator =
|
|
192
|
+
return await context.currentTab().runAndWaitWithSnapshot(async (snapshot) => {
|
|
193
|
+
const locator = snapshot.refLocator(validatedParams.ref);
|
|
194
|
+
const code = [
|
|
195
|
+
`// Select options [${validatedParams.values.join(', ')}] in ${validatedParams.element}`,
|
|
196
|
+
`await page.${await (0, context_1.generateLocator)(locator)}.selectOption(${javascript.formatObject(validatedParams.values)});`
|
|
197
|
+
];
|
|
137
198
|
await locator.selectOption(validatedParams.values);
|
|
138
|
-
|
|
139
|
-
status: `Selected option in "${validatedParams.element}"`,
|
|
199
|
+
return { code };
|
|
140
200
|
});
|
|
141
201
|
},
|
|
142
202
|
};
|
package/lib/tools/tabs.js
CHANGED
|
@@ -47,7 +47,12 @@ const selectTab = captureSnapshot => ({
|
|
|
47
47
|
const validatedParams = selectTabSchema.parse(params);
|
|
48
48
|
await context.selectTab(validatedParams.index);
|
|
49
49
|
const currentTab = await context.ensureTab();
|
|
50
|
-
return await currentTab.run(async () => {
|
|
50
|
+
return await currentTab.run(async () => {
|
|
51
|
+
const code = [
|
|
52
|
+
`// <internal code to select tab ${validatedParams.index}>`,
|
|
53
|
+
];
|
|
54
|
+
return { code };
|
|
55
|
+
}, { captureSnapshot });
|
|
51
56
|
},
|
|
52
57
|
});
|
|
53
58
|
const newTabSchema = zod_1.z.object({
|
|
@@ -65,7 +70,12 @@ const newTab = {
|
|
|
65
70
|
await context.newTab();
|
|
66
71
|
if (validatedParams.url)
|
|
67
72
|
await context.currentTab().navigate(validatedParams.url);
|
|
68
|
-
return await context.currentTab().run(async () => {
|
|
73
|
+
return await context.currentTab().run(async () => {
|
|
74
|
+
const code = [
|
|
75
|
+
`// <internal code to open a new tab>`,
|
|
76
|
+
];
|
|
77
|
+
return { code };
|
|
78
|
+
}, { captureSnapshot: true });
|
|
69
79
|
},
|
|
70
80
|
};
|
|
71
81
|
const closeTabSchema = zod_1.z.object({
|
|
@@ -81,9 +91,15 @@ const closeTab = captureSnapshot => ({
|
|
|
81
91
|
handle: async (context, params) => {
|
|
82
92
|
const validatedParams = closeTabSchema.parse(params);
|
|
83
93
|
await context.closeTab(validatedParams.index);
|
|
84
|
-
const currentTab =
|
|
85
|
-
if (currentTab)
|
|
86
|
-
return await currentTab.run(async () => {
|
|
94
|
+
const currentTab = context.currentTab();
|
|
95
|
+
if (currentTab) {
|
|
96
|
+
return await currentTab.run(async () => {
|
|
97
|
+
const code = [
|
|
98
|
+
`// <internal code to close tab ${validatedParams.index}>`,
|
|
99
|
+
];
|
|
100
|
+
return { code };
|
|
101
|
+
}, { captureSnapshot });
|
|
102
|
+
}
|
|
87
103
|
return {
|
|
88
104
|
content: [{
|
|
89
105
|
type: 'text',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playwright/mcp",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.13",
|
|
4
4
|
"description": "Playwright Tools for MCP",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"lint": "eslint .",
|
|
20
20
|
"watch": "tsc --watch",
|
|
21
21
|
"test": "playwright test",
|
|
22
|
+
"ctest": "playwright test --project=chrome",
|
|
22
23
|
"clean": "rm -rf lib",
|
|
23
24
|
"npm-publish": "npm run clean && npm run build && npm run test && npm publish"
|
|
24
25
|
},
|