@cotestdev/mcp_playwright 0.0.51 → 0.0.53
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/lib/mcp/browser/browserContextFactory.js +11 -6
- package/lib/mcp/browser/browserServerBackend.js +2 -4
- package/lib/mcp/browser/config.js +71 -47
- package/lib/mcp/browser/context.js +65 -4
- package/lib/mcp/browser/logFile.js +96 -0
- package/lib/mcp/browser/response.js +107 -104
- package/lib/mcp/browser/sessionLog.js +1 -1
- package/lib/mcp/browser/tab.js +73 -18
- package/lib/mcp/browser/tools/config.js +41 -0
- package/lib/mcp/browser/tools/console.js +6 -2
- package/lib/mcp/browser/tools/cookies.js +152 -0
- package/lib/mcp/browser/tools/install.js +1 -0
- package/lib/mcp/browser/tools/network.js +25 -11
- package/lib/mcp/browser/tools/pdf.js +3 -4
- package/lib/mcp/browser/tools/route.js +140 -0
- package/lib/mcp/browser/tools/runCode.js +0 -2
- package/lib/mcp/browser/tools/screenshot.js +6 -7
- package/lib/mcp/browser/tools/storage.js +3 -4
- package/lib/mcp/browser/tools/tracing.js +10 -9
- package/lib/mcp/browser/tools/utils.js +0 -6
- package/lib/mcp/browser/tools/video.js +31 -13
- package/lib/mcp/browser/tools/webstorage.js +223 -0
- package/lib/mcp/browser/tools.js +11 -3
- package/lib/mcp/extension/cdpRelay.js +7 -7
- package/lib/mcp/extension/extensionContextFactory.js +4 -2
- package/lib/mcp/program.js +20 -12
- package/lib/mcp/terminal/cli.js +23 -2
- package/lib/mcp/terminal/command.js +34 -30
- package/lib/mcp/terminal/commands.js +329 -46
- package/lib/mcp/terminal/daemon.js +25 -39
- package/lib/mcp/terminal/helpGenerator.js +27 -7
- package/lib/mcp/terminal/program.js +495 -200
- package/lib/mcp/terminal/socketConnection.js +17 -2
- package/lib/mcpBundleImpl/index.js +27 -27
- package/package.json +2 -2
- package/lib/common/config.js +0 -281
- package/lib/common/configLoader.js +0 -344
- package/lib/common/esmLoaderHost.js +0 -104
- package/lib/common/expectBundle.js +0 -43
- package/lib/common/expectBundleImpl.js +0 -407
- package/lib/common/fixtures.js +0 -302
- package/lib/common/globals.js +0 -58
- package/lib/common/ipc.js +0 -60
- package/lib/common/poolBuilder.js +0 -85
- package/lib/common/process.js +0 -132
- package/lib/common/suiteUtils.js +0 -140
- package/lib/common/test.js +0 -322
- package/lib/common/testLoader.js +0 -101
- package/lib/common/testType.js +0 -298
- package/lib/common/validators.js +0 -68
- package/lib/mcp/browser/actions.d.js +0 -16
- package/lib/mcp/browser/codegen.js +0 -66
- package/lib/mcp/browser/processUtils.js +0 -102
- package/lib/mcp/browser/tools/script.js +0 -60
- package/lib/mcp/sdk/bundle.js +0 -75
- package/lib/mcp/sdk/mdb.js +0 -208
- package/lib/mcp/sdk/proxyBackend.js +0 -128
- package/lib/mcp/vscode/host.js +0 -187
- package/lib/mcp/vscode/main.js +0 -77
- package/lib/mcpBundleImpl.js +0 -41
- package/lib/third_party/pirates.js +0 -62
- package/lib/third_party/tsconfig-loader.js +0 -103
- package/lib/transform/babelBundle.js +0 -43
- package/lib/transform/babelBundleImpl.js +0 -461
- package/lib/transform/babelHighlightUtils.js +0 -63
- package/lib/transform/compilationCache.js +0 -272
- package/lib/transform/esmLoader.js +0 -103
- package/lib/transform/portTransport.js +0 -67
- package/lib/transform/transform.js +0 -296
- package/lib/utilsBundleImpl/index.js +0 -218
- package/lib/utilsBundleImpl.js +0 -103
|
@@ -23,24 +23,53 @@ __export(commands_exports, {
|
|
|
23
23
|
module.exports = __toCommonJS(commands_exports);
|
|
24
24
|
var import_mcpBundle = require("../../mcpBundle");
|
|
25
25
|
var import_command = require("./command");
|
|
26
|
+
const numberArg = import_mcpBundle.z.preprocess((val, ctx) => {
|
|
27
|
+
const number = Number(val);
|
|
28
|
+
if (Number.isNaN(number)) {
|
|
29
|
+
ctx.issues.push({
|
|
30
|
+
code: "custom",
|
|
31
|
+
message: `expected number, received '${val}'`,
|
|
32
|
+
input: val
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return number;
|
|
36
|
+
}, import_mcpBundle.z.number());
|
|
26
37
|
const open = (0, import_command.declareCommand)({
|
|
27
38
|
name: "open",
|
|
28
|
-
description: "Open
|
|
39
|
+
description: "Open the browser",
|
|
29
40
|
category: "core",
|
|
30
41
|
args: import_mcpBundle.z.object({
|
|
31
|
-
url: import_mcpBundle.z.string().describe("The URL to navigate to")
|
|
42
|
+
url: import_mcpBundle.z.string().optional().describe("The URL to navigate to")
|
|
43
|
+
}),
|
|
44
|
+
options: import_mcpBundle.z.object({
|
|
45
|
+
browser: import_mcpBundle.z.string().optional().describe("Browser or chrome channel to use, possible values: chrome, firefox, webkit, msedge."),
|
|
46
|
+
config: import_mcpBundle.z.string().optional().describe("Path to the configuration file, defaults to .playwright/cli.config.json"),
|
|
47
|
+
extension: import_mcpBundle.z.boolean().optional().describe("Connect to browser extension"),
|
|
48
|
+
headed: import_mcpBundle.z.boolean().optional().describe("Run browser in headed mode"),
|
|
49
|
+
persistent: import_mcpBundle.z.boolean().optional().describe("Use persistent browser profile"),
|
|
50
|
+
profile: import_mcpBundle.z.string().optional().describe("Use persistent browser profile, store profile in specified directory.")
|
|
32
51
|
}),
|
|
33
52
|
toolName: "browser_navigate",
|
|
34
|
-
toolParams: ({ url }) => ({ url })
|
|
53
|
+
toolParams: ({ url }) => ({ url: url || "about:blank" })
|
|
35
54
|
});
|
|
36
55
|
const close = (0, import_command.declareCommand)({
|
|
37
56
|
name: "close",
|
|
38
|
-
description: "Close the
|
|
57
|
+
description: "Close the browser",
|
|
39
58
|
category: "core",
|
|
40
59
|
args: import_mcpBundle.z.object({}),
|
|
41
60
|
toolName: "",
|
|
42
61
|
toolParams: () => ({})
|
|
43
62
|
});
|
|
63
|
+
const goto = (0, import_command.declareCommand)({
|
|
64
|
+
name: "goto",
|
|
65
|
+
description: "Navigate to a URL",
|
|
66
|
+
category: "core",
|
|
67
|
+
args: import_mcpBundle.z.object({
|
|
68
|
+
url: import_mcpBundle.z.string().describe("The URL to navigate to")
|
|
69
|
+
}),
|
|
70
|
+
toolName: "browser_navigate",
|
|
71
|
+
toolParams: ({ url }) => ({ url })
|
|
72
|
+
});
|
|
44
73
|
const goBack = (0, import_command.declareCommand)({
|
|
45
74
|
name: "go-back",
|
|
46
75
|
description: "Go back to the previous page",
|
|
@@ -113,8 +142,8 @@ const mouseMove = (0, import_command.declareCommand)({
|
|
|
113
142
|
description: "Move mouse to a given position",
|
|
114
143
|
category: "mouse",
|
|
115
144
|
args: import_mcpBundle.z.object({
|
|
116
|
-
x:
|
|
117
|
-
y:
|
|
145
|
+
x: numberArg.describe("X coordinate"),
|
|
146
|
+
y: numberArg.describe("Y coordinate")
|
|
118
147
|
}),
|
|
119
148
|
toolName: "browser_mouse_move_xy",
|
|
120
149
|
toolParams: ({ x, y }) => ({ x, y })
|
|
@@ -144,8 +173,8 @@ const mouseWheel = (0, import_command.declareCommand)({
|
|
|
144
173
|
description: "Scroll mouse wheel",
|
|
145
174
|
category: "mouse",
|
|
146
175
|
args: import_mcpBundle.z.object({
|
|
147
|
-
dx:
|
|
148
|
-
dy:
|
|
176
|
+
dx: numberArg.describe("Y delta"),
|
|
177
|
+
dy: numberArg.describe("X delta")
|
|
149
178
|
}),
|
|
150
179
|
toolName: "browser_mouse_wheel",
|
|
151
180
|
toolParams: ({ dx: deltaY, dy: deltaX }) => ({ deltaY, deltaX })
|
|
@@ -186,9 +215,6 @@ const drag = (0, import_command.declareCommand)({
|
|
|
186
215
|
startRef: import_mcpBundle.z.string().describe("Exact source element reference from the page snapshot"),
|
|
187
216
|
endRef: import_mcpBundle.z.string().describe("Exact target element reference from the page snapshot")
|
|
188
217
|
}),
|
|
189
|
-
options: import_mcpBundle.z.object({
|
|
190
|
-
headed: import_mcpBundle.z.boolean().default(false).describe("Run browser in headed mode")
|
|
191
|
-
}),
|
|
192
218
|
toolName: "browser_drag",
|
|
193
219
|
toolParams: ({ startRef, endRef }) => ({ startRef, endRef })
|
|
194
220
|
});
|
|
@@ -302,8 +328,8 @@ const resize = (0, import_command.declareCommand)({
|
|
|
302
328
|
description: "Resize the browser window",
|
|
303
329
|
category: "core",
|
|
304
330
|
args: import_mcpBundle.z.object({
|
|
305
|
-
w:
|
|
306
|
-
h:
|
|
331
|
+
w: numberArg.describe("Width of the browser window"),
|
|
332
|
+
h: numberArg.describe("Height of the browser window")
|
|
307
333
|
}),
|
|
308
334
|
toolName: "browser_resize",
|
|
309
335
|
toolParams: ({ w: width, h: height }) => ({ width, height })
|
|
@@ -341,7 +367,7 @@ const tabClose = (0, import_command.declareCommand)({
|
|
|
341
367
|
description: "Close a browser tab",
|
|
342
368
|
category: "tabs",
|
|
343
369
|
args: import_mcpBundle.z.object({
|
|
344
|
-
index:
|
|
370
|
+
index: numberArg.optional().describe("Tab index. If omitted, current tab is closed.")
|
|
345
371
|
}),
|
|
346
372
|
toolName: "browser_tabs",
|
|
347
373
|
toolParams: ({ index }) => ({ action: "close", index })
|
|
@@ -351,11 +377,226 @@ const tabSelect = (0, import_command.declareCommand)({
|
|
|
351
377
|
description: "Select a browser tab",
|
|
352
378
|
category: "tabs",
|
|
353
379
|
args: import_mcpBundle.z.object({
|
|
354
|
-
index:
|
|
380
|
+
index: numberArg.describe("Tab index")
|
|
355
381
|
}),
|
|
356
382
|
toolName: "browser_tabs",
|
|
357
383
|
toolParams: ({ index }) => ({ action: "select", index })
|
|
358
384
|
});
|
|
385
|
+
const stateLoad = (0, import_command.declareCommand)({
|
|
386
|
+
name: "state-load",
|
|
387
|
+
description: "Loads browser storage (authentication) state from a file",
|
|
388
|
+
category: "storage",
|
|
389
|
+
args: import_mcpBundle.z.object({
|
|
390
|
+
filename: import_mcpBundle.z.string().describe("File name to load the storage state from.")
|
|
391
|
+
}),
|
|
392
|
+
toolName: "browser_set_storage_state",
|
|
393
|
+
toolParams: ({ filename }) => ({ filename })
|
|
394
|
+
});
|
|
395
|
+
const stateSave = (0, import_command.declareCommand)({
|
|
396
|
+
name: "state-save",
|
|
397
|
+
description: "Saves the current storage (authentication) state to a file",
|
|
398
|
+
category: "storage",
|
|
399
|
+
args: import_mcpBundle.z.object({
|
|
400
|
+
filename: import_mcpBundle.z.string().optional().describe("File name to save the storage state to.")
|
|
401
|
+
}),
|
|
402
|
+
toolName: "browser_storage_state",
|
|
403
|
+
toolParams: ({ filename }) => ({ filename })
|
|
404
|
+
});
|
|
405
|
+
const cookieList = (0, import_command.declareCommand)({
|
|
406
|
+
name: "cookie-list",
|
|
407
|
+
description: "List all cookies (optionally filtered by domain/path)",
|
|
408
|
+
category: "storage",
|
|
409
|
+
args: import_mcpBundle.z.object({}),
|
|
410
|
+
options: import_mcpBundle.z.object({
|
|
411
|
+
domain: import_mcpBundle.z.string().optional().describe("Filter cookies by domain"),
|
|
412
|
+
path: import_mcpBundle.z.string().optional().describe("Filter cookies by path")
|
|
413
|
+
}),
|
|
414
|
+
toolName: "browser_cookie_list",
|
|
415
|
+
toolParams: ({ domain, path }) => ({ domain, path })
|
|
416
|
+
});
|
|
417
|
+
const cookieGet = (0, import_command.declareCommand)({
|
|
418
|
+
name: "cookie-get",
|
|
419
|
+
description: "Get a specific cookie by name",
|
|
420
|
+
category: "storage",
|
|
421
|
+
args: import_mcpBundle.z.object({
|
|
422
|
+
name: import_mcpBundle.z.string().describe("Cookie name")
|
|
423
|
+
}),
|
|
424
|
+
toolName: "browser_cookie_get",
|
|
425
|
+
toolParams: ({ name }) => ({ name })
|
|
426
|
+
});
|
|
427
|
+
const cookieSet = (0, import_command.declareCommand)({
|
|
428
|
+
name: "cookie-set",
|
|
429
|
+
description: "Set a cookie with optional flags",
|
|
430
|
+
category: "storage",
|
|
431
|
+
args: import_mcpBundle.z.object({
|
|
432
|
+
name: import_mcpBundle.z.string().describe("Cookie name"),
|
|
433
|
+
value: import_mcpBundle.z.string().describe("Cookie value")
|
|
434
|
+
}),
|
|
435
|
+
options: import_mcpBundle.z.object({
|
|
436
|
+
domain: import_mcpBundle.z.string().optional().describe("Cookie domain"),
|
|
437
|
+
path: import_mcpBundle.z.string().optional().describe("Cookie path"),
|
|
438
|
+
expires: numberArg.optional().describe("Cookie expiration as Unix timestamp"),
|
|
439
|
+
httpOnly: import_mcpBundle.z.boolean().optional().describe("Whether the cookie is HTTP only"),
|
|
440
|
+
secure: import_mcpBundle.z.boolean().optional().describe("Whether the cookie is secure"),
|
|
441
|
+
sameSite: import_mcpBundle.z.enum(["Strict", "Lax", "None"]).optional().describe("Cookie SameSite attribute")
|
|
442
|
+
}),
|
|
443
|
+
toolName: "browser_cookie_set",
|
|
444
|
+
toolParams: ({ name, value, domain, path, expires, httpOnly, secure, sameSite }) => ({ name, value, domain, path, expires, httpOnly, secure, sameSite })
|
|
445
|
+
});
|
|
446
|
+
const cookieDelete = (0, import_command.declareCommand)({
|
|
447
|
+
name: "cookie-delete",
|
|
448
|
+
description: "Delete a specific cookie",
|
|
449
|
+
category: "storage",
|
|
450
|
+
args: import_mcpBundle.z.object({
|
|
451
|
+
name: import_mcpBundle.z.string().describe("Cookie name")
|
|
452
|
+
}),
|
|
453
|
+
toolName: "browser_cookie_delete",
|
|
454
|
+
toolParams: ({ name }) => ({ name })
|
|
455
|
+
});
|
|
456
|
+
const cookieClear = (0, import_command.declareCommand)({
|
|
457
|
+
name: "cookie-clear",
|
|
458
|
+
description: "Clear all cookies",
|
|
459
|
+
category: "storage",
|
|
460
|
+
args: import_mcpBundle.z.object({}),
|
|
461
|
+
toolName: "browser_cookie_clear",
|
|
462
|
+
toolParams: () => ({})
|
|
463
|
+
});
|
|
464
|
+
const localStorageList = (0, import_command.declareCommand)({
|
|
465
|
+
name: "localstorage-list",
|
|
466
|
+
description: "List all localStorage key-value pairs",
|
|
467
|
+
category: "storage",
|
|
468
|
+
args: import_mcpBundle.z.object({}),
|
|
469
|
+
toolName: "browser_localstorage_list",
|
|
470
|
+
toolParams: () => ({})
|
|
471
|
+
});
|
|
472
|
+
const localStorageGet = (0, import_command.declareCommand)({
|
|
473
|
+
name: "localstorage-get",
|
|
474
|
+
description: "Get a localStorage item by key",
|
|
475
|
+
category: "storage",
|
|
476
|
+
args: import_mcpBundle.z.object({
|
|
477
|
+
key: import_mcpBundle.z.string().describe("Key to get")
|
|
478
|
+
}),
|
|
479
|
+
toolName: "browser_localstorage_get",
|
|
480
|
+
toolParams: ({ key }) => ({ key })
|
|
481
|
+
});
|
|
482
|
+
const localStorageSet = (0, import_command.declareCommand)({
|
|
483
|
+
name: "localstorage-set",
|
|
484
|
+
description: "Set a localStorage item",
|
|
485
|
+
category: "storage",
|
|
486
|
+
args: import_mcpBundle.z.object({
|
|
487
|
+
key: import_mcpBundle.z.string().describe("Key to set"),
|
|
488
|
+
value: import_mcpBundle.z.string().describe("Value to set")
|
|
489
|
+
}),
|
|
490
|
+
toolName: "browser_localstorage_set",
|
|
491
|
+
toolParams: ({ key, value }) => ({ key, value })
|
|
492
|
+
});
|
|
493
|
+
const localStorageDelete = (0, import_command.declareCommand)({
|
|
494
|
+
name: "localstorage-delete",
|
|
495
|
+
description: "Delete a localStorage item",
|
|
496
|
+
category: "storage",
|
|
497
|
+
args: import_mcpBundle.z.object({
|
|
498
|
+
key: import_mcpBundle.z.string().describe("Key to delete")
|
|
499
|
+
}),
|
|
500
|
+
toolName: "browser_localstorage_delete",
|
|
501
|
+
toolParams: ({ key }) => ({ key })
|
|
502
|
+
});
|
|
503
|
+
const localStorageClear = (0, import_command.declareCommand)({
|
|
504
|
+
name: "localstorage-clear",
|
|
505
|
+
description: "Clear all localStorage",
|
|
506
|
+
category: "storage",
|
|
507
|
+
args: import_mcpBundle.z.object({}),
|
|
508
|
+
toolName: "browser_localstorage_clear",
|
|
509
|
+
toolParams: () => ({})
|
|
510
|
+
});
|
|
511
|
+
const sessionStorageList = (0, import_command.declareCommand)({
|
|
512
|
+
name: "sessionstorage-list",
|
|
513
|
+
description: "List all sessionStorage key-value pairs",
|
|
514
|
+
category: "storage",
|
|
515
|
+
args: import_mcpBundle.z.object({}),
|
|
516
|
+
toolName: "browser_sessionstorage_list",
|
|
517
|
+
toolParams: () => ({})
|
|
518
|
+
});
|
|
519
|
+
const sessionStorageGet = (0, import_command.declareCommand)({
|
|
520
|
+
name: "sessionstorage-get",
|
|
521
|
+
description: "Get a sessionStorage item by key",
|
|
522
|
+
category: "storage",
|
|
523
|
+
args: import_mcpBundle.z.object({
|
|
524
|
+
key: import_mcpBundle.z.string().describe("Key to get")
|
|
525
|
+
}),
|
|
526
|
+
toolName: "browser_sessionstorage_get",
|
|
527
|
+
toolParams: ({ key }) => ({ key })
|
|
528
|
+
});
|
|
529
|
+
const sessionStorageSet = (0, import_command.declareCommand)({
|
|
530
|
+
name: "sessionstorage-set",
|
|
531
|
+
description: "Set a sessionStorage item",
|
|
532
|
+
category: "storage",
|
|
533
|
+
args: import_mcpBundle.z.object({
|
|
534
|
+
key: import_mcpBundle.z.string().describe("Key to set"),
|
|
535
|
+
value: import_mcpBundle.z.string().describe("Value to set")
|
|
536
|
+
}),
|
|
537
|
+
toolName: "browser_sessionstorage_set",
|
|
538
|
+
toolParams: ({ key, value }) => ({ key, value })
|
|
539
|
+
});
|
|
540
|
+
const sessionStorageDelete = (0, import_command.declareCommand)({
|
|
541
|
+
name: "sessionstorage-delete",
|
|
542
|
+
description: "Delete a sessionStorage item",
|
|
543
|
+
category: "storage",
|
|
544
|
+
args: import_mcpBundle.z.object({
|
|
545
|
+
key: import_mcpBundle.z.string().describe("Key to delete")
|
|
546
|
+
}),
|
|
547
|
+
toolName: "browser_sessionstorage_delete",
|
|
548
|
+
toolParams: ({ key }) => ({ key })
|
|
549
|
+
});
|
|
550
|
+
const sessionStorageClear = (0, import_command.declareCommand)({
|
|
551
|
+
name: "sessionstorage-clear",
|
|
552
|
+
description: "Clear all sessionStorage",
|
|
553
|
+
category: "storage",
|
|
554
|
+
args: import_mcpBundle.z.object({}),
|
|
555
|
+
toolName: "browser_sessionstorage_clear",
|
|
556
|
+
toolParams: () => ({})
|
|
557
|
+
});
|
|
558
|
+
const routeMock = (0, import_command.declareCommand)({
|
|
559
|
+
name: "route",
|
|
560
|
+
description: "Mock network requests matching a URL pattern",
|
|
561
|
+
category: "network",
|
|
562
|
+
args: import_mcpBundle.z.object({
|
|
563
|
+
pattern: import_mcpBundle.z.string().describe('URL pattern to match (e.g., "**/api/users")')
|
|
564
|
+
}),
|
|
565
|
+
options: import_mcpBundle.z.object({
|
|
566
|
+
status: numberArg.optional().describe("HTTP status code (default: 200)"),
|
|
567
|
+
body: import_mcpBundle.z.string().optional().describe("Response body (text or JSON string)"),
|
|
568
|
+
["content-type"]: import_mcpBundle.z.string().optional().describe("Content-Type header"),
|
|
569
|
+
header: import_mcpBundle.z.union([import_mcpBundle.z.string(), import_mcpBundle.z.array(import_mcpBundle.z.string())]).optional().transform((v) => v ? Array.isArray(v) ? v : [v] : void 0).describe('Header to add in "Name: Value" format (repeatable)'),
|
|
570
|
+
["remove-header"]: import_mcpBundle.z.string().optional().describe("Comma-separated header names to remove")
|
|
571
|
+
}),
|
|
572
|
+
toolName: "browser_route",
|
|
573
|
+
toolParams: ({ pattern, status, body, ["content-type"]: contentType, header: headers, ["remove-header"]: removeHeaders }) => ({
|
|
574
|
+
pattern,
|
|
575
|
+
status,
|
|
576
|
+
body,
|
|
577
|
+
contentType,
|
|
578
|
+
headers,
|
|
579
|
+
removeHeaders
|
|
580
|
+
})
|
|
581
|
+
});
|
|
582
|
+
const routeList = (0, import_command.declareCommand)({
|
|
583
|
+
name: "route-list",
|
|
584
|
+
description: "List all active network routes",
|
|
585
|
+
category: "network",
|
|
586
|
+
args: import_mcpBundle.z.object({}),
|
|
587
|
+
toolName: "browser_route_list",
|
|
588
|
+
toolParams: () => ({})
|
|
589
|
+
});
|
|
590
|
+
const unroute = (0, import_command.declareCommand)({
|
|
591
|
+
name: "unroute",
|
|
592
|
+
description: "Remove routes matching a pattern (or all routes)",
|
|
593
|
+
category: "network",
|
|
594
|
+
args: import_mcpBundle.z.object({
|
|
595
|
+
pattern: import_mcpBundle.z.string().optional().describe("URL pattern to unroute (omit to remove all)")
|
|
596
|
+
}),
|
|
597
|
+
toolName: "browser_unroute",
|
|
598
|
+
toolParams: ({ pattern }) => ({ pattern })
|
|
599
|
+
});
|
|
359
600
|
const screenshot = (0, import_command.declareCommand)({
|
|
360
601
|
name: "screenshot",
|
|
361
602
|
description: "screenshot of the current page or element",
|
|
@@ -441,54 +682,71 @@ const videoStop = (0, import_command.declareCommand)({
|
|
|
441
682
|
toolParams: ({ filename }) => ({ filename })
|
|
442
683
|
});
|
|
443
684
|
const sessionList = (0, import_command.declareCommand)({
|
|
444
|
-
name: "
|
|
445
|
-
description: "List
|
|
446
|
-
category: "
|
|
685
|
+
name: "list",
|
|
686
|
+
description: "List browser sessions",
|
|
687
|
+
category: "browsers",
|
|
447
688
|
args: import_mcpBundle.z.object({}),
|
|
689
|
+
options: import_mcpBundle.z.object({
|
|
690
|
+
all: import_mcpBundle.z.boolean().optional().describe("List all browser sessions across all workspaces")
|
|
691
|
+
}),
|
|
448
692
|
toolName: "",
|
|
449
693
|
toolParams: () => ({})
|
|
450
694
|
});
|
|
451
|
-
const
|
|
452
|
-
name: "
|
|
453
|
-
description: "
|
|
454
|
-
category: "
|
|
455
|
-
args: import_mcpBundle.z.object({
|
|
456
|
-
name: import_mcpBundle.z.string().optional().describe("Name of the session to stop. If omitted, current session is stopped.")
|
|
457
|
-
}),
|
|
695
|
+
const sessionCloseAll = (0, import_command.declareCommand)({
|
|
696
|
+
name: "close-all",
|
|
697
|
+
description: "Close all browser sessions",
|
|
698
|
+
category: "browsers",
|
|
458
699
|
toolName: "",
|
|
459
700
|
toolParams: () => ({})
|
|
460
701
|
});
|
|
461
|
-
const
|
|
462
|
-
name: "
|
|
463
|
-
description: "
|
|
464
|
-
category: "
|
|
702
|
+
const killAll = (0, import_command.declareCommand)({
|
|
703
|
+
name: "kill-all",
|
|
704
|
+
description: "Forcefully kill all browser sessions (for stale/zombie processes)",
|
|
705
|
+
category: "browsers",
|
|
465
706
|
toolName: "",
|
|
466
707
|
toolParams: () => ({})
|
|
467
708
|
});
|
|
468
|
-
const
|
|
469
|
-
name: "
|
|
709
|
+
const deleteData = (0, import_command.declareCommand)({
|
|
710
|
+
name: "delete-data",
|
|
470
711
|
description: "Delete session data",
|
|
471
|
-
category: "
|
|
472
|
-
args: import_mcpBundle.z.object({
|
|
473
|
-
name: import_mcpBundle.z.string().optional().describe("Name of the session to delete. If omitted, current session is deleted.")
|
|
474
|
-
}),
|
|
712
|
+
category: "core",
|
|
475
713
|
toolName: "",
|
|
476
|
-
toolParams: (
|
|
714
|
+
toolParams: () => ({})
|
|
477
715
|
});
|
|
478
|
-
const
|
|
479
|
-
name: "config",
|
|
480
|
-
description: "
|
|
716
|
+
const configPrint = (0, import_command.declareCommand)({
|
|
717
|
+
name: "config-print",
|
|
718
|
+
description: "Print the final resolved config after merging CLI options, environment variables and config file.",
|
|
481
719
|
category: "config",
|
|
482
|
-
|
|
483
|
-
|
|
720
|
+
hidden: true,
|
|
721
|
+
toolName: "browser_get_config",
|
|
722
|
+
toolParams: () => ({})
|
|
723
|
+
});
|
|
724
|
+
const install = (0, import_command.declareCommand)({
|
|
725
|
+
name: "install",
|
|
726
|
+
description: "Initialize workspace",
|
|
727
|
+
category: "install",
|
|
728
|
+
args: import_mcpBundle.z.object({}),
|
|
729
|
+
options: import_mcpBundle.z.object({
|
|
730
|
+
skills: import_mcpBundle.z.boolean().optional().describe("Install skills for Claude / GitHub Copilot")
|
|
484
731
|
}),
|
|
485
732
|
toolName: "",
|
|
486
733
|
toolParams: () => ({})
|
|
487
734
|
});
|
|
735
|
+
const installBrowser = (0, import_command.declareCommand)({
|
|
736
|
+
name: "install-browser",
|
|
737
|
+
description: "Install browser",
|
|
738
|
+
category: "install",
|
|
739
|
+
options: import_mcpBundle.z.object({
|
|
740
|
+
browser: import_mcpBundle.z.string().optional().describe("Browser or chrome channel to use, possible values: chrome, firefox, webkit, msedge")
|
|
741
|
+
}),
|
|
742
|
+
toolName: "browser_install",
|
|
743
|
+
toolParams: () => ({})
|
|
744
|
+
});
|
|
488
745
|
const commandsArray = [
|
|
489
746
|
// core category
|
|
490
747
|
open,
|
|
491
748
|
close,
|
|
749
|
+
goto,
|
|
492
750
|
type,
|
|
493
751
|
click,
|
|
494
752
|
doubleClick,
|
|
@@ -506,6 +764,7 @@ const commandsArray = [
|
|
|
506
764
|
dialogDismiss,
|
|
507
765
|
resize,
|
|
508
766
|
runCode,
|
|
767
|
+
deleteData,
|
|
509
768
|
// navigation category
|
|
510
769
|
goBack,
|
|
511
770
|
goForward,
|
|
@@ -527,8 +786,33 @@ const commandsArray = [
|
|
|
527
786
|
tabNew,
|
|
528
787
|
tabClose,
|
|
529
788
|
tabSelect,
|
|
530
|
-
//
|
|
531
|
-
|
|
789
|
+
// storage category
|
|
790
|
+
stateLoad,
|
|
791
|
+
stateSave,
|
|
792
|
+
cookieList,
|
|
793
|
+
cookieGet,
|
|
794
|
+
cookieSet,
|
|
795
|
+
cookieDelete,
|
|
796
|
+
cookieClear,
|
|
797
|
+
localStorageList,
|
|
798
|
+
localStorageGet,
|
|
799
|
+
localStorageSet,
|
|
800
|
+
localStorageDelete,
|
|
801
|
+
localStorageClear,
|
|
802
|
+
sessionStorageList,
|
|
803
|
+
sessionStorageGet,
|
|
804
|
+
sessionStorageSet,
|
|
805
|
+
sessionStorageDelete,
|
|
806
|
+
sessionStorageClear,
|
|
807
|
+
// network category
|
|
808
|
+
routeMock,
|
|
809
|
+
routeList,
|
|
810
|
+
unroute,
|
|
811
|
+
// config category
|
|
812
|
+
configPrint,
|
|
813
|
+
// install category
|
|
814
|
+
install,
|
|
815
|
+
installBrowser,
|
|
532
816
|
// devtools category
|
|
533
817
|
networkRequests,
|
|
534
818
|
tracingStart,
|
|
@@ -537,9 +821,8 @@ const commandsArray = [
|
|
|
537
821
|
videoStop,
|
|
538
822
|
// session category
|
|
539
823
|
sessionList,
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
sessionDelete
|
|
824
|
+
sessionCloseAll,
|
|
825
|
+
killAll
|
|
543
826
|
];
|
|
544
827
|
const commands = Object.fromEntries(commandsArray.map((cmd) => [cmd.name, cmd]));
|
|
545
828
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -38,6 +38,7 @@ var import_path = __toESM(require("path"));
|
|
|
38
38
|
var import_url = __toESM(require("url"));
|
|
39
39
|
var import_utilsBundle = require("playwright-core/lib/utilsBundle");
|
|
40
40
|
var import_utils = require("playwright-core/lib/utils");
|
|
41
|
+
var import_browserServerBackend = require("../browser/browserServerBackend");
|
|
41
42
|
var import_socketConnection = require("./socketConnection");
|
|
42
43
|
var import_commands = require("./commands");
|
|
43
44
|
var import_command = require("./command");
|
|
@@ -51,7 +52,9 @@ async function socketExists(socketPath) {
|
|
|
51
52
|
}
|
|
52
53
|
return false;
|
|
53
54
|
}
|
|
54
|
-
async function startMcpDaemonServer(
|
|
55
|
+
async function startMcpDaemonServer(config, contextFactory) {
|
|
56
|
+
const sessionConfig = config.sessionConfig;
|
|
57
|
+
const { socketPath, version } = sessionConfig;
|
|
55
58
|
if (import_os.default.platform() !== "win32" && await socketExists(socketPath)) {
|
|
56
59
|
daemonDebug(`Socket already exists, removing: ${socketPath}`);
|
|
57
60
|
try {
|
|
@@ -61,37 +64,38 @@ async function startMcpDaemonServer(socketPath, serverBackendFactory, daemonVers
|
|
|
61
64
|
throw error;
|
|
62
65
|
}
|
|
63
66
|
}
|
|
64
|
-
const backend = serverBackendFactory.create();
|
|
65
67
|
const cwd = import_url.default.pathToFileURL(process.cwd()).href;
|
|
66
|
-
|
|
68
|
+
const clientInfo = {
|
|
67
69
|
name: "playwright-cli",
|
|
68
|
-
version:
|
|
70
|
+
version: sessionConfig.version,
|
|
69
71
|
roots: [{
|
|
70
72
|
uri: cwd,
|
|
71
73
|
name: "cwd"
|
|
72
74
|
}],
|
|
73
75
|
timestamp: Date.now()
|
|
76
|
+
};
|
|
77
|
+
const { browserContext, close } = await contextFactory.createContext(clientInfo, new AbortController().signal, {});
|
|
78
|
+
browserContext.on("close", () => {
|
|
79
|
+
daemonDebug("browser closed, shutting down daemon");
|
|
80
|
+
shutdown(0);
|
|
74
81
|
});
|
|
82
|
+
const existingContextFactory = {
|
|
83
|
+
createContext: () => Promise.resolve({ browserContext, close })
|
|
84
|
+
};
|
|
85
|
+
const backend = new import_browserServerBackend.BrowserServerBackend(config, existingContextFactory, { allTools: true });
|
|
86
|
+
await backend.initialize?.(clientInfo);
|
|
75
87
|
await import_promises.default.mkdir(import_path.default.dirname(socketPath), { recursive: true });
|
|
88
|
+
const shutdown = (exitCode) => {
|
|
89
|
+
daemonDebug(`shutting down daemon with exit code ${exitCode}`);
|
|
90
|
+
server.close();
|
|
91
|
+
(0, import_utils.gracefullyProcessExitDoNotHang)(exitCode);
|
|
92
|
+
};
|
|
76
93
|
const server = import_net.default.createServer((socket) => {
|
|
77
94
|
daemonDebug("new client connection");
|
|
78
|
-
const connection = new import_socketConnection.SocketConnection(socket,
|
|
95
|
+
const connection = new import_socketConnection.SocketConnection(socket, version);
|
|
79
96
|
connection.onclose = () => {
|
|
80
97
|
daemonDebug("client disconnected");
|
|
81
98
|
};
|
|
82
|
-
connection.onversionerror = (id, e) => {
|
|
83
|
-
if (daemonVersion === "undefined-for-test")
|
|
84
|
-
return false;
|
|
85
|
-
if (semverGreater(daemonVersion, e.received)) {
|
|
86
|
-
connection.send({ id, error: `Client is too old: daemon is ${daemonVersion}, client is ${e.received}.` }).catch((e2) => console.error(e2));
|
|
87
|
-
} else {
|
|
88
|
-
(0, import_utils.gracefullyProcessExitDoNotHang)(0, async () => {
|
|
89
|
-
await connection.send({ id, error: `Daemon is too old: daemon is ${daemonVersion}, client is ${e.received}. Stopping it.` }).catch((e2) => console.error(e2));
|
|
90
|
-
server.close();
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
return true;
|
|
94
|
-
};
|
|
95
99
|
connection.onmessage = async (message) => {
|
|
96
100
|
const { id, method, params } = message;
|
|
97
101
|
try {
|
|
@@ -107,23 +111,18 @@ async function startMcpDaemonServer(socketPath, serverBackendFactory, daemonVers
|
|
|
107
111
|
const { toolName, toolParams } = parseCliCommand(params.args);
|
|
108
112
|
if (params.cwd)
|
|
109
113
|
toolParams._meta = { cwd: params.cwd };
|
|
110
|
-
const response = await backend.callTool(toolName, toolParams
|
|
111
|
-
});
|
|
114
|
+
const response = await backend.callTool(toolName, toolParams);
|
|
112
115
|
await connection.send({ id, result: formatResult(response) });
|
|
113
116
|
} else {
|
|
114
117
|
throw new Error(`Unknown method: ${method}`);
|
|
115
118
|
}
|
|
116
119
|
} catch (e) {
|
|
117
120
|
daemonDebug("command failed", e);
|
|
118
|
-
|
|
121
|
+
const error = process.env.PWDEBUGIMPL ? e.stack || e.message : e.message;
|
|
122
|
+
await connection.send({ id, error });
|
|
119
123
|
}
|
|
120
124
|
};
|
|
121
125
|
});
|
|
122
|
-
backend.onBrowserContextClosed = () => {
|
|
123
|
-
daemonDebug("browser closed, shutting down daemon");
|
|
124
|
-
server.close();
|
|
125
|
-
(0, import_utils.gracefullyProcessExitDoNotHang)(0);
|
|
126
|
-
};
|
|
127
126
|
return new Promise((resolve, reject) => {
|
|
128
127
|
server.on("error", (error) => {
|
|
129
128
|
daemonDebug(`server error: ${error.message}`);
|
|
@@ -146,19 +145,6 @@ function parseCliCommand(args) {
|
|
|
146
145
|
throw new Error("Command is required");
|
|
147
146
|
return (0, import_command.parseCommand)(command, args);
|
|
148
147
|
}
|
|
149
|
-
function semverGreater(a, b) {
|
|
150
|
-
a = a.replace(/-next$/, "");
|
|
151
|
-
b = b.replace(/-next$/, "");
|
|
152
|
-
const aParts = a.split(".").map(Number);
|
|
153
|
-
const bParts = b.split(".").map(Number);
|
|
154
|
-
for (let i = 0; i < 4; i++) {
|
|
155
|
-
if (aParts[i] > bParts[i])
|
|
156
|
-
return true;
|
|
157
|
-
if (aParts[i] < bParts[i])
|
|
158
|
-
return false;
|
|
159
|
-
}
|
|
160
|
-
return false;
|
|
161
|
-
}
|
|
162
148
|
// Annotate the CommonJS export names for ESM import in node:
|
|
163
149
|
0 && (module.exports = {
|
|
164
150
|
startMcpDaemonServer
|
|
@@ -23,6 +23,7 @@ __export(helpGenerator_exports, {
|
|
|
23
23
|
generateReadme: () => generateReadme
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(helpGenerator_exports);
|
|
26
|
+
var import_mcpBundle = require("../../mcpBundle");
|
|
26
27
|
var import_commands = require("./commands");
|
|
27
28
|
function commandArgs(command) {
|
|
28
29
|
const args = [];
|
|
@@ -68,18 +69,24 @@ const categories = [
|
|
|
68
69
|
{ name: "export", title: "Save as" },
|
|
69
70
|
{ name: "tabs", title: "Tabs" },
|
|
70
71
|
{ name: "storage", title: "Storage" },
|
|
72
|
+
{ name: "network", title: "Network" },
|
|
71
73
|
{ name: "devtools", title: "DevTools" },
|
|
74
|
+
{ name: "install", title: "Install" },
|
|
72
75
|
{ name: "config", title: "Configuration" },
|
|
73
|
-
{ name: "
|
|
76
|
+
{ name: "browsers", title: "Browser sessions" }
|
|
74
77
|
];
|
|
75
78
|
function generateHelp() {
|
|
76
79
|
const lines = [];
|
|
77
80
|
lines.push("Usage: playwright-cli <command> [args] [options]");
|
|
81
|
+
lines.push("Usage: playwright-cli -s=<session> <command> [args] [options]");
|
|
78
82
|
const commandsByCategory = /* @__PURE__ */ new Map();
|
|
79
83
|
for (const c of categories)
|
|
80
84
|
commandsByCategory.set(c.name, []);
|
|
81
|
-
for (const command of Object.values(import_commands.commands))
|
|
85
|
+
for (const command of Object.values(import_commands.commands)) {
|
|
86
|
+
if (command.hidden)
|
|
87
|
+
continue;
|
|
82
88
|
commandsByCategory.get(command.category).push(command);
|
|
89
|
+
}
|
|
83
90
|
for (const c of categories) {
|
|
84
91
|
const cc = commandsByCategory.get(c.name);
|
|
85
92
|
if (!cc.length)
|
|
@@ -90,11 +97,7 @@ ${c.title}:`);
|
|
|
90
97
|
lines.push(generateHelpEntry(command));
|
|
91
98
|
}
|
|
92
99
|
lines.push("\nGlobal options:");
|
|
93
|
-
lines.push(formatWithGap(" --config <path>", "create a session with custom config, defaults to `playwright-cli.json`"));
|
|
94
|
-
lines.push(formatWithGap(" --extension", "connect to a running browser instance using Playwright MCP Bridge extension"));
|
|
95
|
-
lines.push(formatWithGap(" --headed", "create a headed session"));
|
|
96
100
|
lines.push(formatWithGap(" --help [command]", "print help"));
|
|
97
|
-
lines.push(formatWithGap(" --session", "run command in the scope of a specific session"));
|
|
98
101
|
lines.push(formatWithGap(" --version", "print version"));
|
|
99
102
|
return lines.join("\n");
|
|
100
103
|
}
|
|
@@ -132,12 +135,29 @@ function generateReadmeEntry(command) {
|
|
|
132
135
|
const suffix = "# " + command.description.toLowerCase();
|
|
133
136
|
return formatWithGap(prefix, suffix, 40);
|
|
134
137
|
}
|
|
138
|
+
function unwrapZodType(schema) {
|
|
139
|
+
if ("unwrap" in schema && typeof schema.unwrap === "function")
|
|
140
|
+
return unwrapZodType(schema.unwrap());
|
|
141
|
+
return schema;
|
|
142
|
+
}
|
|
135
143
|
function generateHelpJSON() {
|
|
144
|
+
const booleanOptions = /* @__PURE__ */ new Set();
|
|
145
|
+
for (const command of Object.values(import_commands.commands)) {
|
|
146
|
+
if (!command.options)
|
|
147
|
+
continue;
|
|
148
|
+
const optionsShape = command.options.shape;
|
|
149
|
+
for (const [name, schema] of Object.entries(optionsShape)) {
|
|
150
|
+
const innerSchema = unwrapZodType(schema);
|
|
151
|
+
if (innerSchema instanceof import_mcpBundle.z.ZodBoolean)
|
|
152
|
+
booleanOptions.add(name);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
136
155
|
const help = {
|
|
137
156
|
global: generateHelp(),
|
|
138
157
|
commands: Object.fromEntries(
|
|
139
158
|
Object.entries(import_commands.commands).map(([name, command]) => [name, generateCommandHelp(command)])
|
|
140
|
-
)
|
|
159
|
+
),
|
|
160
|
+
booleanOptions: [...booleanOptions]
|
|
141
161
|
};
|
|
142
162
|
return help;
|
|
143
163
|
}
|