@playwright/mcp 0.0.13 → 0.0.14
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 +134 -30
- package/index.d.ts +37 -24
- package/lib/context.js +155 -81
- package/lib/index.js +61 -9
- package/lib/manualPromise.js +116 -0
- package/lib/program.js +4 -53
- package/lib/server.js +15 -4
- package/lib/tools/common.js +21 -20
- package/lib/{resources → tools}/console.js +23 -12
- package/lib/tools/dialogs.js +54 -0
- package/lib/tools/files.js +16 -10
- package/lib/tools/install.js +3 -4
- package/lib/tools/keyboard.js +11 -9
- package/lib/tools/navigate.js +34 -32
- package/lib/tools/pdf.js +43 -6
- package/lib/tools/screen.js +108 -36
- package/lib/tools/snapshot.js +119 -67
- package/lib/tools/tabs.js +36 -34
- package/lib/tools/utils.js +2 -2
- package/package.json +4 -1
package/lib/tools/snapshot.js
CHANGED
|
@@ -51,8 +51,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
51
51
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
52
52
|
};
|
|
53
53
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
54
|
+
const path_1 = __importDefault(require("path"));
|
|
55
|
+
const os_1 = __importDefault(require("os"));
|
|
54
56
|
const zod_1 = require("zod");
|
|
55
57
|
const zod_to_json_schema_1 = __importDefault(require("zod-to-json-schema"));
|
|
58
|
+
const utils_1 = require("./utils");
|
|
56
59
|
const context_1 = require("../context");
|
|
57
60
|
const javascript = __importStar(require("../javascript"));
|
|
58
61
|
const snapshot = {
|
|
@@ -63,11 +66,12 @@ const snapshot = {
|
|
|
63
66
|
inputSchema: (0, zod_to_json_schema_1.default)(zod_1.z.object({})),
|
|
64
67
|
},
|
|
65
68
|
handle: async (context) => {
|
|
66
|
-
|
|
67
|
-
return
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
await context.ensureTab();
|
|
70
|
+
return {
|
|
71
|
+
code: [`// <internal code to capture accessibility snapshot>`],
|
|
72
|
+
captureSnapshot: true,
|
|
73
|
+
waitForNetwork: false,
|
|
74
|
+
};
|
|
71
75
|
},
|
|
72
76
|
};
|
|
73
77
|
const elementSchema = zod_1.z.object({
|
|
@@ -83,15 +87,18 @@ const click = {
|
|
|
83
87
|
},
|
|
84
88
|
handle: async (context, params) => {
|
|
85
89
|
const validatedParams = elementSchema.parse(params);
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
90
|
+
const tab = context.currentTabOrDie();
|
|
91
|
+
const locator = tab.snapshotOrDie().refLocator(validatedParams.ref);
|
|
92
|
+
const code = [
|
|
93
|
+
`// Click ${validatedParams.element}`,
|
|
94
|
+
`await page.${await (0, context_1.generateLocator)(locator)}.click();`
|
|
95
|
+
];
|
|
96
|
+
return {
|
|
97
|
+
code,
|
|
98
|
+
action: () => locator.click(),
|
|
99
|
+
captureSnapshot: true,
|
|
100
|
+
waitForNetwork: true,
|
|
101
|
+
};
|
|
95
102
|
},
|
|
96
103
|
};
|
|
97
104
|
const dragSchema = zod_1.z.object({
|
|
@@ -109,16 +116,19 @@ const drag = {
|
|
|
109
116
|
},
|
|
110
117
|
handle: async (context, params) => {
|
|
111
118
|
const validatedParams = dragSchema.parse(params);
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
119
|
+
const snapshot = context.currentTabOrDie().snapshotOrDie();
|
|
120
|
+
const startLocator = snapshot.refLocator(validatedParams.startRef);
|
|
121
|
+
const endLocator = snapshot.refLocator(validatedParams.endRef);
|
|
122
|
+
const code = [
|
|
123
|
+
`// Drag ${validatedParams.startElement} to ${validatedParams.endElement}`,
|
|
124
|
+
`await page.${await (0, context_1.generateLocator)(startLocator)}.dragTo(page.${await (0, context_1.generateLocator)(endLocator)});`
|
|
125
|
+
];
|
|
126
|
+
return {
|
|
127
|
+
code,
|
|
128
|
+
action: () => startLocator.dragTo(endLocator),
|
|
129
|
+
captureSnapshot: true,
|
|
130
|
+
waitForNetwork: true,
|
|
131
|
+
};
|
|
122
132
|
},
|
|
123
133
|
};
|
|
124
134
|
const hover = {
|
|
@@ -130,15 +140,18 @@ const hover = {
|
|
|
130
140
|
},
|
|
131
141
|
handle: async (context, params) => {
|
|
132
142
|
const validatedParams = elementSchema.parse(params);
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
143
|
+
const snapshot = context.currentTabOrDie().snapshotOrDie();
|
|
144
|
+
const locator = snapshot.refLocator(validatedParams.ref);
|
|
145
|
+
const code = [
|
|
146
|
+
`// Hover over ${validatedParams.element}`,
|
|
147
|
+
`await page.${await (0, context_1.generateLocator)(locator)}.hover();`
|
|
148
|
+
];
|
|
149
|
+
return {
|
|
150
|
+
code,
|
|
151
|
+
action: () => locator.hover(),
|
|
152
|
+
captureSnapshot: true,
|
|
153
|
+
waitForNetwork: true,
|
|
154
|
+
};
|
|
142
155
|
},
|
|
143
156
|
};
|
|
144
157
|
const typeSchema = elementSchema.extend({
|
|
@@ -155,26 +168,31 @@ const type = {
|
|
|
155
168
|
},
|
|
156
169
|
handle: async (context, params) => {
|
|
157
170
|
const validatedParams = typeSchema.parse(params);
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
171
|
+
const snapshot = context.currentTabOrDie().snapshotOrDie();
|
|
172
|
+
const locator = snapshot.refLocator(validatedParams.ref);
|
|
173
|
+
const code = [];
|
|
174
|
+
const steps = [];
|
|
175
|
+
if (validatedParams.slowly) {
|
|
176
|
+
code.push(`// Press "${validatedParams.text}" sequentially into "${validatedParams.element}"`);
|
|
177
|
+
code.push(`await page.${await (0, context_1.generateLocator)(locator)}.pressSequentially(${javascript.quote(validatedParams.text)});`);
|
|
178
|
+
steps.push(() => locator.pressSequentially(validatedParams.text));
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
code.push(`// Fill "${validatedParams.text}" into "${validatedParams.element}"`);
|
|
182
|
+
code.push(`await page.${await (0, context_1.generateLocator)(locator)}.fill(${javascript.quote(validatedParams.text)});`);
|
|
183
|
+
steps.push(() => locator.fill(validatedParams.text));
|
|
184
|
+
}
|
|
185
|
+
if (validatedParams.submit) {
|
|
186
|
+
code.push(`// Submit text`);
|
|
187
|
+
code.push(`await page.${await (0, context_1.generateLocator)(locator)}.press('Enter');`);
|
|
188
|
+
steps.push(() => locator.press('Enter'));
|
|
189
|
+
}
|
|
190
|
+
return {
|
|
191
|
+
code,
|
|
192
|
+
action: () => steps.reduce((acc, step) => acc.then(step), Promise.resolve()),
|
|
193
|
+
captureSnapshot: true,
|
|
194
|
+
waitForNetwork: true,
|
|
195
|
+
};
|
|
178
196
|
},
|
|
179
197
|
};
|
|
180
198
|
const selectOptionSchema = elementSchema.extend({
|
|
@@ -189,19 +207,29 @@ const selectOption = {
|
|
|
189
207
|
},
|
|
190
208
|
handle: async (context, params) => {
|
|
191
209
|
const validatedParams = selectOptionSchema.parse(params);
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
210
|
+
const snapshot = context.currentTabOrDie().snapshotOrDie();
|
|
211
|
+
const locator = snapshot.refLocator(validatedParams.ref);
|
|
212
|
+
const code = [
|
|
213
|
+
`// Select options [${validatedParams.values.join(', ')}] in ${validatedParams.element}`,
|
|
214
|
+
`await page.${await (0, context_1.generateLocator)(locator)}.selectOption(${javascript.formatObject(validatedParams.values)});`
|
|
215
|
+
];
|
|
216
|
+
return {
|
|
217
|
+
code,
|
|
218
|
+
action: () => locator.selectOption(validatedParams.values).then(() => { }),
|
|
219
|
+
captureSnapshot: true,
|
|
220
|
+
waitForNetwork: true,
|
|
221
|
+
};
|
|
201
222
|
},
|
|
202
223
|
};
|
|
203
224
|
const screenshotSchema = zod_1.z.object({
|
|
204
225
|
raw: zod_1.z.boolean().optional().describe('Whether to return without compression (in PNG format). Default is false, which returns a JPEG image.'),
|
|
226
|
+
element: zod_1.z.string().optional().describe('Human-readable element description used to obtain permission to screenshot the element. If not provided, the screenshot will be taken of viewport. If element is provided, ref must be provided too.'),
|
|
227
|
+
ref: zod_1.z.string().optional().describe('Exact target element reference from the page snapshot. If not provided, the screenshot will be taken of viewport. If ref is provided, element must be provided too.'),
|
|
228
|
+
}).refine(data => {
|
|
229
|
+
return !!data.element === !!data.ref;
|
|
230
|
+
}, {
|
|
231
|
+
message: 'Both element and ref must be provided or neither.',
|
|
232
|
+
path: ['ref', 'element']
|
|
205
233
|
});
|
|
206
234
|
const screenshot = {
|
|
207
235
|
capability: 'core',
|
|
@@ -212,13 +240,37 @@ const screenshot = {
|
|
|
212
240
|
},
|
|
213
241
|
handle: async (context, params) => {
|
|
214
242
|
const validatedParams = screenshotSchema.parse(params);
|
|
215
|
-
const tab = context.
|
|
216
|
-
const
|
|
217
|
-
const
|
|
243
|
+
const tab = context.currentTabOrDie();
|
|
244
|
+
const snapshot = tab.snapshotOrDie();
|
|
245
|
+
const fileType = validatedParams.raw ? 'png' : 'jpeg';
|
|
246
|
+
const fileName = path_1.default.join(os_1.default.tmpdir(), (0, utils_1.sanitizeForFilePath)(`page-${new Date().toISOString()}`)) + `.${fileType}`;
|
|
247
|
+
const options = { type: fileType, quality: fileType === 'png' ? undefined : 50, scale: 'css', path: fileName };
|
|
248
|
+
const isElementScreenshot = validatedParams.element && validatedParams.ref;
|
|
249
|
+
const code = [
|
|
250
|
+
`// Screenshot ${isElementScreenshot ? validatedParams.element : 'viewport'} and save it as ${fileName}`,
|
|
251
|
+
];
|
|
252
|
+
const locator = validatedParams.ref ? snapshot.refLocator(validatedParams.ref) : null;
|
|
253
|
+
if (locator)
|
|
254
|
+
code.push(`await page.${await (0, context_1.generateLocator)(locator)}.screenshot(${javascript.formatObject(options)});`);
|
|
255
|
+
else
|
|
256
|
+
code.push(`await page.screenshot(${javascript.formatObject(options)});`);
|
|
257
|
+
const action = async () => {
|
|
258
|
+
const screenshot = locator ? await locator.screenshot(options) : await tab.page.screenshot(options);
|
|
259
|
+
return {
|
|
260
|
+
content: [{
|
|
261
|
+
type: 'image',
|
|
262
|
+
data: screenshot.toString('base64'),
|
|
263
|
+
mimeType: fileType === 'png' ? 'image/png' : 'image/jpeg',
|
|
264
|
+
}]
|
|
265
|
+
};
|
|
266
|
+
};
|
|
218
267
|
return {
|
|
219
|
-
|
|
268
|
+
code,
|
|
269
|
+
action,
|
|
270
|
+
captureSnapshot: true,
|
|
271
|
+
waitForNetwork: false,
|
|
220
272
|
};
|
|
221
|
-
}
|
|
273
|
+
}
|
|
222
274
|
};
|
|
223
275
|
exports.default = [
|
|
224
276
|
snapshot,
|
package/lib/tools/tabs.js
CHANGED
|
@@ -25,11 +25,17 @@ const listTabs = {
|
|
|
25
25
|
inputSchema: (0, zod_to_json_schema_1.zodToJsonSchema)(zod_1.z.object({})),
|
|
26
26
|
},
|
|
27
27
|
handle: async (context) => {
|
|
28
|
+
await context.ensureTab();
|
|
28
29
|
return {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
code: [`// <internal code to list tabs>`],
|
|
31
|
+
captureSnapshot: false,
|
|
32
|
+
waitForNetwork: false,
|
|
33
|
+
resultOverride: {
|
|
34
|
+
content: [{
|
|
35
|
+
type: 'text',
|
|
36
|
+
text: await context.listTabsMarkdown(),
|
|
37
|
+
}],
|
|
38
|
+
},
|
|
33
39
|
};
|
|
34
40
|
},
|
|
35
41
|
};
|
|
@@ -46,19 +52,20 @@ const selectTab = captureSnapshot => ({
|
|
|
46
52
|
handle: async (context, params) => {
|
|
47
53
|
const validatedParams = selectTabSchema.parse(params);
|
|
48
54
|
await context.selectTab(validatedParams.index);
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
const code = [
|
|
56
|
+
`// <internal code to select tab ${validatedParams.index}>`,
|
|
57
|
+
];
|
|
58
|
+
return {
|
|
59
|
+
code,
|
|
60
|
+
captureSnapshot,
|
|
61
|
+
waitForNetwork: false
|
|
62
|
+
};
|
|
56
63
|
},
|
|
57
64
|
});
|
|
58
65
|
const newTabSchema = zod_1.z.object({
|
|
59
66
|
url: zod_1.z.string().optional().describe('The URL to navigate to in the new tab. If not provided, the new tab will be blank.'),
|
|
60
67
|
});
|
|
61
|
-
const newTab = {
|
|
68
|
+
const newTab = captureSnapshot => ({
|
|
62
69
|
capability: 'tabs',
|
|
63
70
|
schema: {
|
|
64
71
|
name: 'browser_tab_new',
|
|
@@ -69,15 +76,17 @@ const newTab = {
|
|
|
69
76
|
const validatedParams = newTabSchema.parse(params);
|
|
70
77
|
await context.newTab();
|
|
71
78
|
if (validatedParams.url)
|
|
72
|
-
await context.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
+
await context.currentTabOrDie().navigate(validatedParams.url);
|
|
80
|
+
const code = [
|
|
81
|
+
`// <internal code to open a new tab>`,
|
|
82
|
+
];
|
|
83
|
+
return {
|
|
84
|
+
code,
|
|
85
|
+
captureSnapshot,
|
|
86
|
+
waitForNetwork: false
|
|
87
|
+
};
|
|
79
88
|
},
|
|
80
|
-
};
|
|
89
|
+
});
|
|
81
90
|
const closeTabSchema = zod_1.z.object({
|
|
82
91
|
index: zod_1.z.number().optional().describe('The index of the tab to close. Closes current tab if not provided.'),
|
|
83
92
|
});
|
|
@@ -91,26 +100,19 @@ const closeTab = captureSnapshot => ({
|
|
|
91
100
|
handle: async (context, params) => {
|
|
92
101
|
const validatedParams = closeTabSchema.parse(params);
|
|
93
102
|
await context.closeTab(validatedParams.index);
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const code = [
|
|
98
|
-
`// <internal code to close tab ${validatedParams.index}>`,
|
|
99
|
-
];
|
|
100
|
-
return { code };
|
|
101
|
-
}, { captureSnapshot });
|
|
102
|
-
}
|
|
103
|
+
const code = [
|
|
104
|
+
`// <internal code to close tab ${validatedParams.index}>`,
|
|
105
|
+
];
|
|
103
106
|
return {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}],
|
|
107
|
+
code,
|
|
108
|
+
captureSnapshot,
|
|
109
|
+
waitForNetwork: false
|
|
108
110
|
};
|
|
109
111
|
},
|
|
110
112
|
});
|
|
111
113
|
exports.default = (captureSnapshot) => [
|
|
112
114
|
listTabs,
|
|
113
|
-
newTab,
|
|
115
|
+
newTab(captureSnapshot),
|
|
114
116
|
selectTab(captureSnapshot),
|
|
115
117
|
closeTab(captureSnapshot),
|
|
116
118
|
];
|
package/lib/tools/utils.js
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
18
|
exports.waitForCompletion = waitForCompletion;
|
|
19
19
|
exports.sanitizeForFilePath = sanitizeForFilePath;
|
|
20
|
-
async function waitForCompletion(page, callback) {
|
|
20
|
+
async function waitForCompletion(context, page, callback) {
|
|
21
21
|
const requests = new Set();
|
|
22
22
|
let frameNavigated = false;
|
|
23
23
|
let waitCallback = () => { };
|
|
@@ -57,7 +57,7 @@ async function waitForCompletion(page, callback) {
|
|
|
57
57
|
if (!requests.size && !frameNavigated)
|
|
58
58
|
waitCallback();
|
|
59
59
|
await waitBarrier;
|
|
60
|
-
await
|
|
60
|
+
await context.waitForTimeout(1000);
|
|
61
61
|
return result;
|
|
62
62
|
}
|
|
63
63
|
finally {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playwright/mcp",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.14",
|
|
4
4
|
"description": "Playwright Tools for MCP",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -17,9 +17,12 @@
|
|
|
17
17
|
"scripts": {
|
|
18
18
|
"build": "tsc",
|
|
19
19
|
"lint": "eslint .",
|
|
20
|
+
"update-readme": "node utils/update-readme.js",
|
|
20
21
|
"watch": "tsc --watch",
|
|
21
22
|
"test": "playwright test",
|
|
22
23
|
"ctest": "playwright test --project=chrome",
|
|
24
|
+
"ftest": "playwright test --project=firefox",
|
|
25
|
+
"wtest": "playwright test --project=webkit",
|
|
23
26
|
"clean": "rm -rf lib",
|
|
24
27
|
"npm-publish": "npm run clean && npm run build && npm run test && npm publish"
|
|
25
28
|
},
|