@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.
@@ -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
- 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 });
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
- 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
- ];
92
- await locator.click();
93
- return { code };
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
- 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
- ];
119
- await startLocator.dragTo(endLocator);
120
- return { code };
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
- 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
- ];
139
- await locator.hover();
140
- return { code };
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
- 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)});`);
164
- await locator.pressSequentially(validatedParams.text);
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)});`);
169
- await locator.fill(validatedParams.text);
170
- }
171
- if (validatedParams.submit) {
172
- code.push(`// Submit text`);
173
- code.push(`await page.${await (0, context_1.generateLocator)(locator)}.press('Enter');`);
174
- await locator.press('Enter');
175
- }
176
- return { code };
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
- 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
- ];
198
- await locator.selectOption(validatedParams.values);
199
- return { code };
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.currentTab();
216
- const options = validatedParams.raw ? { type: 'png', scale: 'css' } : { type: 'jpeg', quality: 50, scale: 'css' };
217
- const screenshot = await tab.page.screenshot(options);
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
- content: [{ type: 'image', data: screenshot.toString('base64'), mimeType: validatedParams.raw ? 'image/png' : 'image/jpeg' }],
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
- content: [{
30
- type: 'text',
31
- text: await context.listTabs(),
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 currentTab = await context.ensureTab();
50
- return await currentTab.run(async () => {
51
- const code = [
52
- `// <internal code to select tab ${validatedParams.index}>`,
53
- ];
54
- return { code };
55
- }, { captureSnapshot });
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.currentTab().navigate(validatedParams.url);
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 });
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 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
- }
103
+ const code = [
104
+ `// <internal code to close tab ${validatedParams.index}>`,
105
+ ];
103
106
  return {
104
- content: [{
105
- type: 'text',
106
- text: await context.listTabs(),
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
  ];
@@ -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 page.evaluate(() => new Promise(f => setTimeout(f, 1000)));
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.13",
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
  },