@playwright/mcp 0.0.20 → 0.0.22
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 +84 -9
- package/config.d.ts +14 -14
- package/index.d.ts +8 -2
- package/index.js +2 -2
- package/lib/config.js +14 -16
- package/lib/{server.js → connection.js} +31 -28
- package/lib/context.js +36 -2
- package/lib/index.js +2 -51
- package/lib/program.js +15 -9
- package/lib/tab.js +18 -1
- package/lib/tools/common.js +6 -0
- package/lib/tools/console.js +2 -0
- package/lib/tools/dialogs.js +2 -0
- package/lib/tools/files.js +2 -0
- package/lib/tools/install.js +6 -4
- package/lib/tools/keyboard.js +2 -0
- package/lib/tools/navigate.js +6 -0
- package/lib/tools/network.js +2 -0
- package/lib/tools/pdf.js +2 -0
- package/lib/tools/screen.js +10 -0
- package/lib/tools/snapshot.js +15 -1
- package/lib/tools/tabs.js +8 -0
- package/lib/tools/testing.js +2 -0
- package/lib/tools.js +56 -0
- package/lib/transport.js +23 -15
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -76,7 +76,10 @@ The Playwright MCP server supports the following command-line options:
|
|
|
76
76
|
- `--user-data-dir <path>`: Path to the user data directory
|
|
77
77
|
- `--port <port>`: Port to listen on for SSE transport
|
|
78
78
|
- `--host <host>`: Host to bind server to. Default is localhost. Use 0.0.0.0 to bind to all interfaces.
|
|
79
|
+
- `--allowed-origins <origins>`: Semicolon-separated list of origins to allow the browser to request. Default is to allow all. Origins matching both `--allowed-origins` and `--blocked-origins` will be blocked.
|
|
80
|
+
- `--blocked-origins <origins>`: Semicolon-separated list of origins to block the browser to request. Origins matching both `--allowed-origins` and `--blocked-origins` will be blocked.
|
|
79
81
|
- `--vision`: Run server that uses screenshots (Aria snapshots are used by default)
|
|
82
|
+
- `--output-dir`: Directory for output files
|
|
80
83
|
- `--config <path>`: Path to the configuration file
|
|
81
84
|
|
|
82
85
|
### User profile
|
|
@@ -152,13 +155,19 @@ The Playwright MCP server can be configured using a JSON configuration file. Her
|
|
|
152
155
|
// Directory for output files
|
|
153
156
|
outputDir?: string;
|
|
154
157
|
|
|
155
|
-
//
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
158
|
+
// Network configuration
|
|
159
|
+
network?: {
|
|
160
|
+
// List of origins to allow the browser to request. Default is to allow all. Origins matching both `allowedOrigins` and `blockedOrigins` will be blocked.
|
|
161
|
+
allowedOrigins?: string[];
|
|
162
|
+
|
|
163
|
+
// List of origins to block the browser to request. Origins matching both `allowedOrigins` and `blockedOrigins` will be blocked.
|
|
164
|
+
blockedOrigins?: string[];
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Do not send image responses to the client.
|
|
169
|
+
*/
|
|
170
|
+
noImageResponses?: boolean;
|
|
162
171
|
}
|
|
163
172
|
```
|
|
164
173
|
|
|
@@ -222,9 +231,9 @@ http.createServer(async (req, res) => {
|
|
|
222
231
|
// ...
|
|
223
232
|
|
|
224
233
|
// Creates a headless Playwright MCP server with SSE transport
|
|
225
|
-
const
|
|
234
|
+
const connection = await createConnection({ headless: true });
|
|
226
235
|
const transport = new SSEServerTransport('/messages', res);
|
|
227
|
-
await
|
|
236
|
+
await connection.connect(transport);
|
|
228
237
|
|
|
229
238
|
// ...
|
|
230
239
|
});
|
|
@@ -264,38 +273,47 @@ X Y coordinate space, based on the provided screenshot.
|
|
|
264
273
|
<!-- NOTE: This has been generated via update-readme.js -->
|
|
265
274
|
|
|
266
275
|
- **browser_snapshot**
|
|
276
|
+
- Title: Page snapshot
|
|
267
277
|
- Description: Capture accessibility snapshot of the current page, this is better than screenshot
|
|
268
278
|
- Parameters: None
|
|
279
|
+
- Read-only: **true**
|
|
269
280
|
|
|
270
281
|
<!-- NOTE: This has been generated via update-readme.js -->
|
|
271
282
|
|
|
272
283
|
- **browser_click**
|
|
284
|
+
- Title: Click
|
|
273
285
|
- Description: Perform click on a web page
|
|
274
286
|
- Parameters:
|
|
275
287
|
- `element` (string): Human-readable element description used to obtain permission to interact with the element
|
|
276
288
|
- `ref` (string): Exact target element reference from the page snapshot
|
|
289
|
+
- Read-only: **false**
|
|
277
290
|
|
|
278
291
|
<!-- NOTE: This has been generated via update-readme.js -->
|
|
279
292
|
|
|
280
293
|
- **browser_drag**
|
|
294
|
+
- Title: Drag mouse
|
|
281
295
|
- Description: Perform drag and drop between two elements
|
|
282
296
|
- Parameters:
|
|
283
297
|
- `startElement` (string): Human-readable source element description used to obtain the permission to interact with the element
|
|
284
298
|
- `startRef` (string): Exact source element reference from the page snapshot
|
|
285
299
|
- `endElement` (string): Human-readable target element description used to obtain the permission to interact with the element
|
|
286
300
|
- `endRef` (string): Exact target element reference from the page snapshot
|
|
301
|
+
- Read-only: **false**
|
|
287
302
|
|
|
288
303
|
<!-- NOTE: This has been generated via update-readme.js -->
|
|
289
304
|
|
|
290
305
|
- **browser_hover**
|
|
306
|
+
- Title: Hover mouse
|
|
291
307
|
- Description: Hover over element on page
|
|
292
308
|
- Parameters:
|
|
293
309
|
- `element` (string): Human-readable element description used to obtain permission to interact with the element
|
|
294
310
|
- `ref` (string): Exact target element reference from the page snapshot
|
|
311
|
+
- Read-only: **true**
|
|
295
312
|
|
|
296
313
|
<!-- NOTE: This has been generated via update-readme.js -->
|
|
297
314
|
|
|
298
315
|
- **browser_type**
|
|
316
|
+
- Title: Type text
|
|
299
317
|
- Description: Type text into editable element
|
|
300
318
|
- Parameters:
|
|
301
319
|
- `element` (string): Human-readable element description used to obtain permission to interact with the element
|
|
@@ -303,54 +321,66 @@ X Y coordinate space, based on the provided screenshot.
|
|
|
303
321
|
- `text` (string): Text to type into the element
|
|
304
322
|
- `submit` (boolean, optional): Whether to submit entered text (press Enter after)
|
|
305
323
|
- `slowly` (boolean, optional): 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.
|
|
324
|
+
- Read-only: **false**
|
|
306
325
|
|
|
307
326
|
<!-- NOTE: This has been generated via update-readme.js -->
|
|
308
327
|
|
|
309
328
|
- **browser_select_option**
|
|
329
|
+
- Title: Select option
|
|
310
330
|
- Description: Select an option in a dropdown
|
|
311
331
|
- Parameters:
|
|
312
332
|
- `element` (string): Human-readable element description used to obtain permission to interact with the element
|
|
313
333
|
- `ref` (string): Exact target element reference from the page snapshot
|
|
314
334
|
- `values` (array): Array of values to select in the dropdown. This can be a single value or multiple values.
|
|
335
|
+
- Read-only: **false**
|
|
315
336
|
|
|
316
337
|
<!-- NOTE: This has been generated via update-readme.js -->
|
|
317
338
|
|
|
318
339
|
- **browser_take_screenshot**
|
|
340
|
+
- Title: Take a screenshot
|
|
319
341
|
- Description: Take a screenshot of the current page. You can't perform actions based on the screenshot, use browser_snapshot for actions.
|
|
320
342
|
- Parameters:
|
|
321
343
|
- `raw` (boolean, optional): Whether to return without compression (in PNG format). Default is false, which returns a JPEG image.
|
|
322
344
|
- `element` (string, optional): 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.
|
|
323
345
|
- `ref` (string, optional): 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.
|
|
346
|
+
- Read-only: **true**
|
|
324
347
|
|
|
325
348
|
### Vision-based Interactions
|
|
326
349
|
|
|
327
350
|
<!-- NOTE: This has been generated via update-readme.js -->
|
|
328
351
|
|
|
329
352
|
- **browser_screen_capture**
|
|
353
|
+
- Title: Take a screenshot
|
|
330
354
|
- Description: Take a screenshot of the current page
|
|
331
355
|
- Parameters: None
|
|
356
|
+
- Read-only: **true**
|
|
332
357
|
|
|
333
358
|
<!-- NOTE: This has been generated via update-readme.js -->
|
|
334
359
|
|
|
335
360
|
- **browser_screen_move_mouse**
|
|
361
|
+
- Title: Move mouse
|
|
336
362
|
- Description: Move mouse to a given position
|
|
337
363
|
- Parameters:
|
|
338
364
|
- `element` (string): Human-readable element description used to obtain permission to interact with the element
|
|
339
365
|
- `x` (number): X coordinate
|
|
340
366
|
- `y` (number): Y coordinate
|
|
367
|
+
- Read-only: **true**
|
|
341
368
|
|
|
342
369
|
<!-- NOTE: This has been generated via update-readme.js -->
|
|
343
370
|
|
|
344
371
|
- **browser_screen_click**
|
|
372
|
+
- Title: Click
|
|
345
373
|
- Description: Click left mouse button
|
|
346
374
|
- Parameters:
|
|
347
375
|
- `element` (string): Human-readable element description used to obtain permission to interact with the element
|
|
348
376
|
- `x` (number): X coordinate
|
|
349
377
|
- `y` (number): Y coordinate
|
|
378
|
+
- Read-only: **false**
|
|
350
379
|
|
|
351
380
|
<!-- NOTE: This has been generated via update-readme.js -->
|
|
352
381
|
|
|
353
382
|
- **browser_screen_drag**
|
|
383
|
+
- Title: Drag mouse
|
|
354
384
|
- Description: Drag left mouse button
|
|
355
385
|
- Parameters:
|
|
356
386
|
- `element` (string): Human-readable element description used to obtain permission to interact with the element
|
|
@@ -358,143 +388,188 @@ X Y coordinate space, based on the provided screenshot.
|
|
|
358
388
|
- `startY` (number): Start Y coordinate
|
|
359
389
|
- `endX` (number): End X coordinate
|
|
360
390
|
- `endY` (number): End Y coordinate
|
|
391
|
+
- Read-only: **false**
|
|
361
392
|
|
|
362
393
|
<!-- NOTE: This has been generated via update-readme.js -->
|
|
363
394
|
|
|
364
395
|
- **browser_screen_type**
|
|
396
|
+
- Title: Type text
|
|
365
397
|
- Description: Type text
|
|
366
398
|
- Parameters:
|
|
367
399
|
- `text` (string): Text to type into the element
|
|
368
400
|
- `submit` (boolean, optional): Whether to submit entered text (press Enter after)
|
|
401
|
+
- Read-only: **false**
|
|
369
402
|
|
|
370
403
|
### Tab Management
|
|
371
404
|
|
|
372
405
|
<!-- NOTE: This has been generated via update-readme.js -->
|
|
373
406
|
|
|
374
407
|
- **browser_tab_list**
|
|
408
|
+
- Title: List tabs
|
|
375
409
|
- Description: List browser tabs
|
|
376
410
|
- Parameters: None
|
|
411
|
+
- Read-only: **true**
|
|
377
412
|
|
|
378
413
|
<!-- NOTE: This has been generated via update-readme.js -->
|
|
379
414
|
|
|
380
415
|
- **browser_tab_new**
|
|
416
|
+
- Title: Open a new tab
|
|
381
417
|
- Description: Open a new tab
|
|
382
418
|
- Parameters:
|
|
383
419
|
- `url` (string, optional): The URL to navigate to in the new tab. If not provided, the new tab will be blank.
|
|
420
|
+
- Read-only: **true**
|
|
384
421
|
|
|
385
422
|
<!-- NOTE: This has been generated via update-readme.js -->
|
|
386
423
|
|
|
387
424
|
- **browser_tab_select**
|
|
425
|
+
- Title: Select a tab
|
|
388
426
|
- Description: Select a tab by index
|
|
389
427
|
- Parameters:
|
|
390
428
|
- `index` (number): The index of the tab to select
|
|
429
|
+
- Read-only: **true**
|
|
391
430
|
|
|
392
431
|
<!-- NOTE: This has been generated via update-readme.js -->
|
|
393
432
|
|
|
394
433
|
- **browser_tab_close**
|
|
434
|
+
- Title: Close a tab
|
|
395
435
|
- Description: Close a tab
|
|
396
436
|
- Parameters:
|
|
397
437
|
- `index` (number, optional): The index of the tab to close. Closes current tab if not provided.
|
|
438
|
+
- Read-only: **false**
|
|
398
439
|
|
|
399
440
|
### Navigation
|
|
400
441
|
|
|
401
442
|
<!-- NOTE: This has been generated via update-readme.js -->
|
|
402
443
|
|
|
403
444
|
- **browser_navigate**
|
|
445
|
+
- Title: Navigate to a URL
|
|
404
446
|
- Description: Navigate to a URL
|
|
405
447
|
- Parameters:
|
|
406
448
|
- `url` (string): The URL to navigate to
|
|
449
|
+
- Read-only: **false**
|
|
407
450
|
|
|
408
451
|
<!-- NOTE: This has been generated via update-readme.js -->
|
|
409
452
|
|
|
410
453
|
- **browser_navigate_back**
|
|
454
|
+
- Title: Go back
|
|
411
455
|
- Description: Go back to the previous page
|
|
412
456
|
- Parameters: None
|
|
457
|
+
- Read-only: **true**
|
|
413
458
|
|
|
414
459
|
<!-- NOTE: This has been generated via update-readme.js -->
|
|
415
460
|
|
|
416
461
|
- **browser_navigate_forward**
|
|
462
|
+
- Title: Go forward
|
|
417
463
|
- Description: Go forward to the next page
|
|
418
464
|
- Parameters: None
|
|
465
|
+
- Read-only: **true**
|
|
419
466
|
|
|
420
467
|
### Keyboard
|
|
421
468
|
|
|
422
469
|
<!-- NOTE: This has been generated via update-readme.js -->
|
|
423
470
|
|
|
424
471
|
- **browser_press_key**
|
|
472
|
+
- Title: Press a key
|
|
425
473
|
- Description: Press a key on the keyboard
|
|
426
474
|
- Parameters:
|
|
427
475
|
- `key` (string): Name of the key to press or a character to generate, such as `ArrowLeft` or `a`
|
|
476
|
+
- Read-only: **false**
|
|
428
477
|
|
|
429
478
|
### Console
|
|
430
479
|
|
|
431
480
|
<!-- NOTE: This has been generated via update-readme.js -->
|
|
432
481
|
|
|
433
482
|
- **browser_console_messages**
|
|
483
|
+
- Title: Get console messages
|
|
434
484
|
- Description: Returns all console messages
|
|
435
485
|
- Parameters: None
|
|
486
|
+
- Read-only: **true**
|
|
436
487
|
|
|
437
488
|
### Files and Media
|
|
438
489
|
|
|
439
490
|
<!-- NOTE: This has been generated via update-readme.js -->
|
|
440
491
|
|
|
441
492
|
- **browser_file_upload**
|
|
493
|
+
- Title: Upload files
|
|
442
494
|
- Description: Upload one or multiple files
|
|
443
495
|
- Parameters:
|
|
444
496
|
- `paths` (array): The absolute paths to the files to upload. Can be a single file or multiple files.
|
|
497
|
+
- Read-only: **false**
|
|
445
498
|
|
|
446
499
|
<!-- NOTE: This has been generated via update-readme.js -->
|
|
447
500
|
|
|
448
501
|
- **browser_pdf_save**
|
|
502
|
+
- Title: Save as PDF
|
|
449
503
|
- Description: Save page as PDF
|
|
450
504
|
- Parameters: None
|
|
505
|
+
- Read-only: **true**
|
|
451
506
|
|
|
452
507
|
### Utilities
|
|
453
508
|
|
|
454
509
|
<!-- NOTE: This has been generated via update-readme.js -->
|
|
455
510
|
|
|
456
511
|
- **browser_close**
|
|
512
|
+
- Title: Close browser
|
|
457
513
|
- Description: Close the page
|
|
458
514
|
- Parameters: None
|
|
515
|
+
- Read-only: **true**
|
|
459
516
|
|
|
460
517
|
<!-- NOTE: This has been generated via update-readme.js -->
|
|
461
518
|
|
|
462
519
|
- **browser_wait**
|
|
520
|
+
- Title: Wait
|
|
463
521
|
- Description: Wait for a specified time in seconds
|
|
464
522
|
- Parameters:
|
|
465
523
|
- `time` (number): The time to wait in seconds
|
|
524
|
+
- Read-only: **true**
|
|
466
525
|
|
|
467
526
|
<!-- NOTE: This has been generated via update-readme.js -->
|
|
468
527
|
|
|
469
528
|
- **browser_resize**
|
|
529
|
+
- Title: Resize browser window
|
|
470
530
|
- Description: Resize the browser window
|
|
471
531
|
- Parameters:
|
|
472
532
|
- `width` (number): Width of the browser window
|
|
473
533
|
- `height` (number): Height of the browser window
|
|
534
|
+
- Read-only: **true**
|
|
474
535
|
|
|
475
536
|
<!-- NOTE: This has been generated via update-readme.js -->
|
|
476
537
|
|
|
477
538
|
- **browser_install**
|
|
539
|
+
- Title: Install the browser specified in the config
|
|
478
540
|
- Description: Install the browser specified in the config. Call this if you get an error about the browser not being installed.
|
|
479
541
|
- Parameters: None
|
|
542
|
+
- Read-only: **false**
|
|
480
543
|
|
|
481
544
|
<!-- NOTE: This has been generated via update-readme.js -->
|
|
482
545
|
|
|
483
546
|
- **browser_handle_dialog**
|
|
547
|
+
- Title: Handle a dialog
|
|
484
548
|
- Description: Handle a dialog
|
|
485
549
|
- Parameters:
|
|
486
550
|
- `accept` (boolean): Whether to accept the dialog.
|
|
487
551
|
- `promptText` (string, optional): The text of the prompt in case of a prompt dialog.
|
|
552
|
+
- Read-only: **false**
|
|
553
|
+
|
|
554
|
+
<!-- NOTE: This has been generated via update-readme.js -->
|
|
555
|
+
|
|
556
|
+
- **browser_network_requests**
|
|
557
|
+
- Title: List network requests
|
|
558
|
+
- Description: Returns all network requests since loading the page
|
|
559
|
+
- Parameters: None
|
|
560
|
+
- Read-only: **true**
|
|
488
561
|
|
|
489
562
|
### Testing
|
|
490
563
|
|
|
491
564
|
<!-- NOTE: This has been generated via update-readme.js -->
|
|
492
565
|
|
|
493
566
|
- **browser_generate_playwright_test**
|
|
567
|
+
- Title: Generate a Playwright test
|
|
494
568
|
- Description: Generate a Playwright test for given scenario
|
|
495
569
|
- Parameters:
|
|
496
570
|
- `name` (string): The name of the test
|
|
497
571
|
- `description` (string): The description of the test
|
|
498
572
|
- `steps` (array): The steps of the test
|
|
573
|
+
- Read-only: **true**
|
|
499
574
|
|
|
500
575
|
<!--- End of generated section -->
|
package/config.d.ts
CHANGED
|
@@ -94,20 +94,20 @@ export type Config = {
|
|
|
94
94
|
*/
|
|
95
95
|
outputDir?: string;
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
97
|
+
network?: {
|
|
98
|
+
/**
|
|
99
|
+
* List of origins to allow the browser to request. Default is to allow all. Origins matching both `allowedOrigins` and `blockedOrigins` will be blocked.
|
|
100
|
+
*/
|
|
101
|
+
allowedOrigins?: string[];
|
|
102
|
+
|
|
101
103
|
/**
|
|
102
|
-
*
|
|
104
|
+
* List of origins to block the browser to request. Origins matching both `allowedOrigins` and `blockedOrigins` will be blocked.
|
|
103
105
|
*/
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
112
|
-
}
|
|
106
|
+
blockedOrigins?: string[];
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Do not send image responses to the client.
|
|
111
|
+
*/
|
|
112
|
+
noImageResponses?: boolean;
|
|
113
113
|
};
|
package/index.d.ts
CHANGED
|
@@ -16,8 +16,14 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import type { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
19
|
-
|
|
20
19
|
import type { Config } from './config';
|
|
20
|
+
import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
|
21
|
+
|
|
22
|
+
export type Connection = {
|
|
23
|
+
server: Server;
|
|
24
|
+
connect(transport: Transport): Promise<void>;
|
|
25
|
+
close(): Promise<void>;
|
|
26
|
+
};
|
|
21
27
|
|
|
22
|
-
export declare function
|
|
28
|
+
export declare function createConnection(config?: Config): Promise<Connection>;
|
|
23
29
|
export {};
|
package/index.js
CHANGED
package/lib/config.js
CHANGED
|
@@ -22,7 +22,6 @@ import { sanitizeForFilePath } from './tools/utils.js';
|
|
|
22
22
|
const defaultConfig = {
|
|
23
23
|
browser: {
|
|
24
24
|
browserName: 'chromium',
|
|
25
|
-
userDataDir: os.tmpdir(),
|
|
26
25
|
launchOptions: {
|
|
27
26
|
channel: 'chrome',
|
|
28
27
|
headless: os.platform() === 'linux' && !process.env.DISPLAY,
|
|
@@ -31,6 +30,10 @@ const defaultConfig = {
|
|
|
31
30
|
viewport: null,
|
|
32
31
|
},
|
|
33
32
|
},
|
|
33
|
+
network: {
|
|
34
|
+
allowedOrigins: undefined,
|
|
35
|
+
blockedOrigins: undefined,
|
|
36
|
+
},
|
|
34
37
|
};
|
|
35
38
|
export async function resolveConfig(cliOptions) {
|
|
36
39
|
const config = await loadConfig(cliOptions.config);
|
|
@@ -74,7 +77,7 @@ export async function configFromCLIOptions(cliOptions) {
|
|
|
74
77
|
return {
|
|
75
78
|
browser: {
|
|
76
79
|
browserName,
|
|
77
|
-
userDataDir: cliOptions.userDataDir
|
|
80
|
+
userDataDir: cliOptions.userDataDir,
|
|
78
81
|
launchOptions,
|
|
79
82
|
contextOptions,
|
|
80
83
|
cdpEndpoint: cliOptions.cdpEndpoint,
|
|
@@ -85,6 +88,11 @@ export async function configFromCLIOptions(cliOptions) {
|
|
|
85
88
|
},
|
|
86
89
|
capabilities: cliOptions.caps?.split(',').map((c) => c.trim()),
|
|
87
90
|
vision: !!cliOptions.vision,
|
|
91
|
+
network: {
|
|
92
|
+
allowedOrigins: cliOptions.allowedOrigins,
|
|
93
|
+
blockedOrigins: cliOptions.blockedOrigins,
|
|
94
|
+
},
|
|
95
|
+
outputDir: cliOptions.outputDir,
|
|
88
96
|
};
|
|
89
97
|
}
|
|
90
98
|
async function findFreePort() {
|
|
@@ -107,20 +115,6 @@ async function loadConfig(configFile) {
|
|
|
107
115
|
throw new Error(`Failed to load config file: ${configFile}, ${error}`);
|
|
108
116
|
}
|
|
109
117
|
}
|
|
110
|
-
async function createUserDataDir(options) {
|
|
111
|
-
let cacheDirectory;
|
|
112
|
-
if (process.platform === 'linux')
|
|
113
|
-
cacheDirectory = process.env.XDG_CACHE_HOME || path.join(os.homedir(), '.cache');
|
|
114
|
-
else if (process.platform === 'darwin')
|
|
115
|
-
cacheDirectory = path.join(os.homedir(), 'Library', 'Caches');
|
|
116
|
-
else if (process.platform === 'win32')
|
|
117
|
-
cacheDirectory = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
|
|
118
|
-
else
|
|
119
|
-
throw new Error('Unsupported platform: ' + process.platform);
|
|
120
|
-
const result = path.join(cacheDirectory, 'ms-playwright', `mcp-${options.channel ?? options.browserName}-profile`);
|
|
121
|
-
await fs.promises.mkdir(result, { recursive: true });
|
|
122
|
-
return result;
|
|
123
|
-
}
|
|
124
118
|
export async function outputFile(config, name) {
|
|
125
119
|
const result = config.outputDir ?? os.tmpdir();
|
|
126
120
|
await fs.promises.mkdir(result, { recursive: true });
|
|
@@ -150,5 +144,9 @@ function mergeConfig(base, overrides) {
|
|
|
150
144
|
...pickDefined(base),
|
|
151
145
|
...pickDefined(overrides),
|
|
152
146
|
browser,
|
|
147
|
+
network: {
|
|
148
|
+
...pickDefined(base.network),
|
|
149
|
+
...pickDefined(overrides.network),
|
|
150
|
+
},
|
|
153
151
|
};
|
|
154
152
|
}
|
|
@@ -16,11 +16,13 @@
|
|
|
16
16
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
17
17
|
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
18
18
|
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
19
|
-
import { Context } from './context.js';
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
import { Context, packageJSON } from './context.js';
|
|
20
|
+
import { snapshotTools, screenshotTools } from './tools.js';
|
|
21
|
+
export async function createConnection(config) {
|
|
22
|
+
const allTools = config.vision ? screenshotTools : snapshotTools;
|
|
23
|
+
const tools = allTools.filter(tool => !config.capabilities || tool.capability === 'core' || config.capabilities.includes(tool.capability));
|
|
22
24
|
const context = new Context(tools, config);
|
|
23
|
-
const server = new Server({ name, version }, {
|
|
25
|
+
const server = new Server({ name: 'Playwright', version: packageJSON.version }, {
|
|
24
26
|
capabilities: {
|
|
25
27
|
tools: {},
|
|
26
28
|
}
|
|
@@ -30,7 +32,13 @@ export function createServerWithTools(serverOptions, config) {
|
|
|
30
32
|
tools: tools.map(tool => ({
|
|
31
33
|
name: tool.schema.name,
|
|
32
34
|
description: tool.schema.description,
|
|
33
|
-
inputSchema: zodToJsonSchema(tool.schema.inputSchema)
|
|
35
|
+
inputSchema: zodToJsonSchema(tool.schema.inputSchema),
|
|
36
|
+
annotations: {
|
|
37
|
+
title: tool.schema.title,
|
|
38
|
+
readOnlyHint: tool.schema.type === 'readOnly',
|
|
39
|
+
destructiveHint: tool.schema.type === 'destructive',
|
|
40
|
+
openWorldHint: true,
|
|
41
|
+
},
|
|
34
42
|
})),
|
|
35
43
|
};
|
|
36
44
|
});
|
|
@@ -54,31 +62,26 @@ export function createServerWithTools(serverOptions, config) {
|
|
|
54
62
|
return errorResult(String(error));
|
|
55
63
|
}
|
|
56
64
|
});
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
await oldClose();
|
|
60
|
-
await context.close();
|
|
61
|
-
};
|
|
62
|
-
return server;
|
|
65
|
+
const connection = new Connection(server, context);
|
|
66
|
+
return connection;
|
|
63
67
|
}
|
|
64
|
-
export class
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
constructor(
|
|
68
|
-
this.
|
|
68
|
+
export class Connection {
|
|
69
|
+
server;
|
|
70
|
+
context;
|
|
71
|
+
constructor(server, context) {
|
|
72
|
+
this.server = server;
|
|
73
|
+
this.context = context;
|
|
69
74
|
}
|
|
70
|
-
async
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if (index !== -1)
|
|
78
|
-
this._servers.splice(index, 1);
|
|
79
|
-
await server.close();
|
|
75
|
+
async connect(transport) {
|
|
76
|
+
await this.server.connect(transport);
|
|
77
|
+
await new Promise(resolve => {
|
|
78
|
+
this.server.oninitialized = () => resolve();
|
|
79
|
+
});
|
|
80
|
+
if (this.server.getClientVersion()?.name.includes('cursor'))
|
|
81
|
+
this.context.config.noImageResponses = true;
|
|
80
82
|
}
|
|
81
|
-
async
|
|
82
|
-
await
|
|
83
|
+
async close() {
|
|
84
|
+
await this.server.close();
|
|
85
|
+
await this.context.close();
|
|
83
86
|
}
|
|
84
87
|
}
|
package/lib/context.js
CHANGED
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
+
import fs from 'node:fs';
|
|
17
|
+
import url from 'node:url';
|
|
18
|
+
import os from 'node:os';
|
|
19
|
+
import path from 'node:path';
|
|
16
20
|
import * as playwright from 'playwright';
|
|
17
21
|
import { waitForCompletion } from './tools/utils.js';
|
|
18
22
|
import { ManualPromise } from './manualPromise.js';
|
|
@@ -237,11 +241,23 @@ ${code.join('\n')}
|
|
|
237
241
|
await browser?.close();
|
|
238
242
|
}).catch(() => { });
|
|
239
243
|
}
|
|
244
|
+
async _setupRequestInterception(context) {
|
|
245
|
+
if (this.config.network?.allowedOrigins?.length) {
|
|
246
|
+
await context.route('**', route => route.abort('blockedbyclient'));
|
|
247
|
+
for (const origin of this.config.network.allowedOrigins)
|
|
248
|
+
await context.route(`*://${origin}/**`, route => route.continue());
|
|
249
|
+
}
|
|
250
|
+
if (this.config.network?.blockedOrigins?.length) {
|
|
251
|
+
for (const origin of this.config.network.blockedOrigins)
|
|
252
|
+
await context.route(`*://${origin}/**`, route => route.abort('blockedbyclient'));
|
|
253
|
+
}
|
|
254
|
+
}
|
|
240
255
|
async _ensureBrowserContext() {
|
|
241
256
|
if (!this._browserContext) {
|
|
242
257
|
const context = await this._createBrowserContext();
|
|
243
258
|
this._browser = context.browser;
|
|
244
259
|
this._browserContext = context.browserContext;
|
|
260
|
+
await this._setupRequestInterception(this._browserContext);
|
|
245
261
|
for (const page of this._browserContext.pages())
|
|
246
262
|
this._onPageCreated(page);
|
|
247
263
|
this._browserContext.on('page', page => this._onPageCreated(page));
|
|
@@ -279,8 +295,10 @@ ${code.join('\n')}
|
|
|
279
295
|
}
|
|
280
296
|
async function launchPersistentContext(browserConfig) {
|
|
281
297
|
try {
|
|
282
|
-
const
|
|
283
|
-
|
|
298
|
+
const browserName = browserConfig?.browserName ?? 'chromium';
|
|
299
|
+
const userDataDir = browserConfig?.userDataDir ?? await createUserDataDir({ ...browserConfig, browserName });
|
|
300
|
+
const browserType = playwright[browserName];
|
|
301
|
+
return await browserType.launchPersistentContext(userDataDir, { ...browserConfig?.launchOptions, ...browserConfig?.contextOptions });
|
|
284
302
|
}
|
|
285
303
|
catch (error) {
|
|
286
304
|
if (error.message.includes('Executable doesn\'t exist'))
|
|
@@ -288,6 +306,22 @@ async function launchPersistentContext(browserConfig) {
|
|
|
288
306
|
throw error;
|
|
289
307
|
}
|
|
290
308
|
}
|
|
309
|
+
async function createUserDataDir(browserConfig) {
|
|
310
|
+
let cacheDirectory;
|
|
311
|
+
if (process.platform === 'linux')
|
|
312
|
+
cacheDirectory = process.env.XDG_CACHE_HOME || path.join(os.homedir(), '.cache');
|
|
313
|
+
else if (process.platform === 'darwin')
|
|
314
|
+
cacheDirectory = path.join(os.homedir(), 'Library', 'Caches');
|
|
315
|
+
else if (process.platform === 'win32')
|
|
316
|
+
cacheDirectory = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
|
|
317
|
+
else
|
|
318
|
+
throw new Error('Unsupported platform: ' + process.platform);
|
|
319
|
+
const result = path.join(cacheDirectory, 'ms-playwright', `mcp-${browserConfig?.launchOptions.channel ?? browserConfig?.browserName}-profile`);
|
|
320
|
+
await fs.promises.mkdir(result, { recursive: true });
|
|
321
|
+
return result;
|
|
322
|
+
}
|
|
291
323
|
export async function generateLocator(locator) {
|
|
292
324
|
return locator._generateLocatorString();
|
|
293
325
|
}
|
|
326
|
+
const __filename = url.fileURLToPath(import.meta.url);
|
|
327
|
+
export const packageJSON = JSON.parse(fs.readFileSync(path.join(path.dirname(__filename), '..', 'package.json'), 'utf8'));
|