mcpbrowser 0.3.44 → 0.3.45

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 (41) hide show
  1. package/README.md +34 -34
  2. package/package.json +1 -1
  3. package/src/actions/accept-eula.js +5 -5
  4. package/src/actions/click-element.js +25 -25
  5. package/src/actions/close-tab.js +8 -8
  6. package/src/actions/execute-javascript.js +9 -9
  7. package/src/actions/fetch-page.js +15 -15
  8. package/src/actions/get-current-html.js +16 -16
  9. package/src/actions/navigate-history.js +21 -21
  10. package/src/actions/plugin-action.js +15 -15
  11. package/src/actions/plugin-info.js +10 -10
  12. package/src/actions/scroll-page.js +21 -21
  13. package/src/actions/take-screenshot.js +16 -16
  14. package/src/actions/type-text.js +28 -28
  15. package/src/cli/help.js +1 -1
  16. package/src/core/logger.js +51 -2
  17. package/src/core/plugin-loader.js +2 -2
  18. package/src/core/responses.js +4 -4
  19. package/src/mcp-browser.js +20 -12
  20. package/src/plugins/_example/index.js +2 -2
  21. package/src/plugins/gcal/actions/check-availability.js +1 -1
  22. package/src/plugins/gcal/actions/create-event.js +1 -1
  23. package/src/plugins/gcal/actions/delete-event.js +1 -1
  24. package/src/plugins/gcal/actions/edit-event.js +1 -1
  25. package/src/plugins/gcal/actions/list-events.js +1 -1
  26. package/src/plugins/gcal/actions/read-event.js +1 -1
  27. package/src/plugins/gcal/actions/rsvp-event.js +1 -1
  28. package/src/plugins/gcal/actions/search-events.js +1 -1
  29. package/src/plugins/gcal/helpers.js +2 -2
  30. package/src/plugins/gmail/actions/archive-email.js +1 -1
  31. package/src/plugins/gmail/actions/compose-email.js +1 -1
  32. package/src/plugins/gmail/actions/delete-email.js +1 -1
  33. package/src/plugins/gmail/actions/forward-email.js +1 -1
  34. package/src/plugins/gmail/actions/label-email.js +1 -1
  35. package/src/plugins/gmail/actions/list-emails.js +1 -1
  36. package/src/plugins/gmail/actions/mark-read.js +1 -1
  37. package/src/plugins/gmail/actions/mark-unread.js +1 -1
  38. package/src/plugins/gmail/actions/read-email.js +1 -1
  39. package/src/plugins/gmail/actions/reply-email.js +1 -1
  40. package/src/plugins/gmail/actions/search-emails.js +1 -1
  41. package/src/plugins/gmail/helpers.js +3 -3
@@ -16,7 +16,7 @@ import logger from '../core/logger.js';
16
16
  // ============================================================================
17
17
 
18
18
  /**
19
- * Response for successful type_text operations
19
+ * Response for successful browser_type_text operations
20
20
  */
21
21
  export class TypeTextSuccessResponse extends MCPResponse {
22
22
  /**
@@ -64,9 +64,9 @@ export class TypeTextSuccessResponse extends MCPResponse {
64
64
  * @type {Tool}
65
65
  */
66
66
  export const TYPE_TEXT_TOOL = {
67
- name: "type_text",
67
+ name: "browser_type_text",
68
68
  title: "Type Text",
69
- description: "**BROWSER INTERACTION** - Types text into multiple input fields on browser-loaded pages in a single call. Use this for filling forms, entering search queries, or any text input on the page.\n\nWorks with input fields, textareas, and other editable elements. Supports filling multiple fields at once for efficient form filling.\n\n**PREREQUISITE**: Page MUST be loaded with fetch_webpage first. This tool operates on an already-loaded page in the browser.",
69
+ description: "**BROWSER INTERACTION** - Types text into multiple input fields on browser-loaded pages in a single call. Use this for filling forms, entering search queries, or any text input on the page.\n\nWorks with input fields, textareas, and other editable elements. Supports filling multiple fields at once for efficient form filling.\n\n**PREREQUISITE**: Page MUST be loaded with browser_fetch_webpage first. This tool operates on an already-loaded page in the browser.",
70
70
  inputSchema: {
71
71
  type: "object",
72
72
  properties: {
@@ -137,7 +137,7 @@ const TYPE_DELAY_MS = 10;
137
137
  */
138
138
  export async function typeText({ url, fields, returnHtml = true, removeUnnecessaryHTML = true, postTypeWait = 1000 }) {
139
139
  const startTime = Date.now();
140
- logger.info(`type_text called: ${fields?.length || 0} fields, url=${url}`);
140
+ logger.info(`browser_type_text called: ${fields?.length || 0} fields, url=${url}`);
141
141
 
142
142
  if (!url) {
143
143
  throw new Error("url parameter is required");
@@ -169,7 +169,7 @@ export async function typeText({ url, fields, returnHtml = true, removeUnnecessa
169
169
  try {
170
170
  await getBrowser();
171
171
  } catch (err) {
172
- logger.error(`type_text: Failed to connect to browser: ${err.message}`);
172
+ logger.error(`browser_type_text: Failed to connect to browser: ${err.message}`);
173
173
  return new InformationalResponse(
174
174
  `Browser connection failed: ${err.message}`,
175
175
  'The browser must be running with remote debugging enabled.',
@@ -186,15 +186,15 @@ export async function typeText({ url, fields, returnHtml = true, removeUnnecessa
186
186
 
187
187
  if (!page) {
188
188
  const isConnectionLost = pageError && pageError.includes('connection');
189
- logger.debug(`type_text: ${pageError || 'No page found for ' + hostname}`);
189
+ logger.debug(`browser_type_text: ${pageError || 'No page found for ' + hostname}`);
190
190
  return new InformationalResponse(
191
191
  isConnectionLost ? `Page connection lost for ${hostname}` : `No open page found for ${hostname}`,
192
192
  isConnectionLost
193
193
  ? 'The browser tab was closed or the connection was lost. The page needs to be reloaded.'
194
194
  : 'The page must be loaded before you can type text into elements',
195
195
  [
196
- "Use MCPBrowser's fetch_webpage tool to load the page first",
197
- "Then retry MCPBrowser's type_text with the same URL"
196
+ "Use MCPBrowser's browser_fetch_webpage tool to load the page first",
197
+ "Then retry MCPBrowser's browser_type_text with the same URL"
198
198
  ]
199
199
  );
200
200
  }
@@ -239,20 +239,20 @@ export async function typeText({ url, fields, returnHtml = true, removeUnnecessa
239
239
  const html = returnHtml ? await extractAndProcessHtml(page, removeUnnecessaryHTML) : null;
240
240
  const nextSteps = returnHtml
241
241
  ? [
242
- "Use MCPBrowser's type_text to fill additional fields",
243
- "Use MCPBrowser's click_element to submit the form or navigate",
244
- "Use MCPBrowser's get_current_html to check for validation messages",
245
- "Use MCPBrowser's take_screenshot if form has visual feedback or validation that's hard to parse from HTML",
246
- "Use MCPBrowser's close_tab when finished"
242
+ "Use MCPBrowser's browser_type_text to fill additional fields",
243
+ "Use MCPBrowser's browser_click_element to submit the form or navigate",
244
+ "Use MCPBrowser's browser_get_current_html to check for validation messages",
245
+ "Use MCPBrowser's browser_take_screenshot if form has visual feedback or validation that's hard to parse from HTML",
246
+ "Use MCPBrowser's browser_close_tab when finished"
247
247
  ]
248
248
  : [
249
- "Use MCPBrowser's get_current_html to see updated page state",
250
- "Use MCPBrowser's take_screenshot if the page has visual feedback that's hard to parse",
251
- "Use MCPBrowser's type_text for additional fields or MCPBrowser's click_element to submit",
252
- "Use MCPBrowser's close_tab when finished"
249
+ "Use MCPBrowser's browser_get_current_html to see updated page state",
250
+ "Use MCPBrowser's browser_take_screenshot if the page has visual feedback that's hard to parse",
251
+ "Use MCPBrowser's browser_type_text for additional fields or MCPBrowser's browser_click_element to submit",
252
+ "Use MCPBrowser's browser_close_tab when finished"
253
253
  ];
254
254
 
255
- logger.info(`type_text completed: typed into ${fieldsSummary}${returnHtml ? '' : ' (no HTML)'}`);
255
+ logger.info(`browser_type_text completed: typed into ${fieldsSummary}${returnHtml ? '' : ' (no HTML)'}`);
256
256
 
257
257
  return new TypeTextSuccessResponse(currentUrl, `Typed text into: ${fieldsSummary}`, html, nextSteps);
258
258
  } catch (err) {
@@ -272,29 +272,29 @@ export async function typeText({ url, fields, returnHtml = true, removeUnnecessa
272
272
  if (isNotFound) {
273
273
  reason = `Selector not found: "${currentSelector}". The element may not exist on the page or have a different selector.`;
274
274
  nextSteps = [
275
- "Use MCPBrowser's get_current_html to find the correct selector",
276
- "Use MCPBrowser's take_screenshot to visually inspect the form",
275
+ "Use MCPBrowser's browser_get_current_html to find the correct selector",
276
+ "Use MCPBrowser's browser_take_screenshot to visually inspect the form",
277
277
  "Check for typos in the selector or try a simpler selector (e.g., 'input[type=\"text\"]')",
278
278
  "The element may load dynamically - try increasing waitForElementTimeout"
279
279
  ];
280
280
  } else if (isNotVisible) {
281
281
  reason = `Element "${currentSelector}" exists but is not visible. It may be hidden, collapsed, or off-screen.`;
282
282
  nextSteps = [
283
- "Use MCPBrowser's take_screenshot to see the page state",
284
- "Use MCPBrowser's click_element to expand/show the form section first",
285
- "Use MCPBrowser's scroll_page to bring the element into view"
283
+ "Use MCPBrowser's browser_take_screenshot to see the page state",
284
+ "Use MCPBrowser's browser_click_element to expand/show the form section first",
285
+ "Use MCPBrowser's browser_scroll_page to bring the element into view"
286
286
  ];
287
287
  } else if (isDetached) {
288
288
  reason = `Element "${currentSelector}" was removed from the page during interaction. The page may have reloaded or updated.`;
289
289
  nextSteps = [
290
- "Use MCPBrowser's get_current_html to check current page state",
291
- "Retry the type_text call - the page may have stabilized"
290
+ "Use MCPBrowser's browser_get_current_html to check current page state",
291
+ "Retry the browser_type_text call - the page may have stabilized"
292
292
  ];
293
293
  } else {
294
294
  reason = `Failed to interact with "${currentSelector}": ${errorMsg}`;
295
295
  nextSteps = [
296
- "Use MCPBrowser's get_current_html to verify page state",
297
- "Use MCPBrowser's take_screenshot to see what's on the page visually",
296
+ "Use MCPBrowser's browser_get_current_html to verify page state",
297
+ "Use MCPBrowser's browser_take_screenshot to see what's on the page visually",
298
298
  "The element may be disabled or read-only"
299
299
  ];
300
300
  }
@@ -307,7 +307,7 @@ export async function typeText({ url, fields, returnHtml = true, removeUnnecessa
307
307
  progressInfo = `Failed on field ${failedFieldNum} of ${totalFields}. Successfully filled ${filledSelectors.length} field(s): ${filledSelectors.join(', ')}. Do NOT re-type these fields.`;
308
308
  }
309
309
 
310
- logger.error(`type_text failed on field ${failedFieldNum}/${totalFields} (${currentSelector}): ${errorMsg}`);
310
+ logger.error(`browser_type_text failed on field ${failedFieldNum}/${totalFields} (${currentSelector}): ${errorMsg}`);
311
311
 
312
312
  return new InformationalResponse(
313
313
  `${progressInfo}`,
package/src/cli/help.js CHANGED
@@ -162,7 +162,7 @@ export function printHelp() {
162
162
  o('━'.repeat(70));
163
163
  o('');
164
164
  o(' No arguments → starts MCP server (stdin/stdout JSON-RPC).');
165
- o(' CLI commands map 1:1 to MCP tools (fetch→fetch_webpage, etc.).');
165
+ o(' CLI commands map 1:1 to MCP tools (fetch→browser_fetch_webpage, etc.).');
166
166
  o('');
167
167
  o(' { "mcpServers": { "mcpbrowser": { "command": "npx", "args": ["-y", "mcpbrowser@latest"] } } }');
168
168
  o('');
@@ -1,5 +1,7 @@
1
1
  /**
2
2
  * Logger - Emits to stderr and, when available, via MCP logging notifications.
3
+ * Also sends MCP progress notifications (notifications/progress) when a
4
+ * progressToken is active, so agents see real-time status during tool execution.
3
5
  * Stderr stays the primary sink to avoid interfering with MCP stdout traffic.
4
6
  */
5
7
 
@@ -8,6 +10,10 @@ const PREFIX = '[MCPBrowser]';
8
10
  // Optional MCP server reference for notifications/message logs.
9
11
  let mcpServer = null;
10
12
 
13
+ // MCP progress tracking — set per-request by the request handler.
14
+ let _progressToken = null;
15
+ let _progressStep = 0;
16
+
11
17
  // Optional stdout mirroring (off by default to avoid corrupting MCP stdout).
12
18
  // Auto-enable during tests so test runners capture output.
13
19
  let consoleOutputEnabled = process.env.NODE_ENV === 'test';
@@ -32,6 +38,25 @@ function setConsoleOutput(enabled = true) {
32
38
  consoleOutputEnabled = !!enabled;
33
39
  }
34
40
 
41
+ /**
42
+ * Set the MCP progress token for the current request.
43
+ * While set, every logger.info() call also sends a notifications/progress
44
+ * message so the agent sees real-time status during tool execution.
45
+ * @param {string|number|null|undefined} token - progressToken from request._meta
46
+ */
47
+ function setProgressToken(token) {
48
+ _progressToken = token ?? null;
49
+ _progressStep = 0;
50
+ }
51
+
52
+ /**
53
+ * Clear the progress token after the request completes.
54
+ */
55
+ function clearProgressToken() {
56
+ _progressToken = null;
57
+ _progressStep = 0;
58
+ }
59
+
35
60
  async function notifyAgent(level, data) {
36
61
  if (!mcpServer?.sendLoggingMessage) return;
37
62
  try {
@@ -44,6 +69,28 @@ async function notifyAgent(level, data) {
44
69
  }
45
70
  }
46
71
 
72
+ /**
73
+ * Send an MCP progress notification if a progressToken is active.
74
+ * Called automatically from info-level log messages.
75
+ * @param {string} message - Human-readable progress message
76
+ */
77
+ async function sendProgress(message) {
78
+ if (!_progressToken || !mcpServer) return;
79
+ _progressStep++;
80
+ try {
81
+ await mcpServer.notification({
82
+ method: 'notifications/progress',
83
+ params: {
84
+ progressToken: _progressToken,
85
+ progress: _progressStep,
86
+ message
87
+ }
88
+ });
89
+ } catch {
90
+ // Fire and forget — don't break the action if progress fails
91
+ }
92
+ }
93
+
47
94
  function emit(level, message, symbol = '') {
48
95
  const line = symbol ? `${PREFIX} ${symbol} ${message}` : `${PREFIX} ${message}`;
49
96
  console.error(line);
@@ -55,6 +102,8 @@ function emit(level, message, symbol = '') {
55
102
 
56
103
  function info(message) {
57
104
  emit('info', message);
105
+ // Also send as MCP progress notification when token is active
106
+ void sendProgress(message);
58
107
  }
59
108
 
60
109
  function warn(message) {
@@ -69,6 +118,6 @@ function debug(message) {
69
118
  emit('debug', message, '🔍');
70
119
  }
71
120
 
72
- export const logger = { info, warn, error, debug, attachServer, setConsoleOutput };
73
- export { attachServer, setConsoleOutput };
121
+ export const logger = { info, warn, error, debug, attachServer, setConsoleOutput, setProgressToken, clearProgressToken };
122
+ export { attachServer, setConsoleOutput, setProgressToken, clearProgressToken };
74
123
  export default logger;
@@ -302,7 +302,7 @@ export function getPluginNextSteps(url, html) {
302
302
  /**
303
303
  * Build the recommendedPlugins payload for structuredContent.
304
304
  * Returns full plugin metadata including action catalog with params,
305
- * so agents can call actions directly without needing plugin_info.
305
+ * so agents can call actions directly without needing browser_plugin_info.
306
306
  * @param {string} url - Current page URL
307
307
  * @param {string} html - Extracted page HTML
308
308
  * @returns {Array<{ plugin: string, recommendation: string, actions: Array, usage: string }>}
@@ -317,7 +317,7 @@ export function getRecommendedPlugins(url, html) {
317
317
  plugin: d.pluginName,
318
318
  recommendation: recommendationText,
319
319
  actions: info.actions || [],
320
- usage: `plugin_action({ plugin: '${d.pluginName}', action: '<name>', params: {...} })`
320
+ usage: `browser_plugin_action({ plugin: '${d.pluginName}', action: '<name>', params: {...} })`
321
321
  };
322
322
  });
323
323
  }
@@ -188,7 +188,7 @@ function getHttpStatusNextSteps(statusCode, url) {
188
188
  return [
189
189
  'Authentication may be required - try logging in first',
190
190
  'Check if you have permission to access this resource',
191
- "Use MCPBrowser's fetch_webpage to navigate to the login page first"
191
+ "Use MCPBrowser's browser_fetch_webpage to navigate to the login page first"
192
192
  ];
193
193
  }
194
194
 
@@ -204,14 +204,14 @@ function getHttpStatusNextSteps(statusCode, url) {
204
204
  return [
205
205
  'Rate limit exceeded - wait a few minutes before retrying',
206
206
  'Reduce request frequency',
207
- "Call MCPBrowser's fetch_webpage again after waiting"
207
+ "Call MCPBrowser's browser_fetch_webpage again after waiting"
208
208
  ];
209
209
  }
210
210
 
211
211
  if (statusCode >= 500 && statusCode < 600) {
212
212
  return [
213
213
  'The server is experiencing issues',
214
- "Wait a moment and try again with MCPBrowser's fetch_webpage",
214
+ "Wait a moment and try again with MCPBrowser's browser_fetch_webpage",
215
215
  'Check if the service has a status page for outages'
216
216
  ];
217
217
  }
@@ -219,7 +219,7 @@ function getHttpStatusNextSteps(statusCode, url) {
219
219
  return [
220
220
  ...baseSteps,
221
221
  'Try again later if this is a temporary issue',
222
- "Call MCPBrowser's fetch_webpage to retry the request"
222
+ "Call MCPBrowser's browser_fetch_webpage to retry the request"
223
223
  ];
224
224
  }
225
225
 
@@ -86,10 +86,16 @@ async function main() {
86
86
 
87
87
  server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));
88
88
 
89
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
89
+ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
90
90
  const { name, arguments: args } = request.params;
91
91
  const safeArgs = args || {};
92
92
 
93
+ // Enable MCP progress notifications for this request if the client sent a progressToken.
94
+ // Every logger.info() call during tool execution will automatically send a
95
+ // notifications/progress message so the agent sees real-time status updates.
96
+ const progressToken = extra?._meta?.progressToken;
97
+ logger.setProgressToken(progressToken);
98
+
93
99
  let result;
94
100
 
95
101
  try {
@@ -104,47 +110,47 @@ async function main() {
104
110
  // result = await handleAcceptEula(safeArgs);
105
111
  // break;
106
112
 
107
- case "fetch_webpage":
113
+ case "browser_fetch_webpage":
108
114
  result = await fetchPage(safeArgs);
109
115
  break;
110
116
 
111
- case "execute_javascript":
117
+ case "browser_execute_javascript":
112
118
  result = await executeJavascript(safeArgs);
113
119
  break;
114
120
 
115
- case "click_element":
121
+ case "browser_click_element":
116
122
  result = await clickElement(safeArgs);
117
123
  break;
118
124
 
119
- case "type_text":
125
+ case "browser_type_text":
120
126
  result = await typeText(safeArgs);
121
127
  break;
122
128
 
123
- case "close_tab":
129
+ case "browser_close_tab":
124
130
  result = await closeTab(safeArgs);
125
131
  break;
126
132
 
127
- case "get_current_html":
133
+ case "browser_get_current_html":
128
134
  result = await getCurrentHtml(safeArgs);
129
135
  break;
130
136
 
131
- case "take_screenshot":
137
+ case "browser_take_screenshot":
132
138
  result = await takeScreenshot(safeArgs);
133
139
  break;
134
140
 
135
- case "scroll_page":
141
+ case "browser_scroll_page":
136
142
  result = await scrollPage(safeArgs);
137
143
  break;
138
144
 
139
- case "navigate_history":
145
+ case "browser_navigate_history":
140
146
  result = await navigateHistory(safeArgs);
141
147
  break;
142
148
 
143
- case "plugin_info":
149
+ case "browser_plugin_info":
144
150
  result = pluginInfo(safeArgs);
145
151
  break;
146
152
 
147
- case "plugin_action":
153
+ case "browser_plugin_action":
148
154
  result = await pluginAction(safeArgs);
149
155
  break;
150
156
 
@@ -161,6 +167,8 @@ async function main() {
161
167
  `${name} failed: ${error.message}`,
162
168
  ['Check browser is installed', 'Try specifying browser parameter explicitly (chrome, edge, or brave)', 'Check MCP server logs for details']
163
169
  ).toMcpFormat();
170
+ } finally {
171
+ logger.clearProgressToken();
164
172
  }
165
173
 
166
174
  // Transform result into MCP-compliant response using instance method
@@ -81,7 +81,7 @@ export function getActions() {
81
81
  }, limit);
82
82
 
83
83
  return new ExampleActionResponse(items, [
84
- "Call plugin_action with action 'get_item_detail' to read a specific item"
84
+ "Call browser_plugin_action with action 'get_item_detail' to read a specific item"
85
85
  ]);
86
86
  }
87
87
  },
@@ -114,7 +114,7 @@ export function getActions() {
114
114
  }
115
115
 
116
116
  return new ExampleActionResponse(detail, [
117
- "Use plugin_action with action 'list_items' to see all items"
117
+ "Use browser_plugin_action with action 'list_items' to see all items"
118
118
  ]);
119
119
  }
120
120
  }
@@ -85,7 +85,7 @@ export async function checkAvailability({ page, params }) {
85
85
  const pre = await checkPrecondition(page, 'on_calendar');
86
86
  if (!pre.met) {
87
87
  return new ErrorResponse(pre.error, [
88
- pre.suggestion || "Use fetch_webpage({ url: 'https://calendar.google.com' }) to open Google Calendar first."
88
+ pre.suggestion || "Use browser_fetch_webpage({ url: 'https://calendar.google.com' }) to open Google Calendar first."
89
89
  ]);
90
90
  }
91
91
 
@@ -47,7 +47,7 @@ export async function createEvent({ page, params }) {
47
47
  const pre = await checkPrecondition(page, 'on_calendar');
48
48
  if (!pre.met) {
49
49
  return new ErrorResponse(pre.error, [
50
- pre.suggestion || "Use fetch_webpage({ url: 'https://calendar.google.com' }) to open Google Calendar first."
50
+ pre.suggestion || "Use browser_fetch_webpage({ url: 'https://calendar.google.com' }) to open Google Calendar first."
51
51
  ]);
52
52
  }
53
53
 
@@ -44,7 +44,7 @@ export async function deleteEvent({ page, params }) {
44
44
  const pre = await checkPrecondition(page, 'on_calendar');
45
45
  if (!pre.met) {
46
46
  return new ErrorResponse(pre.error, [
47
- pre.suggestion || "Use fetch_webpage({ url: 'https://calendar.google.com' }) to open Google Calendar first."
47
+ pre.suggestion || "Use browser_fetch_webpage({ url: 'https://calendar.google.com' }) to open Google Calendar first."
48
48
  ]);
49
49
  }
50
50
 
@@ -51,7 +51,7 @@ export async function editEvent({ page, params }) {
51
51
  const pre = await checkPrecondition(page, 'on_calendar');
52
52
  if (!pre.met) {
53
53
  return new ErrorResponse(pre.error, [
54
- pre.suggestion || "Use fetch_webpage({ url: 'https://calendar.google.com' }) to open Google Calendar first."
54
+ pre.suggestion || "Use browser_fetch_webpage({ url: 'https://calendar.google.com' }) to open Google Calendar first."
55
55
  ]);
56
56
  }
57
57
 
@@ -34,7 +34,7 @@ export async function listEvents({ page, params }) {
34
34
  const pre = await checkPrecondition(page, 'on_calendar');
35
35
  if (!pre.met) {
36
36
  return new ErrorResponse(pre.error, [
37
- pre.suggestion || "Use fetch_webpage({ url: 'https://calendar.google.com' }) to open Google Calendar first."
37
+ pre.suggestion || "Use browser_fetch_webpage({ url: 'https://calendar.google.com' }) to open Google Calendar first."
38
38
  ]);
39
39
  }
40
40
 
@@ -47,7 +47,7 @@ export async function readEvent({ page, params }) {
47
47
  const pre = await checkPrecondition(page, 'on_calendar');
48
48
  if (!pre.met) {
49
49
  return new ErrorResponse(pre.error, [
50
- pre.suggestion || "Use fetch_webpage({ url: 'https://calendar.google.com' }) to open Google Calendar first."
50
+ pre.suggestion || "Use browser_fetch_webpage({ url: 'https://calendar.google.com' }) to open Google Calendar first."
51
51
  ]);
52
52
  }
53
53
 
@@ -62,7 +62,7 @@ export async function rsvpEvent({ page, params }) {
62
62
  const pre = await checkPrecondition(page, 'on_calendar');
63
63
  if (!pre.met) {
64
64
  return new ErrorResponse(pre.error, [
65
- pre.suggestion || "Use fetch_webpage({ url: 'https://calendar.google.com' }) to open Google Calendar first."
65
+ pre.suggestion || "Use browser_fetch_webpage({ url: 'https://calendar.google.com' }) to open Google Calendar first."
66
66
  ]);
67
67
  }
68
68
 
@@ -42,7 +42,7 @@ export async function searchEvents({ page, params }) {
42
42
  const pre = await checkPrecondition(page, 'on_calendar');
43
43
  if (!pre.met) {
44
44
  return new ErrorResponse(pre.error, [
45
- pre.suggestion || "Use fetch_webpage({ url: 'https://calendar.google.com' }) to open Google Calendar first."
45
+ pre.suggestion || "Use browser_fetch_webpage({ url: 'https://calendar.google.com' }) to open Google Calendar first."
46
46
  ]);
47
47
  }
48
48
 
@@ -235,7 +235,7 @@ export async function checkPrecondition(page, requirement) {
235
235
  return {
236
236
  met: false,
237
237
  error: 'Google Calendar is not the active page.',
238
- suggestion: "Use fetch_webpage({ url: 'https://calendar.google.com' }) to navigate to Google Calendar first."
238
+ suggestion: "Use browser_fetch_webpage({ url: 'https://calendar.google.com' }) to navigate to Google Calendar first."
239
239
  };
240
240
  }
241
241
  return { met: true };
@@ -247,7 +247,7 @@ export async function checkPrecondition(page, requirement) {
247
247
  return {
248
248
  met: false,
249
249
  error: 'Google Calendar is not ready.',
250
- suggestion: "Use fetch_webpage({ url: 'https://calendar.google.com' }) to navigate to Google Calendar."
250
+ suggestion: "Use browser_fetch_webpage({ url: 'https://calendar.google.com' }) to navigate to Google Calendar."
251
251
  };
252
252
  }
253
253
  return { met: true };
@@ -30,7 +30,7 @@ export async function archiveEmail({ page, params }) {
30
30
  const pre = await checkPrecondition(page, 'on_gmail');
31
31
  if (!pre.met) {
32
32
  return new ErrorResponse(pre.error, [
33
- pre.suggestion || "Use fetch_webpage({ url: 'https://mail.google.com' }) to open Gmail first."
33
+ pre.suggestion || "Use browser_fetch_webpage({ url: 'https://mail.google.com' }) to open Gmail first."
34
34
  ]);
35
35
  }
36
36
 
@@ -40,7 +40,7 @@ export async function composeEmail({ page, params }) {
40
40
  const pre = await checkPrecondition(page, 'on_gmail');
41
41
  if (!pre.met) {
42
42
  return new ErrorResponse(pre.error, [
43
- pre.suggestion || "Use fetch_webpage({ url: 'https://mail.google.com' }) to open Gmail first."
43
+ pre.suggestion || "Use browser_fetch_webpage({ url: 'https://mail.google.com' }) to open Gmail first."
44
44
  ]);
45
45
  }
46
46
 
@@ -30,7 +30,7 @@ export async function deleteEmail({ page, params }) {
30
30
  const pre = await checkPrecondition(page, 'on_gmail');
31
31
  if (!pre.met) {
32
32
  return new ErrorResponse(pre.error, [
33
- pre.suggestion || "Use fetch_webpage({ url: 'https://mail.google.com' }) to open Gmail first."
33
+ pre.suggestion || "Use browser_fetch_webpage({ url: 'https://mail.google.com' }) to open Gmail first."
34
34
  ]);
35
35
  }
36
36
 
@@ -30,7 +30,7 @@ export async function forwardEmail({ page, params }) {
30
30
  const pre = await checkPrecondition(page, 'on_gmail');
31
31
  if (!pre.met) {
32
32
  return new ErrorResponse(pre.error, [
33
- pre.suggestion || "Use fetch_webpage({ url: 'https://mail.google.com' }) to open Gmail first."
33
+ pre.suggestion || "Use browser_fetch_webpage({ url: 'https://mail.google.com' }) to open Gmail first."
34
34
  ]);
35
35
  }
36
36
 
@@ -43,7 +43,7 @@ export async function labelEmail({ page, params }) {
43
43
  const pre = await checkPrecondition(page, 'on_gmail');
44
44
  if (!pre.met) {
45
45
  return new ErrorResponse(pre.error, [
46
- pre.suggestion || "Use fetch_webpage({ url: 'https://mail.google.com' }) to open Gmail first."
46
+ pre.suggestion || "Use browser_fetch_webpage({ url: 'https://mail.google.com' }) to open Gmail first."
47
47
  ]);
48
48
  }
49
49
 
@@ -31,7 +31,7 @@ export async function listEmails({ page, params }) {
31
31
  const pre = await checkPrecondition(page, 'on_gmail');
32
32
  if (!pre.met) {
33
33
  return new ErrorResponse(pre.error, [
34
- pre.suggestion || "Use fetch_webpage({ url: 'https://mail.google.com' }) to open Gmail first."
34
+ pre.suggestion || "Use browser_fetch_webpage({ url: 'https://mail.google.com' }) to open Gmail first."
35
35
  ]);
36
36
  }
37
37
 
@@ -29,7 +29,7 @@ export async function markRead({ page, params }) {
29
29
  const pre = await checkPrecondition(page, 'on_gmail');
30
30
  if (!pre.met) {
31
31
  return new ErrorResponse(pre.error, [
32
- pre.suggestion || "Use fetch_webpage({ url: 'https://mail.google.com' }) to open Gmail first."
32
+ pre.suggestion || "Use browser_fetch_webpage({ url: 'https://mail.google.com' }) to open Gmail first."
33
33
  ]);
34
34
  }
35
35
 
@@ -29,7 +29,7 @@ export async function markUnread({ page, params }) {
29
29
  const pre = await checkPrecondition(page, 'on_gmail');
30
30
  if (!pre.met) {
31
31
  return new ErrorResponse(pre.error, [
32
- pre.suggestion || "Use fetch_webpage({ url: 'https://mail.google.com' }) to open Gmail first."
32
+ pre.suggestion || "Use browser_fetch_webpage({ url: 'https://mail.google.com' }) to open Gmail first."
33
33
  ]);
34
34
  }
35
35
 
@@ -51,7 +51,7 @@ export async function readEmail({ page, params }) {
51
51
  const pre = await checkPrecondition(page, 'on_gmail');
52
52
  if (!pre.met) {
53
53
  return new ErrorResponse(pre.error, [
54
- pre.suggestion || "Use fetch_webpage({ url: 'https://mail.google.com' }) to open Gmail first."
54
+ pre.suggestion || "Use browser_fetch_webpage({ url: 'https://mail.google.com' }) to open Gmail first."
55
55
  ]);
56
56
  }
57
57
 
@@ -30,7 +30,7 @@ export async function replyEmail({ page, params }) {
30
30
  const pre = await checkPrecondition(page, 'on_gmail');
31
31
  if (!pre.met) {
32
32
  return new ErrorResponse(pre.error, [
33
- pre.suggestion || "Use fetch_webpage({ url: 'https://mail.google.com' }) to open Gmail first."
33
+ pre.suggestion || "Use browser_fetch_webpage({ url: 'https://mail.google.com' }) to open Gmail first."
34
34
  ]);
35
35
  }
36
36
 
@@ -41,7 +41,7 @@ export async function searchEmails({ page, params }) {
41
41
  const pre = await checkPrecondition(page, 'on_gmail');
42
42
  if (!pre.met) {
43
43
  return new ErrorResponse(pre.error, [
44
- pre.suggestion || "Use fetch_webpage({ url: 'https://mail.google.com' }) to open Gmail first."
44
+ pre.suggestion || "Use browser_fetch_webpage({ url: 'https://mail.google.com' }) to open Gmail first."
45
45
  ]);
46
46
  }
47
47
 
@@ -230,7 +230,7 @@ export async function checkPrecondition(page, requirement) {
230
230
  return {
231
231
  met: false,
232
232
  error: 'Gmail is not the active page.',
233
- suggestion: "Use fetch_webpage({ url: 'https://mail.google.com' }) to navigate to Gmail first."
233
+ suggestion: "Use browser_fetch_webpage({ url: 'https://mail.google.com' }) to navigate to Gmail first."
234
234
  };
235
235
  }
236
236
  return { met: true };
@@ -243,7 +243,7 @@ export async function checkPrecondition(page, requirement) {
243
243
  return {
244
244
  met: false,
245
245
  error: 'No email thread is currently open.',
246
- suggestion: "Use plugin_action({ plugin: 'gmail', action: 'read_email', params: { index: 0 } }) to open an email first."
246
+ suggestion: "Use browser_plugin_action({ plugin: 'gmail', action: 'read_email', params: { index: 0 } }) to open an email first."
247
247
  };
248
248
  }
249
249
  return { met: true };
@@ -255,7 +255,7 @@ export async function checkPrecondition(page, requirement) {
255
255
  return {
256
256
  met: false,
257
257
  error: 'Not in email list view.',
258
- suggestion: "Use plugin_action({ plugin: 'gmail', action: 'list_emails' }) to return to the email list."
258
+ suggestion: "Use browser_plugin_action({ plugin: 'gmail', action: 'list_emails' }) to return to the email list."
259
259
  };
260
260
  }
261
261
  return { met: true };