@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 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
- // Tool-specific configurations
156
- tools?: {
157
- browser_take_screenshot?: {
158
- // Disable base64-encoded image responses
159
- omitBase64?: boolean;
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 mcpServer = await createServer({ headless: true });
234
+ const connection = await createConnection({ headless: true });
226
235
  const transport = new SSEServerTransport('/messages', res);
227
- await mcpServer.connect(transport);
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
- * Configuration for specific tools.
99
- */
100
- tools?: {
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
- * Configuration for the browser_take_screenshot tool.
104
+ * List of origins to block the browser to request. Origins matching both `allowedOrigins` and `blockedOrigins` will be blocked.
103
105
  */
104
- browser_take_screenshot?: {
105
-
106
- /**
107
- * Whether to disable base64-encoded image responses to the clients that
108
- * don't support binary data or prefer to save on tokens.
109
- */
110
- omitBase64?: boolean;
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 createServer(config?: Config): Promise<Server>;
28
+ export declare function createConnection(config?: Config): Promise<Connection>;
23
29
  export {};
package/index.js CHANGED
@@ -15,5 +15,5 @@
15
15
  * limitations under the License.
16
16
  */
17
17
 
18
- import { createServer } from './lib/index';
19
- export default { createServer };
18
+ import { createConnection } from './lib/index';
19
+ export default { createConnection };
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 ?? await createUserDataDir({ browserName, channel }),
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
- export function createServerWithTools(serverOptions, config) {
21
- const { name, version, tools } = serverOptions;
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 oldClose = server.close.bind(server);
58
- server.close = async () => {
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 ServerList {
65
- _servers = [];
66
- _serverFactory;
67
- constructor(serverFactory) {
68
- this._serverFactory = serverFactory;
68
+ export class Connection {
69
+ server;
70
+ context;
71
+ constructor(server, context) {
72
+ this.server = server;
73
+ this.context = context;
69
74
  }
70
- async create() {
71
- const server = await this._serverFactory();
72
- this._servers.push(server);
73
- return server;
74
- }
75
- async close(server) {
76
- const index = this._servers.indexOf(server);
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 closeAll() {
82
- await Promise.all(this._servers.map(server => server.close()));
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 browserType = browserConfig?.browserName ? playwright[browserConfig.browserName] : playwright.chromium;
283
- return await browserType.launchPersistentContext(browserConfig?.userDataDir || '', { ...browserConfig?.launchOptions, ...browserConfig?.contextOptions });
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'));