@playwright/mcp 0.0.29 → 0.0.31

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.
@@ -15,8 +15,8 @@
15
15
  */
16
16
  import { z } from 'zod';
17
17
  import { defineTool } from './tool.js';
18
- const uploadFile = captureSnapshot => defineTool({
19
- capability: 'files',
18
+ const uploadFile = defineTool({
19
+ capability: 'core',
20
20
  schema: {
21
21
  name: 'browser_file_upload',
22
22
  title: 'Upload files',
@@ -40,12 +40,12 @@ const uploadFile = captureSnapshot => defineTool({
40
40
  return {
41
41
  code,
42
42
  action,
43
- captureSnapshot,
43
+ captureSnapshot: true,
44
44
  waitForNetwork: true,
45
45
  };
46
46
  },
47
47
  clearsModalState: 'fileChooser',
48
48
  });
49
- export default (captureSnapshot) => [
50
- uploadFile(captureSnapshot),
49
+ export default [
50
+ uploadFile,
51
51
  ];
@@ -19,7 +19,7 @@ import { z } from 'zod';
19
19
  import { defineTool } from './tool.js';
20
20
  import { fileURLToPath } from 'node:url';
21
21
  const install = defineTool({
22
- capability: 'install',
22
+ capability: 'core-install',
23
23
  schema: {
24
24
  name: 'browser_install',
25
25
  title: 'Install the browser specified in the config',
@@ -15,7 +15,10 @@
15
15
  */
16
16
  import { z } from 'zod';
17
17
  import { defineTool } from './tool.js';
18
- const pressKey = captureSnapshot => defineTool({
18
+ import { elementSchema } from './snapshot.js';
19
+ import { generateLocator } from './utils.js';
20
+ import * as javascript from '../javascript.js';
21
+ const pressKey = defineTool({
19
22
  capability: 'core',
20
23
  schema: {
21
24
  name: 'browser_press_key',
@@ -36,11 +39,54 @@ const pressKey = captureSnapshot => defineTool({
36
39
  return {
37
40
  code,
38
41
  action,
39
- captureSnapshot,
42
+ captureSnapshot: true,
40
43
  waitForNetwork: true
41
44
  };
42
45
  },
43
46
  });
44
- export default (captureSnapshot) => [
45
- pressKey(captureSnapshot),
47
+ const typeSchema = elementSchema.extend({
48
+ text: z.string().describe('Text to type into the element'),
49
+ submit: z.boolean().optional().describe('Whether to submit entered text (press Enter after)'),
50
+ slowly: z.boolean().optional().describe('Whether to type one character at a time. Useful for triggering key handlers in the page. By default entire text is filled in at once.'),
51
+ });
52
+ const type = defineTool({
53
+ capability: 'core',
54
+ schema: {
55
+ name: 'browser_type',
56
+ title: 'Type text',
57
+ description: 'Type text into editable element',
58
+ inputSchema: typeSchema,
59
+ type: 'destructive',
60
+ },
61
+ handle: async (context, params) => {
62
+ const snapshot = context.currentTabOrDie().snapshotOrDie();
63
+ const locator = snapshot.refLocator(params);
64
+ const code = [];
65
+ const steps = [];
66
+ if (params.slowly) {
67
+ code.push(`// Press "${params.text}" sequentially into "${params.element}"`);
68
+ code.push(`await page.${await generateLocator(locator)}.pressSequentially(${javascript.quote(params.text)});`);
69
+ steps.push(() => locator.pressSequentially(params.text));
70
+ }
71
+ else {
72
+ code.push(`// Fill "${params.text}" into "${params.element}"`);
73
+ code.push(`await page.${await generateLocator(locator)}.fill(${javascript.quote(params.text)});`);
74
+ steps.push(() => locator.fill(params.text));
75
+ }
76
+ if (params.submit) {
77
+ code.push(`// Submit text`);
78
+ code.push(`await page.${await generateLocator(locator)}.press('Enter');`);
79
+ steps.push(() => locator.press('Enter'));
80
+ }
81
+ return {
82
+ code,
83
+ action: () => steps.reduce((acc, step) => acc.then(step), Promise.resolve()),
84
+ captureSnapshot: true,
85
+ waitForNetwork: true,
86
+ };
87
+ },
88
+ });
89
+ export default [
90
+ pressKey,
91
+ type,
46
92
  ];
@@ -15,43 +15,13 @@
15
15
  */
16
16
  import { z } from 'zod';
17
17
  import { defineTool } from './tool.js';
18
- import * as javascript from '../javascript.js';
19
18
  const elementSchema = z.object({
20
19
  element: z.string().describe('Human-readable element description used to obtain permission to interact with the element'),
21
20
  });
22
- const screenshot = defineTool({
23
- capability: 'core',
21
+ const mouseMove = defineTool({
22
+ capability: 'vision',
24
23
  schema: {
25
- name: 'browser_screen_capture',
26
- title: 'Take a screenshot',
27
- description: 'Take a screenshot of the current page',
28
- inputSchema: z.object({}),
29
- type: 'readOnly',
30
- },
31
- handle: async (context) => {
32
- const tab = await context.ensureTab();
33
- const options = { type: 'jpeg', quality: 50, scale: 'css' };
34
- const code = [
35
- `// Take a screenshot of the current page`,
36
- `await page.screenshot(${javascript.formatObject(options)});`,
37
- ];
38
- const action = () => tab.page.screenshot(options).then(buffer => {
39
- return {
40
- content: [{ type: 'image', data: buffer.toString('base64'), mimeType: 'image/jpeg' }],
41
- };
42
- });
43
- return {
44
- code,
45
- action,
46
- captureSnapshot: false,
47
- waitForNetwork: false
48
- };
49
- },
50
- });
51
- const moveMouse = defineTool({
52
- capability: 'core',
53
- schema: {
54
- name: 'browser_screen_move_mouse',
24
+ name: 'browser_mouse_move_xy',
55
25
  title: 'Move mouse',
56
26
  description: 'Move mouse to a given position',
57
27
  inputSchema: elementSchema.extend({
@@ -75,12 +45,12 @@ const moveMouse = defineTool({
75
45
  };
76
46
  },
77
47
  });
78
- const click = defineTool({
79
- capability: 'core',
48
+ const mouseClick = defineTool({
49
+ capability: 'vision',
80
50
  schema: {
81
- name: 'browser_screen_click',
51
+ name: 'browser_mouse_click_xy',
82
52
  title: 'Click',
83
- description: 'Click left mouse button',
53
+ description: 'Click left mouse button at a given position',
84
54
  inputSchema: elementSchema.extend({
85
55
  x: z.number().describe('X coordinate'),
86
56
  y: z.number().describe('Y coordinate'),
@@ -108,12 +78,12 @@ const click = defineTool({
108
78
  };
109
79
  },
110
80
  });
111
- const drag = defineTool({
112
- capability: 'core',
81
+ const mouseDrag = defineTool({
82
+ capability: 'vision',
113
83
  schema: {
114
- name: 'browser_screen_drag',
84
+ name: 'browser_mouse_drag_xy',
115
85
  title: 'Drag mouse',
116
- description: 'Drag left mouse button',
86
+ description: 'Drag left mouse button to a given position',
117
87
  inputSchema: elementSchema.extend({
118
88
  startX: z.number().describe('Start X coordinate'),
119
89
  startY: z.number().describe('Start Y coordinate'),
@@ -145,45 +115,8 @@ const drag = defineTool({
145
115
  };
146
116
  },
147
117
  });
148
- const type = defineTool({
149
- capability: 'core',
150
- schema: {
151
- name: 'browser_screen_type',
152
- title: 'Type text',
153
- description: 'Type text',
154
- inputSchema: z.object({
155
- text: z.string().describe('Text to type into the element'),
156
- submit: z.boolean().optional().describe('Whether to submit entered text (press Enter after)'),
157
- }),
158
- type: 'destructive',
159
- },
160
- handle: async (context, params) => {
161
- const tab = context.currentTabOrDie();
162
- const code = [
163
- `// Type ${params.text}`,
164
- `await page.keyboard.type('${params.text}');`,
165
- ];
166
- const action = async () => {
167
- await tab.page.keyboard.type(params.text);
168
- if (params.submit)
169
- await tab.page.keyboard.press('Enter');
170
- };
171
- if (params.submit) {
172
- code.push(`// Submit text`);
173
- code.push(`await page.keyboard.press('Enter');`);
174
- }
175
- return {
176
- code,
177
- action,
178
- captureSnapshot: false,
179
- waitForNetwork: true,
180
- };
181
- },
182
- });
183
118
  export default [
184
- screenshot,
185
- moveMouse,
186
- click,
187
- drag,
188
- type,
119
+ mouseMove,
120
+ mouseClick,
121
+ mouseDrag,
189
122
  ];
@@ -15,7 +15,7 @@
15
15
  */
16
16
  import { z } from 'zod';
17
17
  import { defineTool } from './tool.js';
18
- const navigate = captureSnapshot => defineTool({
18
+ const navigate = defineTool({
19
19
  capability: 'core',
20
20
  schema: {
21
21
  name: 'browser_navigate',
@@ -35,13 +35,13 @@ const navigate = captureSnapshot => defineTool({
35
35
  ];
36
36
  return {
37
37
  code,
38
- captureSnapshot,
38
+ captureSnapshot: true,
39
39
  waitForNetwork: false,
40
40
  };
41
41
  },
42
42
  });
43
- const goBack = captureSnapshot => defineTool({
44
- capability: 'history',
43
+ const goBack = defineTool({
44
+ capability: 'core',
45
45
  schema: {
46
46
  name: 'browser_navigate_back',
47
47
  title: 'Go back',
@@ -58,13 +58,13 @@ const goBack = captureSnapshot => defineTool({
58
58
  ];
59
59
  return {
60
60
  code,
61
- captureSnapshot,
61
+ captureSnapshot: true,
62
62
  waitForNetwork: false,
63
63
  };
64
64
  },
65
65
  });
66
- const goForward = captureSnapshot => defineTool({
67
- capability: 'history',
66
+ const goForward = defineTool({
67
+ capability: 'core',
68
68
  schema: {
69
69
  name: 'browser_navigate_forward',
70
70
  title: 'Go forward',
@@ -81,13 +81,13 @@ const goForward = captureSnapshot => defineTool({
81
81
  ];
82
82
  return {
83
83
  code,
84
- captureSnapshot,
84
+ captureSnapshot: true,
85
85
  waitForNetwork: false,
86
86
  };
87
87
  },
88
88
  });
89
- export default (captureSnapshot) => [
90
- navigate(captureSnapshot),
91
- goBack(captureSnapshot),
92
- goForward(captureSnapshot),
89
+ export default [
90
+ navigate,
91
+ goBack,
92
+ goForward,
93
93
  ];
@@ -67,7 +67,7 @@ const screenshot = defineTool({
67
67
  return {
68
68
  code,
69
69
  action,
70
- captureSnapshot: true,
70
+ captureSnapshot: false,
71
71
  waitForNetwork: false,
72
72
  };
73
73
  }
@@ -35,29 +35,40 @@ const snapshot = defineTool({
35
35
  };
36
36
  },
37
37
  });
38
- const elementSchema = z.object({
38
+ export const elementSchema = z.object({
39
39
  element: z.string().describe('Human-readable element description used to obtain permission to interact with the element'),
40
40
  ref: z.string().describe('Exact target element reference from the page snapshot'),
41
41
  });
42
+ const clickSchema = elementSchema.extend({
43
+ doubleClick: z.boolean().optional().describe('Whether to perform a double click instead of a single click'),
44
+ button: z.enum(['left', 'right', 'middle']).optional().describe('Button to click, defaults to left'),
45
+ });
42
46
  const click = defineTool({
43
47
  capability: 'core',
44
48
  schema: {
45
49
  name: 'browser_click',
46
50
  title: 'Click',
47
51
  description: 'Perform click on a web page',
48
- inputSchema: elementSchema,
52
+ inputSchema: clickSchema,
49
53
  type: 'destructive',
50
54
  },
51
55
  handle: async (context, params) => {
52
56
  const tab = context.currentTabOrDie();
53
57
  const locator = tab.snapshotOrDie().refLocator(params);
54
- const code = [
55
- `// Click ${params.element}`,
56
- `await page.${await generateLocator(locator)}.click();`
57
- ];
58
+ const button = params.button;
59
+ const buttonAttr = button ? `{ button: '${button}' }` : '';
60
+ const code = [];
61
+ if (params.doubleClick) {
62
+ code.push(`// Double click ${params.element}`);
63
+ code.push(`await page.${await generateLocator(locator)}.dblclick(${buttonAttr});`);
64
+ }
65
+ else {
66
+ code.push(`// Click ${params.element}`);
67
+ code.push(`await page.${await generateLocator(locator)}.click(${buttonAttr});`);
68
+ }
58
69
  return {
59
70
  code,
60
- action: () => locator.click(),
71
+ action: () => params.doubleClick ? locator.dblclick({ button }) : locator.click({ button }),
61
72
  captureSnapshot: true,
62
73
  waitForNetwork: true,
63
74
  };
@@ -117,48 +128,6 @@ const hover = defineTool({
117
128
  };
118
129
  },
119
130
  });
120
- const typeSchema = elementSchema.extend({
121
- text: z.string().describe('Text to type into the element'),
122
- submit: z.boolean().optional().describe('Whether to submit entered text (press Enter after)'),
123
- slowly: z.boolean().optional().describe('Whether to type one character at a time. Useful for triggering key handlers in the page. By default entire text is filled in at once.'),
124
- });
125
- const type = defineTool({
126
- capability: 'core',
127
- schema: {
128
- name: 'browser_type',
129
- title: 'Type text',
130
- description: 'Type text into editable element',
131
- inputSchema: typeSchema,
132
- type: 'destructive',
133
- },
134
- handle: async (context, params) => {
135
- const snapshot = context.currentTabOrDie().snapshotOrDie();
136
- const locator = snapshot.refLocator(params);
137
- const code = [];
138
- const steps = [];
139
- if (params.slowly) {
140
- code.push(`// Press "${params.text}" sequentially into "${params.element}"`);
141
- code.push(`await page.${await generateLocator(locator)}.pressSequentially(${javascript.quote(params.text)});`);
142
- steps.push(() => locator.pressSequentially(params.text));
143
- }
144
- else {
145
- code.push(`// Fill "${params.text}" into "${params.element}"`);
146
- code.push(`await page.${await generateLocator(locator)}.fill(${javascript.quote(params.text)});`);
147
- steps.push(() => locator.fill(params.text));
148
- }
149
- if (params.submit) {
150
- code.push(`// Submit text`);
151
- code.push(`await page.${await generateLocator(locator)}.press('Enter');`);
152
- steps.push(() => locator.press('Enter'));
153
- }
154
- return {
155
- code,
156
- action: () => steps.reduce((acc, step) => acc.then(step), Promise.resolve()),
157
- captureSnapshot: true,
158
- waitForNetwork: true,
159
- };
160
- },
161
- });
162
131
  const selectOptionSchema = elementSchema.extend({
163
132
  values: z.array(z.string()).describe('Array of values to select in the dropdown. This can be a single value or multiple values.'),
164
133
  });
@@ -191,6 +160,5 @@ export default [
191
160
  click,
192
161
  drag,
193
162
  hover,
194
- type,
195
163
  selectOption,
196
164
  ];
package/lib/tools/tabs.js CHANGED
@@ -16,7 +16,7 @@
16
16
  import { z } from 'zod';
17
17
  import { defineTool } from './tool.js';
18
18
  const listTabs = defineTool({
19
- capability: 'tabs',
19
+ capability: 'core-tabs',
20
20
  schema: {
21
21
  name: 'browser_tab_list',
22
22
  title: 'List tabs',
@@ -39,8 +39,8 @@ const listTabs = defineTool({
39
39
  };
40
40
  },
41
41
  });
42
- const selectTab = captureSnapshot => defineTool({
43
- capability: 'tabs',
42
+ const selectTab = defineTool({
43
+ capability: 'core-tabs',
44
44
  schema: {
45
45
  name: 'browser_tab_select',
46
46
  title: 'Select a tab',
@@ -57,13 +57,13 @@ const selectTab = captureSnapshot => defineTool({
57
57
  ];
58
58
  return {
59
59
  code,
60
- captureSnapshot,
60
+ captureSnapshot: true,
61
61
  waitForNetwork: false
62
62
  };
63
63
  },
64
64
  });
65
- const newTab = captureSnapshot => defineTool({
66
- capability: 'tabs',
65
+ const newTab = defineTool({
66
+ capability: 'core-tabs',
67
67
  schema: {
68
68
  name: 'browser_tab_new',
69
69
  title: 'Open a new tab',
@@ -82,13 +82,13 @@ const newTab = captureSnapshot => defineTool({
82
82
  ];
83
83
  return {
84
84
  code,
85
- captureSnapshot,
85
+ captureSnapshot: true,
86
86
  waitForNetwork: false
87
87
  };
88
88
  },
89
89
  });
90
- const closeTab = captureSnapshot => defineTool({
91
- capability: 'tabs',
90
+ const closeTab = defineTool({
91
+ capability: 'core-tabs',
92
92
  schema: {
93
93
  name: 'browser_tab_close',
94
94
  title: 'Close a tab',
@@ -105,14 +105,14 @@ const closeTab = captureSnapshot => defineTool({
105
105
  ];
106
106
  return {
107
107
  code,
108
- captureSnapshot,
108
+ captureSnapshot: true,
109
109
  waitForNetwork: false
110
110
  };
111
111
  },
112
112
  });
113
- export default (captureSnapshot) => [
113
+ export default [
114
114
  listTabs,
115
- newTab(captureSnapshot),
116
- selectTab(captureSnapshot),
117
- closeTab(captureSnapshot),
115
+ newTab,
116
+ selectTab,
117
+ closeTab,
118
118
  ];
@@ -13,6 +13,8 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
+ // @ts-ignore
17
+ import { asLocator } from 'playwright-core/lib/utils';
16
18
  export async function waitForCompletion(context, tab, callback) {
17
19
  const requests = new Set();
18
20
  let frameNavigated = false;
@@ -66,7 +68,13 @@ export function sanitizeForFilePath(s) {
66
68
  return sanitize(s.substring(0, separator)) + '.' + sanitize(s.substring(separator + 1));
67
69
  }
68
70
  export async function generateLocator(locator) {
69
- return locator._generateLocatorString();
71
+ try {
72
+ const { resolvedSelector } = await locator._resolveSelector();
73
+ return asLocator('javascript', resolvedSelector);
74
+ }
75
+ catch (e) {
76
+ throw new Error('Ref not found, likely because element was removed. Use browser_snapshot to see what elements are currently on the page.');
77
+ }
70
78
  }
71
79
  export async function callOnPageNoTrace(page, callback) {
72
80
  return await page._wrapApiCall(() => callback(page), { internal: true });
package/lib/tools/wait.js CHANGED
@@ -15,8 +15,8 @@
15
15
  */
16
16
  import { z } from 'zod';
17
17
  import { defineTool } from './tool.js';
18
- const wait = captureSnapshot => defineTool({
19
- capability: 'wait',
18
+ const wait = defineTool({
19
+ capability: 'core',
20
20
  schema: {
21
21
  name: 'browser_wait_for',
22
22
  title: 'Wait for',
@@ -34,7 +34,7 @@ const wait = captureSnapshot => defineTool({
34
34
  const code = [];
35
35
  if (params.time) {
36
36
  code.push(`await new Promise(f => setTimeout(f, ${params.time} * 1000));`);
37
- await new Promise(f => setTimeout(f, Math.min(10000, params.time * 1000)));
37
+ await new Promise(f => setTimeout(f, Math.min(30000, params.time * 1000)));
38
38
  }
39
39
  const tab = context.currentTabOrDie();
40
40
  const locator = params.text ? tab.page.getByText(params.text).first() : undefined;
@@ -49,11 +49,11 @@ const wait = captureSnapshot => defineTool({
49
49
  }
50
50
  return {
51
51
  code,
52
- captureSnapshot,
52
+ captureSnapshot: true,
53
53
  waitForNetwork: false,
54
54
  };
55
55
  },
56
56
  });
57
- export default (captureSnapshot) => [
58
- wait(captureSnapshot),
57
+ export default [
58
+ wait,
59
59
  ];
package/lib/tools.js CHANGED
@@ -16,6 +16,7 @@
16
16
  import common from './tools/common.js';
17
17
  import console from './tools/console.js';
18
18
  import dialogs from './tools/dialogs.js';
19
+ import evaluate from './tools/evaluate.js';
19
20
  import files from './tools/files.js';
20
21
  import install from './tools/install.js';
21
22
  import keyboard from './tools/keyboard.js';
@@ -25,37 +26,22 @@ import pdf from './tools/pdf.js';
25
26
  import snapshot from './tools/snapshot.js';
26
27
  import tabs from './tools/tabs.js';
27
28
  import screenshot from './tools/screenshot.js';
28
- import testing from './tools/testing.js';
29
- import vision from './tools/vision.js';
30
29
  import wait from './tools/wait.js';
31
- export const snapshotTools = [
32
- ...common(true),
30
+ import mouse from './tools/mouse.js';
31
+ export const allTools = [
32
+ ...common,
33
33
  ...console,
34
- ...dialogs(true),
35
- ...files(true),
34
+ ...dialogs,
35
+ ...evaluate,
36
+ ...files,
36
37
  ...install,
37
- ...keyboard(true),
38
- ...navigate(true),
38
+ ...keyboard,
39
+ ...navigate,
39
40
  ...network,
41
+ ...mouse,
40
42
  ...pdf,
41
43
  ...screenshot,
42
44
  ...snapshot,
43
- ...tabs(true),
44
- ...testing,
45
- ...wait(true),
46
- ];
47
- export const visionTools = [
48
- ...common(false),
49
- ...console,
50
- ...dialogs(false),
51
- ...files(false),
52
- ...install,
53
- ...keyboard(false),
54
- ...navigate(false),
55
- ...network,
56
- ...pdf,
57
- ...tabs(false),
58
- ...testing,
59
- ...vision,
60
- ...wait(false),
45
+ ...tabs,
46
+ ...wait,
61
47
  ];