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.
- package/README.md +34 -34
- package/package.json +1 -1
- package/src/actions/accept-eula.js +5 -5
- package/src/actions/click-element.js +25 -25
- package/src/actions/close-tab.js +8 -8
- package/src/actions/execute-javascript.js +9 -9
- package/src/actions/fetch-page.js +15 -15
- package/src/actions/get-current-html.js +16 -16
- package/src/actions/navigate-history.js +21 -21
- package/src/actions/plugin-action.js +15 -15
- package/src/actions/plugin-info.js +10 -10
- package/src/actions/scroll-page.js +21 -21
- package/src/actions/take-screenshot.js +16 -16
- package/src/actions/type-text.js +28 -28
- package/src/cli/help.js +1 -1
- package/src/core/logger.js +51 -2
- package/src/core/plugin-loader.js +2 -2
- package/src/core/responses.js +4 -4
- package/src/mcp-browser.js +20 -12
- package/src/plugins/_example/index.js +2 -2
- package/src/plugins/gcal/actions/check-availability.js +1 -1
- package/src/plugins/gcal/actions/create-event.js +1 -1
- package/src/plugins/gcal/actions/delete-event.js +1 -1
- package/src/plugins/gcal/actions/edit-event.js +1 -1
- package/src/plugins/gcal/actions/list-events.js +1 -1
- package/src/plugins/gcal/actions/read-event.js +1 -1
- package/src/plugins/gcal/actions/rsvp-event.js +1 -1
- package/src/plugins/gcal/actions/search-events.js +1 -1
- package/src/plugins/gcal/helpers.js +2 -2
- package/src/plugins/gmail/actions/archive-email.js +1 -1
- package/src/plugins/gmail/actions/compose-email.js +1 -1
- package/src/plugins/gmail/actions/delete-email.js +1 -1
- package/src/plugins/gmail/actions/forward-email.js +1 -1
- package/src/plugins/gmail/actions/label-email.js +1 -1
- package/src/plugins/gmail/actions/list-emails.js +1 -1
- package/src/plugins/gmail/actions/mark-read.js +1 -1
- package/src/plugins/gmail/actions/mark-unread.js +1 -1
- package/src/plugins/gmail/actions/read-email.js +1 -1
- package/src/plugins/gmail/actions/reply-email.js +1 -1
- package/src/plugins/gmail/actions/search-emails.js +1 -1
- package/src/plugins/gmail/helpers.js +3 -3
package/src/actions/type-text.js
CHANGED
|
@@ -16,7 +16,7 @@ import logger from '../core/logger.js';
|
|
|
16
16
|
// ============================================================================
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
* Response for successful
|
|
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: "
|
|
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
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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
|
|
197
|
-
"Then retry MCPBrowser's
|
|
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
|
|
243
|
-
"Use MCPBrowser's
|
|
244
|
-
"Use MCPBrowser's
|
|
245
|
-
"Use MCPBrowser's
|
|
246
|
-
"Use MCPBrowser's
|
|
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
|
|
250
|
-
"Use MCPBrowser's
|
|
251
|
-
"Use MCPBrowser's
|
|
252
|
-
"Use MCPBrowser's
|
|
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(`
|
|
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
|
|
276
|
-
"Use MCPBrowser's
|
|
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
|
|
284
|
-
"Use MCPBrowser's
|
|
285
|
-
"Use MCPBrowser's
|
|
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
|
|
291
|
-
"Retry the
|
|
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
|
|
297
|
-
"Use MCPBrowser's
|
|
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(`
|
|
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→
|
|
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('');
|
package/src/core/logger.js
CHANGED
|
@@ -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
|
|
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: `
|
|
320
|
+
usage: `browser_plugin_action({ plugin: '${d.pluginName}', action: '<name>', params: {...} })`
|
|
321
321
|
};
|
|
322
322
|
});
|
|
323
323
|
}
|
package/src/core/responses.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
222
|
+
"Call MCPBrowser's browser_fetch_webpage to retry the request"
|
|
223
223
|
];
|
|
224
224
|
}
|
|
225
225
|
|
package/src/mcp-browser.js
CHANGED
|
@@ -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 "
|
|
113
|
+
case "browser_fetch_webpage":
|
|
108
114
|
result = await fetchPage(safeArgs);
|
|
109
115
|
break;
|
|
110
116
|
|
|
111
|
-
case "
|
|
117
|
+
case "browser_execute_javascript":
|
|
112
118
|
result = await executeJavascript(safeArgs);
|
|
113
119
|
break;
|
|
114
120
|
|
|
115
|
-
case "
|
|
121
|
+
case "browser_click_element":
|
|
116
122
|
result = await clickElement(safeArgs);
|
|
117
123
|
break;
|
|
118
124
|
|
|
119
|
-
case "
|
|
125
|
+
case "browser_type_text":
|
|
120
126
|
result = await typeText(safeArgs);
|
|
121
127
|
break;
|
|
122
128
|
|
|
123
|
-
case "
|
|
129
|
+
case "browser_close_tab":
|
|
124
130
|
result = await closeTab(safeArgs);
|
|
125
131
|
break;
|
|
126
132
|
|
|
127
|
-
case "
|
|
133
|
+
case "browser_get_current_html":
|
|
128
134
|
result = await getCurrentHtml(safeArgs);
|
|
129
135
|
break;
|
|
130
136
|
|
|
131
|
-
case "
|
|
137
|
+
case "browser_take_screenshot":
|
|
132
138
|
result = await takeScreenshot(safeArgs);
|
|
133
139
|
break;
|
|
134
140
|
|
|
135
|
-
case "
|
|
141
|
+
case "browser_scroll_page":
|
|
136
142
|
result = await scrollPage(safeArgs);
|
|
137
143
|
break;
|
|
138
144
|
|
|
139
|
-
case "
|
|
145
|
+
case "browser_navigate_history":
|
|
140
146
|
result = await navigateHistory(safeArgs);
|
|
141
147
|
break;
|
|
142
148
|
|
|
143
|
-
case "
|
|
149
|
+
case "browser_plugin_info":
|
|
144
150
|
result = pluginInfo(safeArgs);
|
|
145
151
|
break;
|
|
146
152
|
|
|
147
|
-
case "
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 };
|