mcpbrowser 0.3.43 → 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
|
@@ -17,7 +17,7 @@ import { getPluginNextSteps, getRecommendedPlugins } from '../core/plugin-loader
|
|
|
17
17
|
// ============================================================================
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
* Response for successful
|
|
20
|
+
* Response for successful browser_get_current_html operations
|
|
21
21
|
*/
|
|
22
22
|
export class GetCurrentHtmlSuccessResponse extends MCPResponse {
|
|
23
23
|
/**
|
|
@@ -62,9 +62,9 @@ export class GetCurrentHtmlSuccessResponse extends MCPResponse {
|
|
|
62
62
|
* @type {Tool}
|
|
63
63
|
*/
|
|
64
64
|
export const GET_CURRENT_HTML_TOOL = {
|
|
65
|
-
name: "
|
|
65
|
+
name: "browser_get_current_html",
|
|
66
66
|
title: "Get Current HTML",
|
|
67
|
-
description: "**BROWSER STATE EXTRACTION** - Retrieves current HTML from an already-loaded page WITHOUT navigating/reloading. Use this to check page state after interactions (click, type) or to re-examine the current page. Much faster than
|
|
67
|
+
description: "**BROWSER STATE EXTRACTION** - Retrieves current HTML from an already-loaded page WITHOUT navigating/reloading. Use this to check page state after interactions (click, type) or to re-examine the current page. Much faster than browser_fetch_webpage since it only extracts HTML from the current page state.\n\n**PREREQUISITE**: Page MUST be loaded with browser_fetch_webpage first. This tool reads from an already-loaded page in the browser.",
|
|
68
68
|
inputSchema: {
|
|
69
69
|
type: "object",
|
|
70
70
|
properties: {
|
|
@@ -109,7 +109,7 @@ export const GET_CURRENT_HTML_TOOL = {
|
|
|
109
109
|
*/
|
|
110
110
|
export async function getCurrentHtml({ url, removeUnnecessaryHTML = true }) {
|
|
111
111
|
const startTime = Date.now();
|
|
112
|
-
logger.info(`
|
|
112
|
+
logger.info(`browser_get_current_html called: url=${url}`);
|
|
113
113
|
|
|
114
114
|
if (!url) {
|
|
115
115
|
throw new Error("url parameter is required");
|
|
@@ -126,7 +126,7 @@ export async function getCurrentHtml({ url, removeUnnecessaryHTML = true }) {
|
|
|
126
126
|
try {
|
|
127
127
|
await getBrowser();
|
|
128
128
|
} catch (err) {
|
|
129
|
-
logger.error(`
|
|
129
|
+
logger.error(`browser_get_current_html: Failed to connect to browser: ${err.message}`);
|
|
130
130
|
return new InformationalResponse(
|
|
131
131
|
`Browser connection failed: ${err.message}`,
|
|
132
132
|
'The browser must be running with remote debugging enabled.',
|
|
@@ -143,15 +143,15 @@ export async function getCurrentHtml({ url, removeUnnecessaryHTML = true }) {
|
|
|
143
143
|
|
|
144
144
|
if (!page) {
|
|
145
145
|
const isConnectionLost = pageError && pageError.includes('connection');
|
|
146
|
-
logger.debug(`
|
|
146
|
+
logger.debug(`browser_get_current_html: ${pageError || 'No page found for ' + hostname}`);
|
|
147
147
|
return new InformationalResponse(
|
|
148
148
|
isConnectionLost ? `Page connection lost for ${hostname}` : `No open page found for ${hostname}`,
|
|
149
149
|
isConnectionLost
|
|
150
150
|
? 'The browser tab was closed or the connection was lost. The page needs to be reloaded.'
|
|
151
151
|
: 'The page must be loaded before you can get its current HTML',
|
|
152
152
|
[
|
|
153
|
-
"Use MCPBrowser's
|
|
154
|
-
"Then retry MCPBrowser's
|
|
153
|
+
"Use MCPBrowser's browser_fetch_webpage tool to load the page first",
|
|
154
|
+
"Then retry MCPBrowser's browser_get_current_html with the same URL"
|
|
155
155
|
]
|
|
156
156
|
);
|
|
157
157
|
}
|
|
@@ -160,28 +160,28 @@ export async function getCurrentHtml({ url, removeUnnecessaryHTML = true }) {
|
|
|
160
160
|
const currentUrl = page.url();
|
|
161
161
|
const html = await extractAndProcessHtml(page, removeUnnecessaryHTML);
|
|
162
162
|
|
|
163
|
-
logger.info(`
|
|
163
|
+
logger.info(`browser_get_current_html completed: got HTML from ${currentUrl}`);
|
|
164
164
|
|
|
165
165
|
return new GetCurrentHtmlSuccessResponse(
|
|
166
166
|
currentUrl,
|
|
167
167
|
html,
|
|
168
168
|
[
|
|
169
169
|
...getPluginNextSteps(currentUrl, html),
|
|
170
|
-
"Use MCPBrowser's
|
|
171
|
-
"Use MCPBrowser's
|
|
172
|
-
"Use MCPBrowser's
|
|
173
|
-
"Use MCPBrowser's
|
|
170
|
+
"Use MCPBrowser's browser_click_element to interact with elements",
|
|
171
|
+
"Use MCPBrowser's browser_type_text to fill forms",
|
|
172
|
+
"Use MCPBrowser's browser_take_screenshot if page layout or visual content is hard to understand from HTML",
|
|
173
|
+
"Use MCPBrowser's browser_close_tab to free resources when done"
|
|
174
174
|
],
|
|
175
175
|
getRecommendedPlugins(currentUrl, html)
|
|
176
176
|
);
|
|
177
177
|
} catch (err) {
|
|
178
|
-
logger.error(`
|
|
178
|
+
logger.error(`browser_get_current_html failed: ${err.message}`);
|
|
179
179
|
return new InformationalResponse(
|
|
180
180
|
`Failed to get HTML: ${err.message}`,
|
|
181
181
|
'Could not extract HTML from the page. The page may have navigated away or the connection was lost.',
|
|
182
182
|
[
|
|
183
|
-
"Try MCPBrowser's
|
|
184
|
-
"Use MCPBrowser's
|
|
183
|
+
"Try MCPBrowser's browser_fetch_webpage to reload the page",
|
|
184
|
+
"Use MCPBrowser's browser_close_tab and start fresh if needed"
|
|
185
185
|
]
|
|
186
186
|
);
|
|
187
187
|
}
|
|
@@ -17,7 +17,7 @@ import logger from '../core/logger.js';
|
|
|
17
17
|
// ============================================================================
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
* Response for successful
|
|
20
|
+
* Response for successful browser_navigate_history operations
|
|
21
21
|
*/
|
|
22
22
|
export class NavigateHistorySuccessResponse extends MCPResponse {
|
|
23
23
|
/**
|
|
@@ -69,9 +69,9 @@ export class NavigateHistorySuccessResponse extends MCPResponse {
|
|
|
69
69
|
|
|
70
70
|
/** @type {Tool} */
|
|
71
71
|
export const NAVIGATE_HISTORY_TOOL = {
|
|
72
|
-
name: "
|
|
72
|
+
name: "browser_navigate_history",
|
|
73
73
|
title: "Navigate Back/Forward",
|
|
74
|
-
description: "**BROWSER HISTORY NAVIGATION** - Navigate back or forward in browser history on an already-loaded page. Use after clicking links to return to the previous page, or to go forward after going back.\n\n**PREREQUISITE**: Page MUST be loaded with
|
|
74
|
+
description: "**BROWSER HISTORY NAVIGATION** - Navigate back or forward in browser history on an already-loaded page. Use after clicking links to return to the previous page, or to go forward after going back.\n\n**PREREQUISITE**: Page MUST be loaded with browser_fetch_webpage first. This tool navigates the history of an existing browser tab.",
|
|
75
75
|
inputSchema: {
|
|
76
76
|
type: "object",
|
|
77
77
|
properties: {
|
|
@@ -126,7 +126,7 @@ export const NAVIGATE_HISTORY_TOOL = {
|
|
|
126
126
|
* @returns {Promise<MCPResponse>} Navigation result
|
|
127
127
|
*/
|
|
128
128
|
export async function navigateHistory({ url, direction = 'back', returnHtml = true, removeUnnecessaryHTML = true }) {
|
|
129
|
-
logger.info(`
|
|
129
|
+
logger.info(`browser_navigate_history called: url=${url}, direction=${direction}`);
|
|
130
130
|
|
|
131
131
|
if (!url) {
|
|
132
132
|
throw new Error("url parameter is required");
|
|
@@ -143,7 +143,7 @@ export async function navigateHistory({ url, direction = 'back', returnHtml = tr
|
|
|
143
143
|
try {
|
|
144
144
|
await getBrowser();
|
|
145
145
|
} catch (err) {
|
|
146
|
-
logger.error(`
|
|
146
|
+
logger.error(`browser_navigate_history: Failed to connect to browser: ${err.message}`);
|
|
147
147
|
return new InformationalResponse(
|
|
148
148
|
`Browser connection failed: ${err.message}`,
|
|
149
149
|
'The browser must be running with remote debugging enabled.',
|
|
@@ -160,15 +160,15 @@ export async function navigateHistory({ url, direction = 'back', returnHtml = tr
|
|
|
160
160
|
|
|
161
161
|
if (!page) {
|
|
162
162
|
const isConnectionLost = pageError && pageError.includes('connection');
|
|
163
|
-
logger.debug(`
|
|
163
|
+
logger.debug(`browser_navigate_history: ${pageError || 'No page found for ' + hostname}`);
|
|
164
164
|
return new InformationalResponse(
|
|
165
165
|
isConnectionLost ? `Page connection lost for ${hostname}` : `No open page found for ${hostname}`,
|
|
166
166
|
isConnectionLost
|
|
167
167
|
? 'The browser tab was closed or the connection was lost. The page needs to be reloaded.'
|
|
168
168
|
: 'The page must be loaded before you can navigate its history.',
|
|
169
169
|
[
|
|
170
|
-
"Use MCPBrowser's
|
|
171
|
-
"Then retry MCPBrowser's
|
|
170
|
+
"Use MCPBrowser's browser_fetch_webpage tool to load the page first",
|
|
171
|
+
"Then retry MCPBrowser's browser_navigate_history with the same URL"
|
|
172
172
|
]
|
|
173
173
|
);
|
|
174
174
|
}
|
|
@@ -186,15 +186,15 @@ export async function navigateHistory({ url, direction = 'back', returnHtml = tr
|
|
|
186
186
|
|
|
187
187
|
// goBack/goForward return null if there's no history entry
|
|
188
188
|
if (response === null) {
|
|
189
|
-
logger.info(`
|
|
189
|
+
logger.info(`browser_navigate_history: No ${direction} history entry available`);
|
|
190
190
|
return new InformationalResponse(
|
|
191
191
|
`No ${direction} history entry available`,
|
|
192
192
|
`The page has no ${direction} history to navigate to. This means you're already at the ${direction === 'back' ? 'first' : 'last'} page in the browsing history for this tab.`,
|
|
193
193
|
[
|
|
194
194
|
direction === 'back'
|
|
195
|
-
? "Use MCPBrowser's
|
|
196
|
-
: "Use MCPBrowser's
|
|
197
|
-
"Use MCPBrowser's
|
|
195
|
+
? "Use MCPBrowser's browser_fetch_webpage to navigate to a different URL"
|
|
196
|
+
: "Use MCPBrowser's browser_navigate_history with direction='back' to go back instead",
|
|
197
|
+
"Use MCPBrowser's browser_get_current_html to check the current page content"
|
|
198
198
|
]
|
|
199
199
|
);
|
|
200
200
|
}
|
|
@@ -207,7 +207,7 @@ export async function navigateHistory({ url, direction = 'back', returnHtml = tr
|
|
|
207
207
|
if (newHostname !== hostname) {
|
|
208
208
|
domainPages.delete(hostname);
|
|
209
209
|
domainPages.set(newHostname, page);
|
|
210
|
-
logger.info(`
|
|
210
|
+
logger.info(`browser_navigate_history: Updated domainPages mapping: ${hostname} → ${newHostname}`);
|
|
211
211
|
}
|
|
212
212
|
} catch {
|
|
213
213
|
// If URL parsing fails, keep existing mapping
|
|
@@ -220,7 +220,7 @@ export async function navigateHistory({ url, direction = 'back', returnHtml = tr
|
|
|
220
220
|
html = await extractAndProcessHtml(page, removeUnnecessaryHTML);
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
-
logger.info(`
|
|
223
|
+
logger.info(`browser_navigate_history completed: ${direction} from ${previousUrl} to ${currentUrl}`);
|
|
224
224
|
|
|
225
225
|
return new NavigateHistorySuccessResponse(
|
|
226
226
|
direction,
|
|
@@ -228,20 +228,20 @@ export async function navigateHistory({ url, direction = 'back', returnHtml = tr
|
|
|
228
228
|
currentUrl,
|
|
229
229
|
html,
|
|
230
230
|
[
|
|
231
|
-
"Use MCPBrowser's
|
|
232
|
-
"Use MCPBrowser's
|
|
233
|
-
"Use MCPBrowser's
|
|
234
|
-
"Use MCPBrowser's
|
|
231
|
+
"Use MCPBrowser's browser_navigate_history to go back or forward again",
|
|
232
|
+
"Use MCPBrowser's browser_click_element to interact with elements on the page",
|
|
233
|
+
"Use MCPBrowser's browser_get_current_html to re-read the page content",
|
|
234
|
+
"Use MCPBrowser's browser_fetch_webpage to navigate to a new URL"
|
|
235
235
|
]
|
|
236
236
|
);
|
|
237
237
|
} catch (err) {
|
|
238
|
-
logger.error(`
|
|
238
|
+
logger.error(`browser_navigate_history failed: ${err.message}`);
|
|
239
239
|
return new InformationalResponse(
|
|
240
240
|
`Navigation ${direction} failed: ${err.message}`,
|
|
241
241
|
'The browser could not navigate. The page may have been closed or the connection was lost.',
|
|
242
242
|
[
|
|
243
|
-
"Try MCPBrowser's
|
|
244
|
-
"Use MCPBrowser's
|
|
243
|
+
"Try MCPBrowser's browser_fetch_webpage to reload the page",
|
|
244
|
+
"Use MCPBrowser's browser_close_tab and start fresh if needed"
|
|
245
245
|
]
|
|
246
246
|
);
|
|
247
247
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* plugin-action.js — MCP tool that dispatches to a plugin's action.
|
|
3
3
|
* Looks up the plugin by name, finds the action, provides the browser
|
|
4
4
|
* page object, and calls the action's execute function.
|
|
5
|
-
* Part of the plugin dispatch pair (
|
|
5
|
+
* Part of the plugin dispatch pair (browser_plugin_info + browser_plugin_action).
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { MCPResponse, ErrorResponse } from '../core/responses.js';
|
|
@@ -36,9 +36,9 @@ export class PluginActionSuccessResponse extends MCPResponse {
|
|
|
36
36
|
|
|
37
37
|
/** @type {Tool} */
|
|
38
38
|
export const PLUGIN_ACTION_TOOL = {
|
|
39
|
-
name: "
|
|
39
|
+
name: "browser_plugin_action",
|
|
40
40
|
title: "Plugin Action",
|
|
41
|
-
description: "Execute a site-specific plugin action. Use
|
|
41
|
+
description: "Execute a site-specific plugin action. Use browser_plugin_info first to discover available actions and their parameters. Plugins provide specialized automation for UI-heavy websites like Gmail, Outlook, PowerBI, AWS, and Azure — faster and more reliable than generic DOM interaction.",
|
|
42
42
|
inputSchema: {
|
|
43
43
|
type: "object",
|
|
44
44
|
properties: {
|
|
@@ -52,7 +52,7 @@ export const PLUGIN_ACTION_TOOL = {
|
|
|
52
52
|
},
|
|
53
53
|
params: {
|
|
54
54
|
type: "object",
|
|
55
|
-
description: "Action parameters. Use
|
|
55
|
+
description: "Action parameters. Use browser_plugin_info to discover accepted parameters.",
|
|
56
56
|
additionalProperties: true
|
|
57
57
|
}
|
|
58
58
|
},
|
|
@@ -86,7 +86,7 @@ export const PLUGIN_ACTION_TOOL = {
|
|
|
86
86
|
* @returns {Promise<MCPResponse>}
|
|
87
87
|
*/
|
|
88
88
|
export async function pluginAction({ plugin: pluginName, action: actionName, params = {} }) {
|
|
89
|
-
logger.info(`
|
|
89
|
+
logger.info(`browser_plugin_action called: plugin=${pluginName} action=${actionName}`);
|
|
90
90
|
|
|
91
91
|
const loadedPlugins = getLoadedPlugins();
|
|
92
92
|
|
|
@@ -96,7 +96,7 @@ export async function pluginAction({ plugin: pluginName, action: actionName, par
|
|
|
96
96
|
const available = [...loadedPlugins.keys()].join(', ') || '(none)';
|
|
97
97
|
return new ErrorResponse(
|
|
98
98
|
`Unknown plugin: '${pluginName}'. Available plugins: ${available}`,
|
|
99
|
-
["Call
|
|
99
|
+
["Call browser_plugin_info() to list all loaded plugins"]
|
|
100
100
|
);
|
|
101
101
|
}
|
|
102
102
|
|
|
@@ -107,7 +107,7 @@ export async function pluginAction({ plugin: pluginName, action: actionName, par
|
|
|
107
107
|
const validActions = actions.map(a => a.name).join(', ');
|
|
108
108
|
return new ErrorResponse(
|
|
109
109
|
`Unknown action '${actionName}' for plugin '${pluginName}'. Available actions: ${validActions}`,
|
|
110
|
-
[`Call
|
|
110
|
+
[`Call browser_plugin_info({ plugin: '${pluginName}' }) to see all available actions and their parameters`]
|
|
111
111
|
);
|
|
112
112
|
}
|
|
113
113
|
|
|
@@ -136,17 +136,17 @@ export async function pluginAction({ plugin: pluginName, action: actionName, par
|
|
|
136
136
|
if (!matchedPage) {
|
|
137
137
|
const targetPatterns = pluginInstance.manifest.urlPatterns.join(', ');
|
|
138
138
|
return new ErrorResponse(
|
|
139
|
-
`Plugin '${pluginName}' requires ${targetPatterns} but no matching page is open. Use
|
|
140
|
-
[`Use MCPBrowser's
|
|
139
|
+
`Plugin '${pluginName}' requires ${targetPatterns} but no matching page is open. Use browser_fetch_webpage to navigate to the correct site first.`,
|
|
140
|
+
[`Use MCPBrowser's browser_fetch_webpage to navigate to a page matching: ${targetPatterns}`, `Then retry browser_plugin_action`]
|
|
141
141
|
);
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
page = matchedPage;
|
|
145
145
|
} catch (err) {
|
|
146
|
-
logger.error(`
|
|
146
|
+
logger.error(`browser_plugin_action: browser error — ${err.message}`);
|
|
147
147
|
return new ErrorResponse(
|
|
148
148
|
`Browser connection failed: ${err.message}`,
|
|
149
|
-
["Ensure the browser is running with remote debugging enabled", "Retry
|
|
149
|
+
["Ensure the browser is running with remote debugging enabled", "Retry browser_plugin_action after browser is connected"]
|
|
150
150
|
);
|
|
151
151
|
}
|
|
152
152
|
|
|
@@ -164,15 +164,15 @@ export async function pluginAction({ plugin: pluginName, action: actionName, par
|
|
|
164
164
|
pluginName,
|
|
165
165
|
actionName,
|
|
166
166
|
result,
|
|
167
|
-
[`Use
|
|
167
|
+
[`Use browser_plugin_info({ plugin: '${pluginName}' }) to see other available actions`]
|
|
168
168
|
);
|
|
169
169
|
} catch (err) {
|
|
170
|
-
logger.error(`
|
|
170
|
+
logger.error(`browser_plugin_action: "${pluginName}/${actionName}" failed — ${err.message}`);
|
|
171
171
|
return new ErrorResponse(
|
|
172
|
-
`Plugin '${pluginName}' action '${actionName}' failed: ${err.message}. The site structure may have changed. You can fall back to generic MCPBrowser tools (
|
|
172
|
+
`Plugin '${pluginName}' action '${actionName}' failed: ${err.message}. The site structure may have changed. You can fall back to generic MCPBrowser tools (browser_click_element, browser_get_current_html).`,
|
|
173
173
|
[
|
|
174
174
|
"Check if the page is on the correct site",
|
|
175
|
-
"Try MCPBrowser's
|
|
175
|
+
"Try MCPBrowser's browser_get_current_html to inspect the page state",
|
|
176
176
|
"Use generic MCPBrowser tools as a fallback"
|
|
177
177
|
]
|
|
178
178
|
);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* plugin-info.js — MCP tool that returns information about installed plugins,
|
|
3
3
|
* their available actions, parameters, and high-level site context.
|
|
4
|
-
* Part of the plugin dispatch pair (
|
|
4
|
+
* Part of the plugin dispatch pair (browser_plugin_info + browser_plugin_action).
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { MCPResponse, ErrorResponse } from '../core/responses.js';
|
|
@@ -53,9 +53,9 @@ export class PluginActionDetailResponse extends MCPResponse {
|
|
|
53
53
|
|
|
54
54
|
/** @type {Tool} */
|
|
55
55
|
export const PLUGIN_INFO_TOOL = {
|
|
56
|
-
name: "
|
|
56
|
+
name: "browser_plugin_info",
|
|
57
57
|
title: "Plugin Info",
|
|
58
|
-
description: "Get information about an installed site plugin — its available actions, parameters, and site context. Call this after a plugin is detected (recommended in nextSteps) to discover what actions you can perform via
|
|
58
|
+
description: "Get information about an installed site plugin — its available actions, parameters, and site context. Call this after a plugin is detected (recommended in nextSteps) to discover what actions you can perform via browser_plugin_action. You can also call with no arguments to list all loaded plugins.",
|
|
59
59
|
inputSchema: {
|
|
60
60
|
type: "object",
|
|
61
61
|
properties: {
|
|
@@ -96,7 +96,7 @@ export const PLUGIN_INFO_TOOL = {
|
|
|
96
96
|
* @returns {MCPResponse}
|
|
97
97
|
*/
|
|
98
98
|
export function pluginInfo({ plugin, action } = {}) {
|
|
99
|
-
logger.info(`
|
|
99
|
+
logger.info(`browser_plugin_info called: plugin=${plugin || '(all)'} action=${action || '(all)'}`);
|
|
100
100
|
|
|
101
101
|
const loadedPlugins = getLoadedPlugins();
|
|
102
102
|
|
|
@@ -112,7 +112,7 @@ export function pluginInfo({ plugin, action } = {}) {
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
const nextSteps = plugins.length > 0
|
|
115
|
-
? plugins.map(p => `Call
|
|
115
|
+
? plugins.map(p => `Call browser_plugin_info({ plugin: '${p.name}' }) to see ${p.name}'s available actions`)
|
|
116
116
|
: ["No plugins are currently loaded. Add plugin names to plugins.json and restart the server."];
|
|
117
117
|
|
|
118
118
|
return new PluginListResponse(plugins, nextSteps);
|
|
@@ -125,7 +125,7 @@ export function pluginInfo({ plugin, action } = {}) {
|
|
|
125
125
|
return new ErrorResponse(
|
|
126
126
|
`Unknown plugin: '${plugin}'. Available plugins: ${available}`,
|
|
127
127
|
loadedPlugins.size > 0
|
|
128
|
-
? [`Call
|
|
128
|
+
? [`Call browser_plugin_info() with no arguments to list all plugins`]
|
|
129
129
|
: ["No plugins are currently loaded. Add plugin names to plugins.json and restart the server."]
|
|
130
130
|
);
|
|
131
131
|
}
|
|
@@ -138,14 +138,14 @@ export function pluginInfo({ plugin, action } = {}) {
|
|
|
138
138
|
const validActions = actions.map(a => a.name).join(', ');
|
|
139
139
|
return new ErrorResponse(
|
|
140
140
|
`Unknown action '${action}' for plugin '${plugin}'. Available actions: ${validActions}`,
|
|
141
|
-
[`Call
|
|
141
|
+
[`Call browser_plugin_info({ plugin: '${plugin}' }) to see all available actions`]
|
|
142
142
|
);
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
return new PluginActionDetailResponse(
|
|
146
146
|
plugin,
|
|
147
147
|
{ name: actionDef.name, description: actionDef.description, params: actionDef.params },
|
|
148
|
-
[`Call
|
|
148
|
+
[`Call browser_plugin_action({ plugin: '${plugin}', action: '${action}', params: { ... } })`]
|
|
149
149
|
);
|
|
150
150
|
}
|
|
151
151
|
|
|
@@ -161,9 +161,9 @@ export function pluginInfo({ plugin, action } = {}) {
|
|
|
161
161
|
|
|
162
162
|
const nextSteps = [
|
|
163
163
|
...(info.actions || []).slice(0, 3).map(a =>
|
|
164
|
-
`Use
|
|
164
|
+
`Use browser_plugin_action({ plugin: '${plugin}', action: '${a.name}' }) to ${a.description.toLowerCase()}`
|
|
165
165
|
),
|
|
166
|
-
"Use
|
|
166
|
+
"Use browser_fetch_webpage to navigate to the target site first if not already there"
|
|
167
167
|
];
|
|
168
168
|
|
|
169
169
|
return new PluginInfoResponse(pluginDetail, nextSteps);
|
|
@@ -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_scroll_page operations
|
|
20
20
|
* Returns scroll result with new scroll position
|
|
21
21
|
*/
|
|
22
22
|
export class ScrollPageSuccessResponse extends MCPResponse {
|
|
@@ -92,9 +92,9 @@ export class ScrollPageSuccessResponse extends MCPResponse {
|
|
|
92
92
|
* @type {Tool}
|
|
93
93
|
*/
|
|
94
94
|
export const SCROLL_PAGE_TOOL = {
|
|
95
|
-
name: "
|
|
95
|
+
name: "browser_scroll_page",
|
|
96
96
|
title: "Scroll Page",
|
|
97
|
-
description: "**PAGE NAVIGATION** - Scrolls within an already-loaded page. Use before
|
|
97
|
+
description: "**PAGE NAVIGATION** - Scrolls within an already-loaded page. Use before browser_take_screenshot to capture different parts of the page, or to bring elements into view before interaction.\n\n**PREREQUISITE**: Page MUST be loaded with browser_fetch_webpage first.\n\n**SCROLL MODES**:\n- By direction: Scroll up/down/left/right by pixel amount\n- To element: Scroll until a specific element is visible\n- To position: Scroll to absolute coordinates",
|
|
98
98
|
inputSchema: {
|
|
99
99
|
type: "object",
|
|
100
100
|
properties: {
|
|
@@ -166,7 +166,7 @@ export const SCROLL_PAGE_TOOL = {
|
|
|
166
166
|
* @returns {Promise<Object>} Result object with scroll position data
|
|
167
167
|
*/
|
|
168
168
|
export async function scrollPage({ url, direction, amount = 500, selector, x, y }) {
|
|
169
|
-
logger.info(`
|
|
169
|
+
logger.info(`browser_scroll_page called: url=${url}, direction=${direction}, amount=${amount}, selector=${selector}, x=${x}, y=${y}`);
|
|
170
170
|
|
|
171
171
|
if (!url) {
|
|
172
172
|
throw new Error("url parameter is required");
|
|
@@ -183,7 +183,7 @@ export async function scrollPage({ url, direction, amount = 500, selector, x, y
|
|
|
183
183
|
try {
|
|
184
184
|
await getBrowser();
|
|
185
185
|
} catch (err) {
|
|
186
|
-
logger.error(`
|
|
186
|
+
logger.error(`browser_scroll_page: Failed to connect to browser: ${err.message}`);
|
|
187
187
|
return new InformationalResponse(
|
|
188
188
|
`Browser connection failed: ${err.message}`,
|
|
189
189
|
'The browser must be running with remote debugging enabled.',
|
|
@@ -200,15 +200,15 @@ export async function scrollPage({ url, direction, amount = 500, selector, x, y
|
|
|
200
200
|
|
|
201
201
|
if (!page) {
|
|
202
202
|
const isConnectionLost = pageError && pageError.includes('connection');
|
|
203
|
-
logger.debug(`
|
|
203
|
+
logger.debug(`browser_scroll_page: ${pageError || 'No page found for ' + hostname}`);
|
|
204
204
|
return new InformationalResponse(
|
|
205
205
|
isConnectionLost ? `Page connection lost for ${hostname}` : `No open page found for ${hostname}`,
|
|
206
206
|
isConnectionLost
|
|
207
207
|
? 'The browser tab was closed or the connection was lost. The page needs to be reloaded.'
|
|
208
208
|
: 'The page must be loaded before you can scroll',
|
|
209
209
|
[
|
|
210
|
-
"Use MCPBrowser's
|
|
211
|
-
"Then retry MCPBrowser's
|
|
210
|
+
"Use MCPBrowser's browser_fetch_webpage tool to load the page first",
|
|
211
|
+
"Then retry MCPBrowser's browser_scroll_page with the same URL"
|
|
212
212
|
]
|
|
213
213
|
);
|
|
214
214
|
}
|
|
@@ -219,7 +219,7 @@ export async function scrollPage({ url, direction, amount = 500, selector, x, y
|
|
|
219
219
|
// Determine scroll mode and execute
|
|
220
220
|
if (selector) {
|
|
221
221
|
// Scroll to element mode
|
|
222
|
-
logger.debug(`
|
|
222
|
+
logger.debug(`browser_scroll_page: Scrolling to element: ${selector}`);
|
|
223
223
|
|
|
224
224
|
const elementExists = await page.$(selector);
|
|
225
225
|
if (!elementExists) {
|
|
@@ -227,7 +227,7 @@ export async function scrollPage({ url, direction, amount = 500, selector, x, y
|
|
|
227
227
|
`Element not found: ${selector}`,
|
|
228
228
|
'The specified CSS selector did not match any element on the page.',
|
|
229
229
|
[
|
|
230
|
-
"Use MCPBrowser's
|
|
230
|
+
"Use MCPBrowser's browser_get_current_html to inspect the page structure",
|
|
231
231
|
"Verify the CSS selector is correct",
|
|
232
232
|
"Try a different selector"
|
|
233
233
|
]
|
|
@@ -243,7 +243,7 @@ export async function scrollPage({ url, direction, amount = 500, selector, x, y
|
|
|
243
243
|
|
|
244
244
|
} else if (typeof x === 'number' && typeof y === 'number') {
|
|
245
245
|
// Absolute position mode
|
|
246
|
-
logger.debug(`
|
|
246
|
+
logger.debug(`browser_scroll_page: Scrolling to absolute position: (${x}, ${y})`);
|
|
247
247
|
|
|
248
248
|
await page.evaluate(({ scrollX, scrollY }) => {
|
|
249
249
|
window.scrollTo(scrollX, scrollY);
|
|
@@ -251,7 +251,7 @@ export async function scrollPage({ url, direction, amount = 500, selector, x, y
|
|
|
251
251
|
|
|
252
252
|
} else if (direction) {
|
|
253
253
|
// Directional scroll mode
|
|
254
|
-
logger.debug(`
|
|
254
|
+
logger.debug(`browser_scroll_page: Scrolling ${direction} by ${amount}px`);
|
|
255
255
|
|
|
256
256
|
const scrollDeltas = {
|
|
257
257
|
up: { x: 0, y: -amount },
|
|
@@ -271,7 +271,7 @@ export async function scrollPage({ url, direction, amount = 500, selector, x, y
|
|
|
271
271
|
|
|
272
272
|
} else {
|
|
273
273
|
// No scroll parameters provided - just return current position
|
|
274
|
-
logger.debug(`
|
|
274
|
+
logger.debug(`browser_scroll_page: No scroll action specified, returning current position`);
|
|
275
275
|
}
|
|
276
276
|
|
|
277
277
|
// Small delay to let scroll complete
|
|
@@ -287,7 +287,7 @@ export async function scrollPage({ url, direction, amount = 500, selector, x, y
|
|
|
287
287
|
viewportHeight: window.innerHeight
|
|
288
288
|
}));
|
|
289
289
|
|
|
290
|
-
logger.info(`
|
|
290
|
+
logger.info(`browser_scroll_page completed: position=(${scrollInfo.scrollX}, ${scrollInfo.scrollY}), page=(${scrollInfo.pageWidth}x${scrollInfo.pageHeight})`);
|
|
291
291
|
|
|
292
292
|
return new ScrollPageSuccessResponse(
|
|
293
293
|
currentUrl,
|
|
@@ -298,20 +298,20 @@ export async function scrollPage({ url, direction, amount = 500, selector, x, y
|
|
|
298
298
|
scrollInfo.viewportWidth,
|
|
299
299
|
scrollInfo.viewportHeight,
|
|
300
300
|
[
|
|
301
|
-
"Use MCPBrowser's
|
|
302
|
-
"Use MCPBrowser's
|
|
303
|
-
"Use MCPBrowser's
|
|
304
|
-
"Use MCPBrowser's
|
|
301
|
+
"Use MCPBrowser's browser_take_screenshot to capture the current view",
|
|
302
|
+
"Use MCPBrowser's browser_scroll_page again to navigate further",
|
|
303
|
+
"Use MCPBrowser's browser_click_element to interact with visible elements",
|
|
304
|
+
"Use MCPBrowser's browser_get_current_html to get the page content"
|
|
305
305
|
]
|
|
306
306
|
);
|
|
307
307
|
} catch (err) {
|
|
308
|
-
logger.error(`
|
|
308
|
+
logger.error(`browser_scroll_page failed: ${err.message}`);
|
|
309
309
|
return new InformationalResponse(
|
|
310
310
|
`Failed to scroll page: ${err.message}`,
|
|
311
311
|
'Could not scroll the page. The page may have navigated away or the connection was lost.',
|
|
312
312
|
[
|
|
313
|
-
"Try MCPBrowser's
|
|
314
|
-
"Use MCPBrowser's
|
|
313
|
+
"Try MCPBrowser's browser_fetch_webpage to reload the page",
|
|
314
|
+
"Use MCPBrowser's browser_close_tab and start fresh if needed"
|
|
315
315
|
]
|
|
316
316
|
);
|
|
317
317
|
}
|
|
@@ -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_take_screenshot operations
|
|
20
20
|
* Returns screenshot as base64-encoded image
|
|
21
21
|
*/
|
|
22
22
|
export class TakeScreenshotSuccessResponse extends MCPResponse {
|
|
@@ -87,9 +87,9 @@ export class TakeScreenshotSuccessResponse extends MCPResponse {
|
|
|
87
87
|
* @type {Tool}
|
|
88
88
|
*/
|
|
89
89
|
export const TAKE_SCREENSHOT_TOOL = {
|
|
90
|
-
name: "
|
|
90
|
+
name: "browser_take_screenshot",
|
|
91
91
|
title: "Take Screenshot",
|
|
92
|
-
description: "**VISUAL CAPTURE** - Takes a screenshot of an already-loaded page for visual analysis. Useful when HTML parsing is insufficient or you need to see visual layout, images, charts, or rendered content. Returns a PNG image.\n\n**PREREQUISITE**: Page MUST be loaded with
|
|
92
|
+
description: "**VISUAL CAPTURE** - Takes a screenshot of an already-loaded page for visual analysis. Useful when HTML parsing is insufficient or you need to see visual layout, images, charts, or rendered content. Returns a PNG image.\n\n**PREREQUISITE**: Page MUST be loaded with browser_fetch_webpage first. This tool captures the current visual state of the page.",
|
|
93
93
|
inputSchema: {
|
|
94
94
|
type: "object",
|
|
95
95
|
properties: {
|
|
@@ -129,7 +129,7 @@ export const TAKE_SCREENSHOT_TOOL = {
|
|
|
129
129
|
* @returns {Promise<Object>} Result object with screenshot data
|
|
130
130
|
*/
|
|
131
131
|
export async function takeScreenshot({ url, fullPage = false }) {
|
|
132
|
-
logger.info(`
|
|
132
|
+
logger.info(`browser_take_screenshot called: url=${url}, fullPage=${fullPage}`);
|
|
133
133
|
|
|
134
134
|
if (!url) {
|
|
135
135
|
throw new Error("url parameter is required");
|
|
@@ -146,7 +146,7 @@ export async function takeScreenshot({ url, fullPage = false }) {
|
|
|
146
146
|
try {
|
|
147
147
|
await getBrowser();
|
|
148
148
|
} catch (err) {
|
|
149
|
-
logger.error(`
|
|
149
|
+
logger.error(`browser_take_screenshot: Failed to connect to browser: ${err.message}`);
|
|
150
150
|
return new InformationalResponse(
|
|
151
151
|
`Browser connection failed: ${err.message}`,
|
|
152
152
|
'The browser must be running with remote debugging enabled.',
|
|
@@ -163,15 +163,15 @@ export async function takeScreenshot({ url, fullPage = false }) {
|
|
|
163
163
|
|
|
164
164
|
if (!page) {
|
|
165
165
|
const isConnectionLost = pageError && pageError.includes('connection');
|
|
166
|
-
logger.debug(`
|
|
166
|
+
logger.debug(`browser_take_screenshot: ${pageError || 'No page found for ' + hostname}`);
|
|
167
167
|
return new InformationalResponse(
|
|
168
168
|
isConnectionLost ? `Page connection lost for ${hostname}` : `No open page found for ${hostname}`,
|
|
169
169
|
isConnectionLost
|
|
170
170
|
? 'The browser tab was closed or the connection was lost. The page needs to be reloaded.'
|
|
171
171
|
: 'The page must be loaded before you can take a screenshot',
|
|
172
172
|
[
|
|
173
|
-
"Use MCPBrowser's
|
|
174
|
-
"Then retry MCPBrowser's
|
|
173
|
+
"Use MCPBrowser's browser_fetch_webpage tool to load the page first",
|
|
174
|
+
"Then retry MCPBrowser's browser_take_screenshot with the same URL"
|
|
175
175
|
]
|
|
176
176
|
);
|
|
177
177
|
}
|
|
@@ -186,27 +186,27 @@ export async function takeScreenshot({ url, fullPage = false }) {
|
|
|
186
186
|
fullPage: fullPage
|
|
187
187
|
});
|
|
188
188
|
|
|
189
|
-
logger.info(`
|
|
189
|
+
logger.info(`browser_take_screenshot completed: captured from ${currentUrl} (fullPage=${fullPage})`);
|
|
190
190
|
|
|
191
191
|
return new TakeScreenshotSuccessResponse(
|
|
192
192
|
currentUrl,
|
|
193
193
|
screenshotBuffer,
|
|
194
194
|
'image/png',
|
|
195
195
|
[
|
|
196
|
-
"Use MCPBrowser's
|
|
197
|
-
"Use MCPBrowser's
|
|
198
|
-
"Use MCPBrowser's
|
|
199
|
-
"Use MCPBrowser's
|
|
196
|
+
"Use MCPBrowser's browser_get_current_html if you need the HTML instead",
|
|
197
|
+
"Use MCPBrowser's browser_click_element to interact with elements",
|
|
198
|
+
"Use MCPBrowser's browser_type_text to fill forms",
|
|
199
|
+
"Use MCPBrowser's browser_close_tab to free resources when done"
|
|
200
200
|
]
|
|
201
201
|
);
|
|
202
202
|
} catch (err) {
|
|
203
|
-
logger.error(`
|
|
203
|
+
logger.error(`browser_take_screenshot failed: ${err.message}`);
|
|
204
204
|
return new InformationalResponse(
|
|
205
205
|
`Failed to take screenshot: ${err.message}`,
|
|
206
206
|
'Could not capture screenshot from the page. The page may have navigated away or the connection was lost.',
|
|
207
207
|
[
|
|
208
|
-
"Try MCPBrowser's
|
|
209
|
-
"Use MCPBrowser's
|
|
208
|
+
"Try MCPBrowser's browser_fetch_webpage to reload the page",
|
|
209
|
+
"Use MCPBrowser's browser_close_tab and start fresh if needed"
|
|
210
210
|
]
|
|
211
211
|
);
|
|
212
212
|
}
|