@cotestdev/mcp_playwright 0.0.35 → 0.0.37

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.
Files changed (42) hide show
  1. package/lib/mcp/browser/browserContextFactory.js +12 -12
  2. package/lib/mcp/browser/browserServerBackend.js +24 -12
  3. package/lib/mcp/browser/config.js +37 -7
  4. package/lib/mcp/browser/context.js +13 -61
  5. package/lib/mcp/browser/response.js +183 -251
  6. package/lib/mcp/browser/sessionLog.js +19 -104
  7. package/lib/mcp/browser/tab.js +49 -28
  8. package/lib/mcp/browser/tools/common.js +8 -9
  9. package/lib/mcp/browser/tools/console.js +20 -3
  10. package/lib/mcp/browser/tools/dialogs.js +2 -3
  11. package/lib/mcp/browser/tools/evaluate.js +8 -6
  12. package/lib/mcp/browser/tools/files.js +2 -2
  13. package/lib/mcp/browser/tools/form.js +2 -2
  14. package/lib/mcp/browser/tools/install.js +4 -1
  15. package/lib/mcp/browser/tools/keyboard.js +77 -10
  16. package/lib/mcp/browser/tools/mouse.js +59 -7
  17. package/lib/mcp/browser/tools/navigate.js +51 -8
  18. package/lib/mcp/browser/tools/network.js +21 -3
  19. package/lib/mcp/browser/tools/pdf.js +4 -3
  20. package/lib/mcp/browser/tools/runCode.js +8 -12
  21. package/lib/mcp/browser/tools/schema.js +31 -0
  22. package/lib/mcp/browser/tools/screenshot.js +10 -28
  23. package/lib/mcp/browser/tools/snapshot.js +40 -24
  24. package/lib/mcp/browser/tools/tabs.js +10 -10
  25. package/lib/mcp/browser/tools/tool.js +3 -6
  26. package/lib/mcp/browser/tools/tracing.js +3 -3
  27. package/lib/mcp/browser/tools/utils.js +2 -2
  28. package/lib/mcp/browser/tools/verify.js +9 -9
  29. package/lib/mcp/browser/tools/wait.js +3 -3
  30. package/lib/mcp/browser/tools.js +2 -2
  31. package/lib/mcp/extension/extensionContextFactory.js +2 -2
  32. package/lib/mcp/program.js +3 -2
  33. package/lib/mcp/terminal/cli.js +4 -216
  34. package/lib/mcp/terminal/command.js +56 -0
  35. package/lib/mcp/terminal/commands.js +528 -0
  36. package/lib/mcp/terminal/daemon.js +42 -25
  37. package/lib/mcp/terminal/helpGenerator.js +152 -0
  38. package/lib/mcp/terminal/program.js +434 -0
  39. package/lib/mcp/terminal/socketConnection.js +2 -4
  40. package/lib/mcpBundleImpl/index.js +44 -44
  41. package/lib/util.js +3 -6
  42. package/package.json +1 -1
@@ -23,32 +23,41 @@ __export(navigate_exports, {
23
23
  module.exports = __toCommonJS(navigate_exports);
24
24
  var import_mcpBundle = require("../../../mcpBundle");
25
25
  var import_tool = require("./tool");
26
- var import_common = require("./common");
26
+ var import_schema = require("./schema");
27
27
  const navigate = (0, import_tool.defineTool)({
28
- capability: "core",
28
+ capability: "core-navigation",
29
29
  schema: {
30
30
  name: "browser_navigate",
31
31
  title: "Navigate to a URL",
32
32
  description: "Navigate to a URL",
33
- inputSchema: import_common.baseSchema.extend({
33
+ inputSchema: import_schema.baseSchema.extend({
34
34
  url: import_mcpBundle.z.string().describe("The URL to navigate to")
35
35
  }),
36
36
  type: "action"
37
37
  },
38
38
  handle: async (context, params, response) => {
39
39
  const tab = await context.ensureTab();
40
- await tab.navigate(params.url);
40
+ let url = params.url;
41
+ try {
42
+ new URL(url);
43
+ } catch (e) {
44
+ if (url.startsWith("localhost"))
45
+ url = "http://" + url;
46
+ else
47
+ url = "https://" + url;
48
+ }
49
+ await tab.navigate(url);
41
50
  response.setIncludeSnapshot();
42
51
  response.addCode(`await page.goto('${params.url}');`);
43
52
  }
44
53
  });
45
54
  const goBack = (0, import_tool.defineTabTool)({
46
- capability: "core",
55
+ capability: "core-navigation",
47
56
  schema: {
48
57
  name: "browser_navigate_back",
49
58
  title: "Go back",
50
- description: "Go back to the previous page",
51
- inputSchema: import_common.baseSchema.extend({}),
59
+ description: "Go back to the previous page in the history",
60
+ inputSchema: import_schema.baseSchema.extend({}),
52
61
  type: "action"
53
62
  },
54
63
  handle: async (tab, params, response) => {
@@ -57,7 +66,41 @@ const goBack = (0, import_tool.defineTabTool)({
57
66
  response.addCode(`await page.goBack();`);
58
67
  }
59
68
  });
69
+ const goForward = (0, import_tool.defineTabTool)({
70
+ capability: "core-navigation",
71
+ skillOnly: true,
72
+ schema: {
73
+ name: "browser_navigate_forward",
74
+ title: "Go forward",
75
+ description: "Go forward to the next page in the history",
76
+ inputSchema: import_schema.baseSchema.extend({}),
77
+ type: "action"
78
+ },
79
+ handle: async (tab, params, response) => {
80
+ await tab.page.goForward();
81
+ response.setIncludeSnapshot();
82
+ response.addCode(`await page.goForward();`);
83
+ }
84
+ });
85
+ const reload = (0, import_tool.defineTabTool)({
86
+ capability: "core-navigation",
87
+ skillOnly: true,
88
+ schema: {
89
+ name: "browser_reload",
90
+ title: "Reload the page",
91
+ description: "Reload the current page",
92
+ inputSchema: import_schema.baseSchema.extend({}),
93
+ type: "action"
94
+ },
95
+ handle: async (tab, params, response) => {
96
+ await tab.page.reload();
97
+ response.setIncludeSnapshot();
98
+ response.addCode(`await page.reload();`);
99
+ }
100
+ });
60
101
  var navigate_default = [
61
102
  navigate,
62
- goBack
103
+ goBack,
104
+ goForward,
105
+ reload
63
106
  ];
@@ -30,17 +30,34 @@ const requests = (0, import_tool.defineTabTool)({
30
30
  title: "List network requests",
31
31
  description: "Returns all network requests since loading the page",
32
32
  inputSchema: import_mcpBundle.z.object({
33
- includeStatic: import_mcpBundle.z.boolean().default(false).describe("Whether to include successful static resources like images, fonts, scripts, etc. Defaults to false.")
33
+ includeStatic: import_mcpBundle.z.boolean().default(false).describe("Whether to include successful static resources like images, fonts, scripts, etc. Defaults to false."),
34
+ filename: import_mcpBundle.z.string().optional().describe("Filename to save the network requests to. If not provided, requests are returned as text.")
34
35
  }),
35
36
  type: "readOnly"
36
37
  },
37
38
  handle: async (tab, params, response) => {
38
39
  const requests2 = await tab.requests();
40
+ const text = [];
39
41
  for (const request of requests2) {
40
42
  const rendered = await renderRequest(request, params.includeStatic);
41
43
  if (rendered)
42
- response.addResult(rendered);
44
+ text.push(rendered);
43
45
  }
46
+ await response.addResult("Network", text.join("\n"), { prefix: "network", ext: "log", suggestedFilename: params.filename });
47
+ }
48
+ });
49
+ const networkClear = (0, import_tool.defineTabTool)({
50
+ capability: "core",
51
+ skillOnly: true,
52
+ schema: {
53
+ name: "browser_network_clear",
54
+ title: "Clear network requests",
55
+ description: "Clear all network requests",
56
+ inputSchema: import_mcpBundle.z.object({}),
57
+ type: "readOnly"
58
+ },
59
+ handle: async (tab, params, response) => {
60
+ await tab.clearRequests();
44
61
  }
45
62
  });
46
63
  async function renderRequest(request, includeStatic) {
@@ -56,5 +73,6 @@ async function renderRequest(request, includeStatic) {
56
73
  return result.join(" ");
57
74
  }
58
75
  var network_default = [
59
- requests
76
+ requests,
77
+ networkClear
60
78
  ];
@@ -38,9 +38,10 @@ const pdf = (0, import_tool.defineTabTool)({
38
38
  type: "readOnly"
39
39
  },
40
40
  handle: async (tab, params, response) => {
41
- const fileName = await response.addFile(params.filename ?? (0, import_utils2.dateAsFileName)("pdf"), { origin: "llm", reason: "Page saved as PDF" });
42
- response.addCode(`await page.pdf(${(0, import_utils.formatObject)({ path: fileName })});`);
43
- await tab.page.pdf({ path: fileName });
41
+ const data = await tab.page.pdf();
42
+ const suggestedFilename = params.filename ?? (0, import_utils2.dateAsFileName)("page", "pdf");
43
+ await response.addResult("Page as pdf", data, { prefix: "page", ext: "pdf", suggestedFilename });
44
+ response.addCode(`await page.pdf(${(0, import_utils.formatObject)({ path: suggestedFilename })});`);
44
45
  }
45
46
  });
46
47
  var pdf_default = [
@@ -35,9 +35,10 @@ var import_vm = __toESM(require("vm"));
35
35
  var import_utils = require("playwright-core/lib/utils");
36
36
  var import_test = require("playwright/test");
37
37
  var import_mcpBundle = require("../../../mcpBundle");
38
+ var import_ai_runner = require("@cotestdev/ai-runner");
38
39
  var import_tool = require("./tool");
39
- var import_common = require("./common");
40
- const codeSchema = import_common.baseSchema.extend({
40
+ var import_schema = require("./schema");
41
+ const codeSchema = import_schema.baseSchema.extend({
41
42
  code: import_mcpBundle.z.string().describe(`A JavaScript function containing Playwright code to execute. It will be invoked with a single argument, page, which you can use for any page interaction. For example: \`async (page) => { await page.getByRole('button', { name: 'Submit' }).click(); return await page.title(); }\``)
42
43
  });
43
44
  const runCode = (0, import_tool.defineTabTool)({
@@ -50,7 +51,6 @@ const runCode = (0, import_tool.defineTabTool)({
50
51
  type: "action"
51
52
  },
52
53
  handle: async (tab, params, response) => {
53
- response.setIncludeSnapshot();
54
54
  response.addCode(`await (${params.code})(page);`);
55
55
  const __end__ = new import_utils.ManualPromise();
56
56
  const context = {
@@ -70,13 +70,12 @@ const runCode = (0, import_tool.defineTabTool)({
70
70
  await import_vm.default.runInContext(snippet, context);
71
71
  const result = await __end__;
72
72
  if (typeof result === "string")
73
- response.addResult(result);
73
+ response.addTextResult(result);
74
74
  });
75
75
  }
76
76
  });
77
- const scriptSchema = import_common.baseSchema.extend({
78
- code: import_mcpBundle.z.string().describe(`A JavaScript function containing Playwright code to execute. It will be invoked with a single argument, page, which you can use for any page interaction. For example: \`async (page) => { await page.getByRole('button', { name: 'Submit' }).click(); return await page.title(); }\``),
79
- params: import_mcpBundle.z.record(import_mcpBundle.z.any()).optional().describe("Parameters to pass to the script.")
77
+ const scriptSchema = import_mcpBundle.z.object({
78
+ code: import_mcpBundle.z.string().describe(`A JavaScript function containing Playwright code to execute. It will be invoked with a single argument, page, which you can use for any page interaction. For example: \`async (page) => { await page.getByRole('button', { name: 'Submit' }).click(); return await page.title(); }\``)
80
79
  });
81
80
  const runScript = (0, import_tool.defineTabTool)({
82
81
  capability: "extra",
@@ -93,12 +92,9 @@ const runScript = (0, import_tool.defineTabTool)({
93
92
  const context = {
94
93
  page: tab.page,
95
94
  expect: import_test.expect,
95
+ Runner: import_ai_runner.Runner,
96
96
  __end__
97
97
  };
98
- if (params.params) {
99
- for (const [key, value] of Object.entries(params.params))
100
- context[key] = value;
101
- }
102
98
  import_vm.default.createContext(context);
103
99
  await tab.waitForCompletion(async () => {
104
100
  const snippet = `(async () => {
@@ -112,7 +108,7 @@ const runScript = (0, import_tool.defineTabTool)({
112
108
  await import_vm.default.runInContext(snippet, context);
113
109
  const result = await __end__;
114
110
  if (typeof result === "string")
115
- response.addResult(result);
111
+ response.addTextResult(result);
116
112
  });
117
113
  }
118
114
  });
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var schema_exports = {};
20
+ __export(schema_exports, {
21
+ baseSchema: () => baseSchema
22
+ });
23
+ module.exports = __toCommonJS(schema_exports);
24
+ var import_mcpBundle = require("../../../mcpBundle");
25
+ const baseSchema = import_mcpBundle.z.object({
26
+ toolText: import_mcpBundle.z.string().describe("A human-readable description of the action to perform by the tool.")
27
+ });
28
+ // Annotate the CommonJS export names for ESM import in node:
29
+ 0 && (module.exports = {
30
+ baseSchema
31
+ });
@@ -1,9 +1,7 @@
1
1
  "use strict";
2
- var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
6
  var __export = (target, all) => {
9
7
  for (var name in all)
@@ -17,14 +15,6 @@ var __copyProps = (to, from, except, desc) => {
17
15
  }
18
16
  return to;
19
17
  };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
19
  var screenshot_exports = {};
30
20
  __export(screenshot_exports, {
@@ -32,15 +22,14 @@ __export(screenshot_exports, {
32
22
  scaleImageToFitMessage: () => scaleImageToFitMessage
33
23
  });
34
24
  module.exports = __toCommonJS(screenshot_exports);
35
- var import_fs = __toESM(require("fs"));
36
25
  var import_utils = require("playwright-core/lib/utils");
37
26
  var import_utilsBundle = require("playwright-core/lib/utilsBundle");
38
27
  var import_utils2 = require("playwright-core/lib/utils");
39
28
  var import_mcpBundle = require("../../../mcpBundle");
40
29
  var import_tool = require("./tool");
41
30
  var import_utils3 = require("./utils");
42
- var import_common = require("./common");
43
- const screenshotSchema = import_common.baseSchema.extend({
31
+ var import_schema = require("./schema");
32
+ const screenshotSchema = import_schema.baseSchema.extend({
44
33
  type: import_mcpBundle.z.enum(["png", "jpeg"]).default("png").describe("Image format for the screenshot. Default is png."),
45
34
  filename: import_mcpBundle.z.string().optional().describe("File name to save the screenshot to. Defaults to `page-{timestamp}.{png|jpeg}` if not specified. Prefer relative file names to stay within the output directory."),
46
35
  element: import_mcpBundle.z.string().optional().describe("Human-readable element description used to obtain permission to screenshot the element. If not provided, the screenshot will be taken of viewport. If element is provided, ref must be provided too."),
@@ -57,8 +46,6 @@ const screenshot = (0, import_tool.defineTabTool)({
57
46
  type: "readOnly"
58
47
  },
59
48
  handle: async (tab, params, response) => {
60
- if (!!params.element !== !!params.ref)
61
- throw new Error("Both element and ref must be provided or neither.");
62
49
  if (params.fullPage && params.ref)
63
50
  throw new Error("fullPage cannot be used with element screenshots.");
64
51
  const fileType = params.type || "png";
@@ -68,22 +55,17 @@ const screenshot = (0, import_tool.defineTabTool)({
68
55
  scale: "css",
69
56
  ...params.fullPage !== void 0 && { fullPage: params.fullPage }
70
57
  };
71
- const isElementScreenshot = params.element && params.ref;
72
- const screenshotTarget = isElementScreenshot ? params.element : params.fullPage ? "full page" : "viewport";
73
- const fileName = await response.addFile(params.filename || (0, import_utils3.dateAsFileName)(fileType), { origin: "llm", reason: `Screenshot of ${screenshotTarget}` });
74
- response.addCode(`// Screenshot ${screenshotTarget} and save it as ${fileName}`);
58
+ const screenshotTarget = params.ref ? params.element || "element" : params.fullPage ? "full page" : "viewport";
75
59
  const ref = params.ref ? await tab.refLocator({ element: params.element || "", ref: params.ref }) : null;
60
+ const data = ref ? await ref.locator.screenshot(options) : await tab.page.screenshot(options);
61
+ const suggestedFilename = params.filename || (0, import_utils3.dateAsFileName)(ref ? "element" : "page", fileType);
62
+ response.addCode(`// Screenshot ${screenshotTarget} and save it as ${suggestedFilename}`);
76
63
  if (ref)
77
- response.addCode(`await page.${ref.resolved}.screenshot(${(0, import_utils2.formatObject)(options)});`);
64
+ response.addCode(`await page.${ref.resolved}.screenshot(${(0, import_utils2.formatObject)({ ...options, path: suggestedFilename })});`);
78
65
  else
79
- response.addCode(`await page.screenshot(${(0, import_utils2.formatObject)(options)});`);
80
- const buffer = ref ? await ref.locator.screenshot(options) : await tab.page.screenshot(options);
81
- await (0, import_utils.mkdirIfNeeded)(fileName);
82
- await import_fs.default.promises.writeFile(fileName, buffer);
83
- response.addImage({
84
- contentType: fileType === "png" ? "image/png" : "image/jpeg",
85
- data: scaleImageToFitMessage(buffer, fileType)
86
- });
66
+ response.addCode(`await page.screenshot(${(0, import_utils2.formatObject)({ ...options, path: suggestedFilename })});`);
67
+ const contentType = fileType === "png" ? "image/png" : "image/jpeg";
68
+ await response.addResult(`Screenshot of ${screenshotTarget}`, data, { prefix: ref ? "element" : "page", ext: fileType, suggestedFilename, contentType });
87
69
  }
88
70
  });
89
71
  function scaleImageToFitMessage(buffer, imageType) {
@@ -1,9 +1,7 @@
1
1
  "use strict";
2
- var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
6
  var __export = (target, all) => {
9
7
  for (var name in all)
@@ -17,14 +15,6 @@ var __copyProps = (to, from, except, desc) => {
17
15
  }
18
16
  return to;
19
17
  };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
19
  var snapshot_exports = {};
30
20
  __export(snapshot_exports, {
@@ -32,11 +22,10 @@ __export(snapshot_exports, {
32
22
  elementSchema: () => elementSchema
33
23
  });
34
24
  module.exports = __toCommonJS(snapshot_exports);
35
- var import_fs = __toESM(require("fs"));
36
25
  var import_mcpBundle = require("../../../mcpBundle");
37
26
  var import_utils = require("playwright-core/lib/utils");
38
27
  var import_tool = require("./tool");
39
- var import_common = require("./common");
28
+ var import_schema = require("./schema");
40
29
  const snapshot = (0, import_tool.defineTool)({
41
30
  capability: "core",
42
31
  schema: {
@@ -50,17 +39,10 @@ const snapshot = (0, import_tool.defineTool)({
50
39
  },
51
40
  handle: async (context, params, response) => {
52
41
  await context.ensureTab();
53
- response.setIncludeFullSnapshot();
54
- if (params.filename) {
55
- await response.finish();
56
- const renderedResponse = response.render();
57
- const fileName = await response.addFile(params.filename, { origin: "llm", reason: "Saved snapshot" });
58
- await import_fs.default.promises.writeFile(fileName, renderedResponse.asText());
59
- response.setIncludeMetaOnly();
60
- }
42
+ response.setIncludeFullSnapshot(params.filename);
61
43
  }
62
44
  });
63
- const elementSchema = import_common.baseSchema.extend({
45
+ const elementSchema = import_schema.baseSchema.extend({
64
46
  element: import_mcpBundle.z.string().optional().describe("Human-readable element description used to obtain permission to interact with the element"),
65
47
  ref: import_mcpBundle.z.string().describe("Exact target element reference from the page snapshot")
66
48
  });
@@ -105,7 +87,7 @@ const drag = (0, import_tool.defineTabTool)({
105
87
  name: "browser_drag",
106
88
  title: "Drag mouse",
107
89
  description: "Perform drag and drop between two elements",
108
- inputSchema: import_mcpBundle.z.object({
90
+ inputSchema: import_schema.baseSchema.extend({
109
91
  startElement: import_mcpBundle.z.string().describe("Human-readable source element description used to obtain the permission to interact with the element"),
110
92
  startRef: import_mcpBundle.z.string().describe("Exact source element reference from the page snapshot"),
111
93
  endElement: import_mcpBundle.z.string().describe("Human-readable target element description used to obtain the permission to interact with the element"),
@@ -175,7 +157,39 @@ const pickLocator = (0, import_tool.defineTabTool)({
175
157
  },
176
158
  handle: async (tab, params, response) => {
177
159
  const { resolved } = await tab.refLocator(params);
178
- response.addResult(resolved);
160
+ response.addTextResult(resolved);
161
+ }
162
+ });
163
+ const check = (0, import_tool.defineTabTool)({
164
+ capability: "core-input",
165
+ skillOnly: true,
166
+ schema: {
167
+ name: "browser_check",
168
+ title: "Check",
169
+ description: "Check a checkbox or radio button",
170
+ inputSchema: elementSchema,
171
+ type: "input"
172
+ },
173
+ handle: async (tab, params, response) => {
174
+ const { locator, resolved } = await tab.refLocator(params);
175
+ response.addCode(`await page.${resolved}.check();`);
176
+ await locator.check();
177
+ }
178
+ });
179
+ const uncheck = (0, import_tool.defineTabTool)({
180
+ capability: "core-input",
181
+ skillOnly: true,
182
+ schema: {
183
+ name: "browser_uncheck",
184
+ title: "Uncheck",
185
+ description: "Uncheck a checkbox or radio button",
186
+ inputSchema: elementSchema,
187
+ type: "input"
188
+ },
189
+ handle: async (tab, params, response) => {
190
+ const { locator, resolved } = await tab.refLocator(params);
191
+ response.addCode(`await page.${resolved}.uncheck();`);
192
+ await locator.uncheck();
179
193
  }
180
194
  });
181
195
  var snapshot_default = [
@@ -184,7 +198,9 @@ var snapshot_default = [
184
198
  drag,
185
199
  hover,
186
200
  selectOption,
187
- pickLocator
201
+ pickLocator,
202
+ check,
203
+ uncheck
188
204
  ];
189
205
  // Annotate the CommonJS export names for ESM import in node:
190
206
  0 && (module.exports = {
@@ -23,14 +23,15 @@ __export(tabs_exports, {
23
23
  module.exports = __toCommonJS(tabs_exports);
24
24
  var import_mcpBundle = require("../../../mcpBundle");
25
25
  var import_tool = require("./tool");
26
- var import_common = require("./common");
26
+ var import_response = require("../response");
27
+ var import_schema = require("./schema");
27
28
  const browserTabs = (0, import_tool.defineTool)({
28
29
  capability: "core-tabs",
29
30
  schema: {
30
31
  name: "browser_tabs",
31
32
  title: "Manage tabs",
32
33
  description: "List, create, close, or select a browser tab.",
33
- inputSchema: import_common.baseSchema.extend({
34
+ inputSchema: import_schema.baseSchema.extend({
34
35
  action: import_mcpBundle.z.enum(["list", "new", "close", "select"]).describe("Operation to perform"),
35
36
  index: import_mcpBundle.z.number().optional().describe("Tab index, used for close/select. If omitted for close, current tab is closed.")
36
37
  }),
@@ -40,27 +41,26 @@ const browserTabs = (0, import_tool.defineTool)({
40
41
  switch (params.action) {
41
42
  case "list": {
42
43
  await context.ensureTab();
43
- response.setIncludeTabs();
44
- return;
44
+ break;
45
45
  }
46
46
  case "new": {
47
47
  await context.newTab();
48
- response.setIncludeTabs();
49
- return;
48
+ break;
50
49
  }
51
50
  case "close": {
52
51
  await context.closeTab(params.index);
53
- response.setIncludeFullSnapshot();
54
- return;
52
+ break;
55
53
  }
56
54
  case "select": {
57
55
  if (params.index === void 0)
58
56
  throw new Error("Tab index is required");
59
57
  await context.selectTab(params.index);
60
- response.setIncludeFullSnapshot();
61
- return;
58
+ break;
62
59
  }
63
60
  }
61
+ const tabHeaders = await Promise.all(context.tabs().map((tab) => tab.headerSnapshot()));
62
+ const result = (0, import_response.renderTabsMarkdown)(tabHeaders);
63
+ response.addTextResult(result.join("\n"));
64
64
  }
65
65
  });
66
66
  var tabs_default = [
@@ -31,15 +31,12 @@ function defineTabTool(tool) {
31
31
  handle: async (context, params, response) => {
32
32
  const tab = await context.ensureTab();
33
33
  const modalStates = tab.modalStates().map((state) => state.type);
34
- if (tool.clearsModalState && !modalStates.includes(tool.clearsModalState)) {
35
- response.setIncludeModalStates(tab.modalStates());
34
+ if (tool.clearsModalState && !modalStates.includes(tool.clearsModalState))
36
35
  response.addError(`Error: The tool "${tool.schema.name}" can only be used when there is related modal state present.`);
37
- } else if (!tool.clearsModalState && modalStates.length) {
38
- response.setIncludeModalStates(tab.modalStates());
36
+ else if (!tool.clearsModalState && modalStates.length)
39
37
  response.addError(`Error: Tool "${tool.schema.name}" does not handle the modal state.`);
40
- } else {
38
+ else
41
39
  return tool.handle(tab, params, response);
42
- }
43
40
  }
44
41
  };
45
42
  }
@@ -34,7 +34,7 @@ const tracingStart = (0, import_tool.defineTool)({
34
34
  },
35
35
  handle: async (context, params, response) => {
36
36
  const browserContext = await context.ensureBrowserContext();
37
- const tracesDir = await context.outputFile(`traces`, { origin: "code", reason: "Collecting trace" });
37
+ const tracesDir = await context.outputFile(`traces`, { origin: "code", title: "Collecting trace" });
38
38
  const name = "trace-" + Date.now();
39
39
  await browserContext.tracing.start({
40
40
  name,
@@ -45,7 +45,7 @@ const tracingStart = (0, import_tool.defineTool)({
45
45
  const traceLegend = `- Action log: ${tracesDir}/${name}.trace
46
46
  - Network log: ${tracesDir}/${name}.network
47
47
  - Resources with content by sha1: ${tracesDir}/resources`;
48
- response.addResult(`Tracing started, saving to ${tracesDir}.
48
+ response.addTextResult(`Tracing started, saving to ${tracesDir}.
49
49
  ${traceLegend}`);
50
50
  browserContext.tracing[traceLegendSymbol] = traceLegend;
51
51
  }
@@ -63,7 +63,7 @@ const tracingStop = (0, import_tool.defineTool)({
63
63
  const browserContext = await context.ensureBrowserContext();
64
64
  await browserContext.tracing.stop();
65
65
  const traceLegend = browserContext.tracing[traceLegendSymbol];
66
- response.addResult(`Tracing stopped.
66
+ response.addTextResult(`Tracing stopped.
67
67
  ${traceLegend}`);
68
68
  }
69
69
  });
@@ -62,9 +62,9 @@ async function waitForCompletion(tab, callback) {
62
62
  async function callOnPageNoTrace(page, callback) {
63
63
  return await page._wrapApiCall(() => callback(page), { internal: true });
64
64
  }
65
- function dateAsFileName(extension) {
65
+ function dateAsFileName(prefix, extension) {
66
66
  const date = /* @__PURE__ */ new Date();
67
- return `page-${date.toISOString().replace(/[:.]/g, "-")}.${extension}`;
67
+ return `${prefix}-${date.toISOString().replace(/[:.]/g, "-")}.${extension}`;
68
68
  }
69
69
  function eventWaiter(page, event, timeout) {
70
70
  const disposables = [];
@@ -24,14 +24,14 @@ module.exports = __toCommonJS(verify_exports);
24
24
  var import_mcpBundle = require("../../../mcpBundle");
25
25
  var import_utils = require("playwright-core/lib/utils");
26
26
  var import_tool = require("./tool");
27
- var import_common = require("./common");
27
+ var import_schema = require("./schema");
28
28
  const verifyElement = (0, import_tool.defineTabTool)({
29
29
  capability: "testing",
30
30
  schema: {
31
31
  name: "browser_verify_element_visible",
32
32
  title: "Verify element visible",
33
33
  description: "Verify element is visible on the page",
34
- inputSchema: import_common.baseSchema.extend({
34
+ inputSchema: import_schema.baseSchema.extend({
35
35
  role: import_mcpBundle.z.string().describe('ROLE of the element. Can be found in the snapshot like this: `- {ROLE} "Accessible Name":`'),
36
36
  accessibleName: import_mcpBundle.z.string().describe('ACCESSIBLE_NAME of the element. Can be found in the snapshot like this: `- role "{ACCESSIBLE_NAME}"`')
37
37
  }),
@@ -44,7 +44,7 @@ const verifyElement = (0, import_tool.defineTabTool)({
44
44
  return;
45
45
  }
46
46
  response.addCode(`await expect(page.getByRole(${(0, import_utils.escapeWithQuotes)(params.role)}, { name: ${(0, import_utils.escapeWithQuotes)(params.accessibleName)} })).toBeVisible();`);
47
- response.addResult("Done");
47
+ response.addTextResult("Done");
48
48
  }
49
49
  });
50
50
  const verifyText = (0, import_tool.defineTabTool)({
@@ -53,7 +53,7 @@ const verifyText = (0, import_tool.defineTabTool)({
53
53
  name: "browser_verify_text_visible",
54
54
  title: "Verify text visible",
55
55
  description: `Verify text is visible on the page. Prefer ${verifyElement.schema.name} if possible.`,
56
- inputSchema: import_common.baseSchema.extend({
56
+ inputSchema: import_schema.baseSchema.extend({
57
57
  text: import_mcpBundle.z.string().describe('TEXT to verify. Can be found in the snapshot like this: `- role "Accessible Name": {TEXT}` or like this: `- text: {TEXT}`')
58
58
  }),
59
59
  type: "assertion"
@@ -65,7 +65,7 @@ const verifyText = (0, import_tool.defineTabTool)({
65
65
  return;
66
66
  }
67
67
  response.addCode(`await expect(page.getByText(${(0, import_utils.escapeWithQuotes)(params.text)})).toBeVisible();`);
68
- response.addResult("Done");
68
+ response.addTextResult("Done");
69
69
  }
70
70
  });
71
71
  const verifyList = (0, import_tool.defineTabTool)({
@@ -74,7 +74,7 @@ const verifyList = (0, import_tool.defineTabTool)({
74
74
  name: "browser_verify_list_visible",
75
75
  title: "Verify list visible",
76
76
  description: "Verify list is visible on the page",
77
- inputSchema: import_common.baseSchema.extend({
77
+ inputSchema: import_schema.baseSchema.extend({
78
78
  element: import_mcpBundle.z.string().describe("Human-readable list description"),
79
79
  ref: import_mcpBundle.z.string().describe("Exact target element reference that points to the list"),
80
80
  items: import_mcpBundle.z.array(import_mcpBundle.z.string()).describe("Items to verify")
@@ -97,7 +97,7 @@ const verifyList = (0, import_tool.defineTabTool)({
97
97
  ${itemTexts.map((t) => ` - listitem: ${(0, import_utils.escapeWithQuotes)(t, '"')}`).join("\n")}
98
98
  \``;
99
99
  response.addCode(`await expect(page.locator('body')).toMatchAriaSnapshot(${ariaSnapshot});`);
100
- response.addResult("Done");
100
+ response.addTextResult("Done");
101
101
  }
102
102
  });
103
103
  const verifyValue = (0, import_tool.defineTabTool)({
@@ -106,7 +106,7 @@ const verifyValue = (0, import_tool.defineTabTool)({
106
106
  name: "browser_verify_value",
107
107
  title: "Verify value",
108
108
  description: "Verify element value",
109
- inputSchema: import_common.baseSchema.extend({
109
+ inputSchema: import_schema.baseSchema.extend({
110
110
  type: import_mcpBundle.z.enum(["textbox", "checkbox", "radio", "combobox", "slider"]).describe("Type of the element"),
111
111
  element: import_mcpBundle.z.string().describe("Human-readable element description"),
112
112
  ref: import_mcpBundle.z.string().describe("Exact target element reference that points to the element"),
@@ -133,7 +133,7 @@ const verifyValue = (0, import_tool.defineTabTool)({
133
133
  const matcher = value ? "toBeChecked" : "not.toBeChecked";
134
134
  response.addCode(`await expect(${locatorSource}).${matcher}();`);
135
135
  }
136
- response.addResult("Done");
136
+ response.addTextResult("Done");
137
137
  }
138
138
  });
139
139
  var verify_default = [