@cotestdev/mcp_playwright 0.0.35 → 0.0.36

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 (39) 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 +4 -4
  9. package/lib/mcp/browser/tools/console.js +20 -3
  10. package/lib/mcp/browser/tools/dialogs.js +0 -1
  11. package/lib/mcp/browser/tools/evaluate.js +6 -4
  12. package/lib/mcp/browser/tools/install.js +4 -1
  13. package/lib/mcp/browser/tools/keyboard.js +75 -8
  14. package/lib/mcp/browser/tools/mouse.js +59 -7
  15. package/lib/mcp/browser/tools/navigate.js +48 -5
  16. package/lib/mcp/browser/tools/network.js +21 -3
  17. package/lib/mcp/browser/tools/pdf.js +4 -3
  18. package/lib/mcp/browser/tools/runCode.js +6 -10
  19. package/lib/mcp/browser/tools/screenshot.js +8 -26
  20. package/lib/mcp/browser/tools/snapshot.js +38 -22
  21. package/lib/mcp/browser/tools/tabs.js +8 -8
  22. package/lib/mcp/browser/tools/tool.js +3 -6
  23. package/lib/mcp/browser/tools/tracing.js +3 -3
  24. package/lib/mcp/browser/tools/utils.js +2 -2
  25. package/lib/mcp/browser/tools/verify.js +4 -4
  26. package/lib/mcp/browser/tools/wait.js +1 -1
  27. package/lib/mcp/browser/tools.js +2 -2
  28. package/lib/mcp/extension/extensionContextFactory.js +2 -2
  29. package/lib/mcp/program.js +3 -2
  30. package/lib/mcp/terminal/cli.js +4 -216
  31. package/lib/mcp/terminal/command.js +56 -0
  32. package/lib/mcp/terminal/commands.js +528 -0
  33. package/lib/mcp/terminal/daemon.js +42 -25
  34. package/lib/mcp/terminal/helpGenerator.js +152 -0
  35. package/lib/mcp/terminal/program.js +434 -0
  36. package/lib/mcp/terminal/socketConnection.js +2 -4
  37. package/lib/mcpBundleImpl/index.js +44 -44
  38. package/lib/util.js +3 -6
  39. package/package.json +1 -1
@@ -20,7 +20,8 @@ var tab_exports = {};
20
20
  __export(tab_exports, {
21
21
  Tab: () => Tab,
22
22
  TabEvents: () => TabEvents,
23
- renderModalStates: () => renderModalStates
23
+ renderModalStates: () => renderModalStates,
24
+ shouldIncludeMessage: () => shouldIncludeMessage
24
25
  });
25
26
  module.exports = __toCommonJS(tab_exports);
26
27
  var import_events = require("events");
@@ -36,26 +37,27 @@ const TabEvents = {
36
37
  class Tab extends import_events.EventEmitter {
37
38
  constructor(context, page, onPageClose) {
38
39
  super();
39
- this._lastTitle = "about:blank";
40
+ this._lastHeader = { title: "about:blank", url: "about:blank", current: false };
40
41
  this._consoleMessages = [];
41
- this._recentConsoleMessages = [];
42
+ this._downloads = [];
42
43
  this._requests = /* @__PURE__ */ new Set();
43
44
  this._modalStates = [];
44
- this._downloads = [];
45
45
  this._needsFullSnapshot = false;
46
+ this._eventEntries = [];
47
+ this._recentEventEntries = [];
46
48
  this.context = context;
47
49
  this.page = page;
48
50
  this._onPageClose = onPageClose;
49
51
  page.on("console", (event) => this._handleConsoleMessage(messageToConsoleMessage(event)));
50
52
  page.on("pageerror", (error) => this._handleConsoleMessage(pageErrorToConsoleMessage(error)));
51
- page.on("request", (request) => this._requests.add(request));
53
+ page.on("request", (request) => this._handleRequest(request));
52
54
  page.on("close", () => this._onClose());
53
55
  page.on("filechooser", (chooser) => {
54
56
  this.setModalState({
55
57
  type: "fileChooser",
56
58
  description: "File chooser",
57
59
  fileChooser: chooser,
58
- clearedBy: import_files.uploadFile.schema.name
60
+ clearedBy: { tool: import_files.uploadFile.schema.name, skill: "upload" }
59
61
  });
60
62
  });
61
63
  page.on("dialog", (dialog) => this._dialogShown(dialog));
@@ -110,39 +112,54 @@ class Tab extends import_events.EventEmitter {
110
112
  type: "dialog",
111
113
  description: `"${dialog.type()}" dialog with message "${dialog.message()}"`,
112
114
  dialog,
113
- clearedBy: import_dialogs.handleDialog.schema.name
115
+ clearedBy: { tool: import_dialogs.handleDialog.schema.name, skill: "dialog-accept or dialog-dismiss" }
114
116
  });
115
117
  }
116
118
  async _downloadStarted(download) {
117
119
  const entry = {
118
120
  download,
119
121
  finished: false,
120
- outputFile: await this.context.outputFile(download.suggestedFilename(), { origin: "web", reason: "Saving download" })
122
+ outputFile: await this.context.outputFile(download.suggestedFilename(), { origin: "web", title: "Saving download" })
121
123
  };
122
124
  this._downloads.push(entry);
125
+ this._addLogEntry({ type: "download-start", wallTime: Date.now(), download: entry });
123
126
  await download.saveAs(entry.outputFile);
124
127
  entry.finished = true;
128
+ this._addLogEntry({ type: "download-finish", wallTime: Date.now(), download: entry });
125
129
  }
126
130
  _clearCollectedArtifacts() {
127
131
  this._consoleMessages.length = 0;
128
- this._recentConsoleMessages.length = 0;
132
+ this._downloads.length = 0;
129
133
  this._requests.clear();
134
+ this._eventEntries.length = 0;
135
+ this._recentEventEntries.length = 0;
136
+ }
137
+ _handleRequest(request) {
138
+ this._requests.add(request);
139
+ this._addLogEntry({ type: "request", wallTime: Date.now(), request });
130
140
  }
131
141
  _handleConsoleMessage(message) {
132
142
  this._consoleMessages.push(message);
133
- this._recentConsoleMessages.push(message);
143
+ this._addLogEntry({ type: "console", wallTime: Date.now(), message });
144
+ }
145
+ _addLogEntry(entry) {
146
+ this._eventEntries.push(entry);
147
+ this._recentEventEntries.push(entry);
134
148
  }
135
149
  _onClose() {
136
150
  this._clearCollectedArtifacts();
137
151
  this._onPageClose(this);
138
152
  }
139
- async updateTitle() {
153
+ async headerSnapshot() {
154
+ let title;
140
155
  await this._raceAgainstModalStates(async () => {
141
- this._lastTitle = await (0, import_utils2.callOnPageNoTrace)(this.page, (page) => page.title());
156
+ title = await (0, import_utils2.callOnPageNoTrace)(this.page, (page) => page.title());
142
157
  });
143
- }
144
- lastTitle() {
145
- return this._lastTitle;
158
+ if (this._lastHeader.title !== title || this._lastHeader.url !== this.page.url() || this._lastHeader.current !== this.isCurrentTab()) {
159
+ this._lastHeader = { title: title ?? "", url: this.page.url(), current: this.isCurrentTab() };
160
+ return { ...this._lastHeader, changed: true };
161
+ }
162
+ return { ...this._lastHeader, changed: false };
146
163
  }
147
164
  isCurrentTab() {
148
165
  return this === this.context.currentTab();
@@ -175,37 +192,40 @@ class Tab extends import_events.EventEmitter {
175
192
  await this._initializedPromise;
176
193
  return this._consoleMessages.filter((message) => shouldIncludeMessage(level, message.type));
177
194
  }
195
+ async clearConsoleMessages() {
196
+ await this._initializedPromise;
197
+ this._consoleMessages.length = 0;
198
+ }
178
199
  async requests() {
179
200
  await this._initializedPromise;
180
201
  return this._requests;
181
202
  }
203
+ async clearRequests() {
204
+ await this._initializedPromise;
205
+ this._requests.clear();
206
+ }
182
207
  async captureSnapshot() {
183
208
  await this._initializedPromise;
184
209
  let tabSnapshot;
185
210
  const modalStates = await this._raceAgainstModalStates(async () => {
186
211
  const snapshot = await this.page._snapshotForAI({ track: "response" });
187
212
  tabSnapshot = {
188
- url: this.page.url(),
189
- title: await this.page.title(),
190
213
  ariaSnapshot: snapshot.full,
191
214
  ariaSnapshotDiff: this._needsFullSnapshot ? void 0 : snapshot.incremental,
192
215
  modalStates: [],
193
- consoleMessages: [],
194
- downloads: this._downloads
216
+ events: []
195
217
  };
196
218
  });
197
219
  if (tabSnapshot) {
198
- tabSnapshot.consoleMessages = this._recentConsoleMessages.filter((message) => shouldIncludeMessage(this.context.config.console.level, message.type));
199
- this._recentConsoleMessages = [];
220
+ tabSnapshot.events = this._recentEventEntries;
221
+ this._recentEventEntries = [];
200
222
  }
201
223
  this._needsFullSnapshot = !tabSnapshot;
202
224
  return tabSnapshot ?? {
203
- url: this.page.url(),
204
- title: "",
205
225
  ariaSnapshot: "",
226
+ ariaSnapshotDiff: "",
206
227
  modalStates,
207
- consoleMessages: [],
208
- downloads: []
228
+ events: []
209
229
  };
210
230
  }
211
231
  _javaScriptBlocked() {
@@ -279,12 +299,12 @@ function pageErrorToConsoleMessage(errorOrValue) {
279
299
  toString: () => String(errorOrValue)
280
300
  };
281
301
  }
282
- function renderModalStates(modalStates) {
302
+ function renderModalStates(config, modalStates) {
283
303
  const result = [];
284
304
  if (modalStates.length === 0)
285
305
  result.push("- There is no modal state present");
286
306
  for (const state of modalStates)
287
- result.push(`- [${state.description}]: can be handled by the "${state.clearedBy}" tool`);
307
+ result.push(`- [${state.description}]: can be handled by ${config.skillMode ? state.clearedBy.skill : state.clearedBy.tool}`);
288
308
  return result;
289
309
  }
290
310
  const consoleMessageLevels = ["error", "warning", "info", "debug"];
@@ -326,5 +346,6 @@ const tabSymbol = Symbol("tabSymbol");
326
346
  0 && (module.exports = {
327
347
  Tab,
328
348
  TabEvents,
329
- renderModalStates
349
+ renderModalStates,
350
+ shouldIncludeMessage
330
351
  });
@@ -24,6 +24,7 @@ __export(common_exports, {
24
24
  module.exports = __toCommonJS(common_exports);
25
25
  var import_mcpBundle = require("../../../mcpBundle");
26
26
  var import_tool = require("./tool");
27
+ var import_response = require("../response");
27
28
  const baseSchema = import_mcpBundle.z.object({
28
29
  toolText: import_mcpBundle.z.string().describe("A human-readable description of the action to perform by the tool.")
29
30
  });
@@ -38,7 +39,8 @@ const close = (0, import_tool.defineTool)({
38
39
  },
39
40
  handle: async (context, params, response) => {
40
41
  await context.closeBrowserContext();
41
- response.setIncludeTabs();
42
+ const result = (0, import_response.renderTabsMarkdown)([]);
43
+ response.addTextResult(result.join("\n"));
42
44
  response.addCode(`await page.close()`);
43
45
  }
44
46
  });
@@ -56,9 +58,7 @@ const resize = (0, import_tool.defineTabTool)({
56
58
  },
57
59
  handle: async (tab, params, response) => {
58
60
  response.addCode(`await page.setViewportSize({ width: ${params.width}, height: ${params.height} });`);
59
- await tab.waitForCompletion(async () => {
60
- await tab.page.setViewportSize({ width: params.width, height: params.height });
61
- });
61
+ await tab.page.setViewportSize({ width: params.width, height: params.height });
62
62
  }
63
63
  });
64
64
  var common_default = [
@@ -30,15 +30,32 @@ const console = (0, import_tool.defineTabTool)({
30
30
  title: "Get console messages",
31
31
  description: "Returns all console messages",
32
32
  inputSchema: import_mcpBundle.z.object({
33
- level: import_mcpBundle.z.enum(["error", "warning", "info", "debug"]).default("info").describe('Level of the console messages to return. Each level includes the messages of more severe levels. Defaults to "info".')
33
+ level: import_mcpBundle.z.enum(["error", "warning", "info", "debug"]).default("info").describe('Level of the console messages to return. Each level includes the messages of more severe levels. Defaults to "info".'),
34
+ filename: import_mcpBundle.z.string().optional().describe("Filename to save the console messages to. If not provided, messages are returned as text.")
34
35
  }),
35
36
  type: "readOnly"
36
37
  },
37
38
  handle: async (tab, params, response) => {
38
39
  const messages = await tab.consoleMessages(params.level);
39
- messages.map((message) => response.addResult(message.toString()));
40
+ const text = messages.map((message) => message.toString()).join("\n");
41
+ await response.addResult("Console", text, { prefix: "console", ext: "log", suggestedFilename: params.filename });
42
+ }
43
+ });
44
+ const consoleClear = (0, import_tool.defineTabTool)({
45
+ capability: "core",
46
+ skillOnly: true,
47
+ schema: {
48
+ name: "browser_console_clear",
49
+ title: "Clear console messages",
50
+ description: "Clear all console messages",
51
+ inputSchema: import_mcpBundle.z.object({}),
52
+ type: "readOnly"
53
+ },
54
+ handle: async (tab) => {
55
+ await tab.clearConsoleMessages();
40
56
  }
41
57
  });
42
58
  var console_default = [
43
- console
59
+ console,
60
+ consoleClear
44
61
  ];
@@ -38,7 +38,6 @@ const handleDialog = (0, import_tool.defineTabTool)({
38
38
  type: "action"
39
39
  },
40
40
  handle: async (tab, params, response) => {
41
- response.setIncludeSnapshot();
42
41
  const dialogState = tab.modalStates().find((state) => state.type === "dialog");
43
42
  if (!dialogState)
44
43
  throw new Error("No dialog visible");
@@ -40,10 +40,11 @@ const evaluate = (0, import_tool.defineTabTool)({
40
40
  type: "action"
41
41
  },
42
42
  handle: async (tab, params, response) => {
43
- response.setIncludeSnapshot();
44
43
  let locator;
45
- if (params.ref && params.element) {
46
- locator = await tab.refLocator({ ref: params.ref, element: params.element });
44
+ if (!params.function.includes("=>"))
45
+ params.function = `() => (${params.function})`;
46
+ if (params.ref) {
47
+ locator = await tab.refLocator({ ref: params.ref, element: params.element || "element" });
47
48
  response.addCode(`await page.${locator.resolved}.evaluate(${(0, import_utils.escapeWithQuotes)(params.function)});`);
48
49
  } else {
49
50
  response.addCode(`await page.evaluate(${(0, import_utils.escapeWithQuotes)(params.function)});`);
@@ -51,7 +52,8 @@ const evaluate = (0, import_tool.defineTabTool)({
51
52
  await tab.waitForCompletion(async () => {
52
53
  const receiver = locator?.locator ?? tab.page;
53
54
  const result = await receiver._evaluateFunction(params.function);
54
- response.addResult(JSON.stringify(result, null, 2) || "undefined");
55
+ const text = JSON.stringify(result, null, 2) || "undefined";
56
+ response.addTextResult(text);
55
57
  });
56
58
  }
57
59
  });
@@ -35,6 +35,7 @@ var import_child_process = require("child_process");
35
35
  var import_path = __toESM(require("path"));
36
36
  var import_mcpBundle = require("../../../mcpBundle");
37
37
  var import_tool = require("./tool");
38
+ var import_response = require("../response");
38
39
  const install = (0, import_tool.defineTool)({
39
40
  capability: "core-install",
40
41
  schema: {
@@ -61,7 +62,9 @@ const install = (0, import_tool.defineTool)({
61
62
  reject(new Error(`Failed to install browser: ${output.join("")}`));
62
63
  });
63
64
  });
64
- response.setIncludeTabs();
65
+ const tabHeaders = await Promise.all(context.tabs().map((tab) => tab.headerSnapshot()));
66
+ const result = (0, import_response.renderTabsMarkdown)(tabHeaders);
67
+ response.addTextResult(result.join("\n"));
65
68
  }
66
69
  });
67
70
  var install_default = [
@@ -25,8 +25,8 @@ var import_mcpBundle = require("../../../mcpBundle");
25
25
  var import_tool = require("./tool");
26
26
  var import_snapshot = require("./snapshot");
27
27
  var import_common = require("./common");
28
- const pressKey = (0, import_tool.defineTabTool)({
29
- capability: "core",
28
+ const press = (0, import_tool.defineTabTool)({
29
+ capability: "core-input",
30
30
  schema: {
31
31
  name: "browser_press_key",
32
32
  title: "Press a key",
@@ -37,12 +37,42 @@ const pressKey = (0, import_tool.defineTabTool)({
37
37
  type: "input"
38
38
  },
39
39
  handle: async (tab, params, response) => {
40
- response.setIncludeSnapshot();
41
40
  response.addCode(`// Press ${params.key}`);
42
41
  response.addCode(`await page.keyboard.press('${params.key}');`);
43
- await tab.waitForCompletion(async () => {
42
+ if (params.key === "Enter") {
43
+ response.setIncludeSnapshot();
44
+ await tab.waitForCompletion(async () => {
45
+ await tab.page.keyboard.press("Enter");
46
+ });
47
+ } else {
44
48
  await tab.page.keyboard.press(params.key);
45
- });
49
+ }
50
+ }
51
+ });
52
+ const pressSequentially = (0, import_tool.defineTabTool)({
53
+ capability: "core-input",
54
+ skillOnly: true,
55
+ schema: {
56
+ name: "browser_press_sequentially",
57
+ title: "Type text key by key",
58
+ description: "Type text key by key on the keyboard",
59
+ inputSchema: import_common.baseSchema.extend({
60
+ text: import_mcpBundle.z.string().describe("Text to type"),
61
+ submit: import_mcpBundle.z.boolean().optional().describe("Whether to submit entered text (press Enter after)")
62
+ }),
63
+ type: "input"
64
+ },
65
+ handle: async (tab, params, response) => {
66
+ response.addCode(`// Press ${params.text}`);
67
+ response.addCode(`await page.keyboard.type('${params.text}');`);
68
+ await tab.page.keyboard.type(params.text);
69
+ if (params.submit) {
70
+ response.addCode(`await page.keyboard.press('Enter');`);
71
+ response.setIncludeSnapshot();
72
+ await tab.waitForCompletion(async () => {
73
+ await tab.page.keyboard.press("Enter");
74
+ });
75
+ }
46
76
  }
47
77
  });
48
78
  const typeSchema = import_snapshot.elementSchema.extend({
@@ -51,7 +81,7 @@ const typeSchema = import_snapshot.elementSchema.extend({
51
81
  slowly: import_mcpBundle.z.boolean().optional().describe("Whether to type one character at a time. Useful for triggering key handlers in the page. By default entire text is filled in at once.")
52
82
  });
53
83
  const type = (0, import_tool.defineTabTool)({
54
- capability: "core",
84
+ capability: "core-input",
55
85
  schema: {
56
86
  name: "browser_type",
57
87
  title: "Type text",
@@ -79,7 +109,44 @@ const type = (0, import_tool.defineTabTool)({
79
109
  });
80
110
  }
81
111
  });
112
+ const keydown = (0, import_tool.defineTabTool)({
113
+ capability: "core-input",
114
+ skillOnly: true,
115
+ schema: {
116
+ name: "browser_keydown",
117
+ title: "Press a key down",
118
+ description: "Press a key down on the keyboard",
119
+ inputSchema: import_common.baseSchema.extend({
120
+ key: import_mcpBundle.z.string().describe("Name of the key to press or a character to generate, such as `ArrowLeft` or `a`")
121
+ }),
122
+ type: "input"
123
+ },
124
+ handle: async (tab, params, response) => {
125
+ response.addCode(`await page.keyboard.down('${params.key}');`);
126
+ await tab.page.keyboard.down(params.key);
127
+ }
128
+ });
129
+ const keyup = (0, import_tool.defineTabTool)({
130
+ capability: "core-input",
131
+ skillOnly: true,
132
+ schema: {
133
+ name: "browser_keyup",
134
+ title: "Press a key up",
135
+ description: "Press a key up on the keyboard",
136
+ inputSchema: import_common.baseSchema.extend({
137
+ key: import_mcpBundle.z.string().describe("Name of the key to press or a character to generate, such as `ArrowLeft` or `a`")
138
+ }),
139
+ type: "input"
140
+ },
141
+ handle: async (tab, params, response) => {
142
+ response.addCode(`await page.keyboard.up('${params.key}');`);
143
+ await tab.page.keyboard.up(params.key);
144
+ }
145
+ });
82
146
  var keyboard_default = [
83
- pressKey,
84
- type
147
+ press,
148
+ type,
149
+ pressSequentially,
150
+ keydown,
151
+ keyup
85
152
  ];
@@ -23,16 +23,13 @@ __export(mouse_exports, {
23
23
  module.exports = __toCommonJS(mouse_exports);
24
24
  var import_mcpBundle = require("../../../mcpBundle");
25
25
  var import_tool = require("./tool");
26
- const elementSchema = import_mcpBundle.z.object({
27
- element: import_mcpBundle.z.string().describe("Human-readable element description used to obtain permission to interact with the element")
28
- });
29
26
  const mouseMove = (0, import_tool.defineTabTool)({
30
27
  capability: "vision",
31
28
  schema: {
32
29
  name: "browser_mouse_move_xy",
33
30
  title: "Move mouse",
34
31
  description: "Move mouse to a given position",
35
- inputSchema: elementSchema.extend({
32
+ inputSchema: import_mcpBundle.z.object({
36
33
  x: import_mcpBundle.z.number().describe("X coordinate"),
37
34
  y: import_mcpBundle.z.number().describe("Y coordinate")
38
35
  }),
@@ -46,13 +43,65 @@ const mouseMove = (0, import_tool.defineTabTool)({
46
43
  });
47
44
  }
48
45
  });
46
+ const mouseDown = (0, import_tool.defineTabTool)({
47
+ capability: "vision",
48
+ schema: {
49
+ name: "browser_mouse_down",
50
+ title: "Press mouse down",
51
+ description: "Press mouse down",
52
+ inputSchema: import_mcpBundle.z.object({
53
+ button: import_mcpBundle.z.enum(["left", "right", "middle"]).optional().describe("Button to press, defaults to left")
54
+ }),
55
+ type: "input"
56
+ },
57
+ handle: async (tab, params, response) => {
58
+ response.addCode(`// Press mouse down`);
59
+ response.addCode(`await page.mouse.down({ button: '${params.button}' });`);
60
+ await tab.page.mouse.down({ button: params.button });
61
+ }
62
+ });
63
+ const mouseUp = (0, import_tool.defineTabTool)({
64
+ capability: "vision",
65
+ schema: {
66
+ name: "browser_mouse_up",
67
+ title: "Press mouse up",
68
+ description: "Press mouse up",
69
+ inputSchema: import_mcpBundle.z.object({
70
+ button: import_mcpBundle.z.enum(["left", "right", "middle"]).optional().describe("Button to press, defaults to left")
71
+ }),
72
+ type: "input"
73
+ },
74
+ handle: async (tab, params, response) => {
75
+ response.addCode(`// Press mouse up`);
76
+ response.addCode(`await page.mouse.up({ button: '${params.button}' });`);
77
+ await tab.page.mouse.up({ button: params.button });
78
+ }
79
+ });
80
+ const mouseWheel = (0, import_tool.defineTabTool)({
81
+ capability: "vision",
82
+ schema: {
83
+ name: "browser_mouse_wheel",
84
+ title: "Scroll mouse wheel",
85
+ description: "Scroll mouse wheel",
86
+ inputSchema: import_mcpBundle.z.object({
87
+ deltaX: import_mcpBundle.z.number().default(0).describe("X delta"),
88
+ deltaY: import_mcpBundle.z.number().default(0).describe("Y delta")
89
+ }),
90
+ type: "input"
91
+ },
92
+ handle: async (tab, params, response) => {
93
+ response.addCode(`// Scroll mouse wheel`);
94
+ response.addCode(`await page.mouse.wheel(${params.deltaX}, ${params.deltaY});`);
95
+ await tab.page.mouse.wheel(params.deltaX, params.deltaY);
96
+ }
97
+ });
49
98
  const mouseClick = (0, import_tool.defineTabTool)({
50
99
  capability: "vision",
51
100
  schema: {
52
101
  name: "browser_mouse_click_xy",
53
102
  title: "Click",
54
103
  description: "Click left mouse button at a given position",
55
- inputSchema: elementSchema.extend({
104
+ inputSchema: import_mcpBundle.z.object({
56
105
  x: import_mcpBundle.z.number().describe("X coordinate"),
57
106
  y: import_mcpBundle.z.number().describe("Y coordinate")
58
107
  }),
@@ -77,7 +126,7 @@ const mouseDrag = (0, import_tool.defineTabTool)({
77
126
  name: "browser_mouse_drag_xy",
78
127
  title: "Drag mouse",
79
128
  description: "Drag left mouse button to a given position",
80
- inputSchema: elementSchema.extend({
129
+ inputSchema: import_mcpBundle.z.object({
81
130
  startX: import_mcpBundle.z.number().describe("Start X coordinate"),
82
131
  startY: import_mcpBundle.z.number().describe("Start Y coordinate"),
83
132
  endX: import_mcpBundle.z.number().describe("End X coordinate"),
@@ -103,5 +152,8 @@ const mouseDrag = (0, import_tool.defineTabTool)({
103
152
  var mouse_default = [
104
153
  mouseMove,
105
154
  mouseClick,
106
- mouseDrag
155
+ mouseDrag,
156
+ mouseDown,
157
+ mouseUp,
158
+ mouseWheel
107
159
  ];
@@ -25,7 +25,7 @@ var import_mcpBundle = require("../../../mcpBundle");
25
25
  var import_tool = require("./tool");
26
26
  var import_common = require("./common");
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",
@@ -37,17 +37,26 @@ const navigate = (0, import_tool.defineTool)({
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",
59
+ description: "Go back to the previous page in the history",
51
60
  inputSchema: import_common.baseSchema.extend({}),
52
61
  type: "action"
53
62
  },
@@ -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_common.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_common.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 = [