mcpbrowser 0.3.33 → 0.3.34
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/package.json +1 -1
- package/src/actions/navigate-history.js +248 -0
- package/src/mcp-browser.js +8 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcpbrowser",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.34",
|
|
4
4
|
"mcpName": "io.github.cherchyk/mcpbrowser",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "MCP browser server - fetch web pages using real Chrome/Edge/Brave browser. Handles authentication, SSO, CAPTCHAs, and anti-bot protection. Browser automation for AI assistants.",
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* navigate-history.js - Browser back/forward navigation
|
|
3
|
+
* Navigates browser history on an already-loaded page.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { getBrowser, getValidatedPage, domainPages } from '../core/browser.js';
|
|
7
|
+
import { extractAndProcessHtml, waitForPageReady } from '../core/page.js';
|
|
8
|
+
import { MCPResponse, InformationalResponse } from '../core/responses.js';
|
|
9
|
+
import logger from '../core/logger.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @typedef {import('@modelcontextprotocol/sdk/types.js').Tool} Tool
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// RESPONSE CLASS
|
|
17
|
+
// ============================================================================
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Response for successful navigate_history operations
|
|
21
|
+
*/
|
|
22
|
+
export class NavigateHistorySuccessResponse extends MCPResponse {
|
|
23
|
+
/**
|
|
24
|
+
* @param {string} direction - Navigation direction (back or forward)
|
|
25
|
+
* @param {string} previousUrl - URL before navigation
|
|
26
|
+
* @param {string} currentUrl - URL after navigation
|
|
27
|
+
* @param {string|null} html - Page HTML content (null if returnHtml=false)
|
|
28
|
+
* @param {string[]} nextSteps - Suggested next actions
|
|
29
|
+
*/
|
|
30
|
+
constructor(direction, previousUrl, currentUrl, html, nextSteps) {
|
|
31
|
+
super(nextSteps);
|
|
32
|
+
|
|
33
|
+
if (typeof direction !== 'string') {
|
|
34
|
+
throw new TypeError('direction must be a string');
|
|
35
|
+
}
|
|
36
|
+
if (typeof previousUrl !== 'string') {
|
|
37
|
+
throw new TypeError('previousUrl must be a string');
|
|
38
|
+
}
|
|
39
|
+
if (typeof currentUrl !== 'string') {
|
|
40
|
+
throw new TypeError('currentUrl must be a string');
|
|
41
|
+
}
|
|
42
|
+
if (html !== null && typeof html !== 'string') {
|
|
43
|
+
throw new TypeError('html must be a string or null');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
this.direction = direction;
|
|
47
|
+
this.previousUrl = previousUrl;
|
|
48
|
+
this.currentUrl = currentUrl;
|
|
49
|
+
this.html = html;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
_getAdditionalFields() {
|
|
53
|
+
return {
|
|
54
|
+
direction: this.direction,
|
|
55
|
+
previousUrl: this.previousUrl,
|
|
56
|
+
currentUrl: this.currentUrl,
|
|
57
|
+
html: this.html
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
getTextSummary() {
|
|
62
|
+
return `Navigated ${this.direction}: ${this.previousUrl} → ${this.currentUrl}`;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ============================================================================
|
|
67
|
+
// TOOL DEFINITION
|
|
68
|
+
// ============================================================================
|
|
69
|
+
|
|
70
|
+
/** @type {Tool} */
|
|
71
|
+
export const NAVIGATE_HISTORY_TOOL = {
|
|
72
|
+
name: "navigate_history",
|
|
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 fetch_webpage first. This tool navigates the history of an existing browser tab.",
|
|
75
|
+
inputSchema: {
|
|
76
|
+
type: "object",
|
|
77
|
+
properties: {
|
|
78
|
+
url: { type: "string", description: "URL of the already-loaded page (identifies which tab to navigate)" },
|
|
79
|
+
direction: {
|
|
80
|
+
type: "string",
|
|
81
|
+
enum: ["back", "forward"],
|
|
82
|
+
description: "Navigation direction: 'back' to go to previous page, 'forward' to go to next page",
|
|
83
|
+
default: "back"
|
|
84
|
+
},
|
|
85
|
+
returnHtml: { type: "boolean", description: "Return page HTML after navigation", default: true },
|
|
86
|
+
removeUnnecessaryHTML: { type: "boolean", description: "Remove unnecessary HTML elements (scripts, styles, etc.) for size reduction.", default: true }
|
|
87
|
+
},
|
|
88
|
+
required: ["url"],
|
|
89
|
+
additionalProperties: false
|
|
90
|
+
},
|
|
91
|
+
outputSchema: {
|
|
92
|
+
type: "object",
|
|
93
|
+
properties: {
|
|
94
|
+
direction: { type: "string", enum: ["back", "forward"], description: "Navigation direction used" },
|
|
95
|
+
previousUrl: { type: "string", description: "URL before navigation" },
|
|
96
|
+
currentUrl: { type: "string", description: "URL after navigation" },
|
|
97
|
+
html: { type: ["string", "null"], description: "Page HTML content after navigation (null if returnHtml=false)" },
|
|
98
|
+
nextSteps: {
|
|
99
|
+
type: "array",
|
|
100
|
+
items: { type: "string" },
|
|
101
|
+
description: "Suggested next actions"
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
required: ["direction", "previousUrl", "currentUrl", "nextSteps"],
|
|
105
|
+
additionalProperties: false
|
|
106
|
+
},
|
|
107
|
+
annotations: {
|
|
108
|
+
title: "Navigate Back/Forward",
|
|
109
|
+
readOnlyHint: false,
|
|
110
|
+
destructiveHint: false,
|
|
111
|
+
openWorldHint: true
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// ============================================================================
|
|
116
|
+
// ACTION FUNCTION
|
|
117
|
+
// ============================================================================
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Navigate browser history (back/forward) on an already-loaded page
|
|
121
|
+
* @param {Object} params - Parameters
|
|
122
|
+
* @param {string} params.url - URL of the already-loaded page
|
|
123
|
+
* @param {string} [params.direction='back'] - Navigation direction
|
|
124
|
+
* @param {boolean} [params.returnHtml=true] - Return page HTML after navigation
|
|
125
|
+
* @param {boolean} [params.removeUnnecessaryHTML=true] - Clean HTML
|
|
126
|
+
* @returns {Promise<MCPResponse>} Navigation result
|
|
127
|
+
*/
|
|
128
|
+
export async function navigateHistory({ url, direction = 'back', returnHtml = true, removeUnnecessaryHTML = true }) {
|
|
129
|
+
logger.info(`navigate_history called: url=${url}, direction=${direction}`);
|
|
130
|
+
|
|
131
|
+
if (!url) {
|
|
132
|
+
throw new Error("url parameter is required");
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
let hostname;
|
|
136
|
+
try {
|
|
137
|
+
hostname = new URL(url).hostname;
|
|
138
|
+
} catch {
|
|
139
|
+
throw new Error(`Invalid URL: ${url}`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Ensure browser connection
|
|
143
|
+
try {
|
|
144
|
+
await getBrowser();
|
|
145
|
+
} catch (err) {
|
|
146
|
+
logger.error(`navigate_history: Failed to connect to browser: ${err.message}`);
|
|
147
|
+
return new InformationalResponse(
|
|
148
|
+
`Browser connection failed: ${err.message}`,
|
|
149
|
+
'The browser must be running with remote debugging enabled.',
|
|
150
|
+
[
|
|
151
|
+
'Ensure the browser is installed and running',
|
|
152
|
+
'Check that remote debugging is enabled (--remote-debugging-port)',
|
|
153
|
+
'Try restarting the MCP server'
|
|
154
|
+
]
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Validate page exists and is usable
|
|
159
|
+
const { page, error: pageError } = await getValidatedPage(hostname);
|
|
160
|
+
|
|
161
|
+
if (!page) {
|
|
162
|
+
const isConnectionLost = pageError && pageError.includes('connection');
|
|
163
|
+
logger.debug(`navigate_history: ${pageError || 'No page found for ' + hostname}`);
|
|
164
|
+
return new InformationalResponse(
|
|
165
|
+
isConnectionLost ? `Page connection lost for ${hostname}` : `No open page found for ${hostname}`,
|
|
166
|
+
isConnectionLost
|
|
167
|
+
? 'The browser tab was closed or the connection was lost. The page needs to be reloaded.'
|
|
168
|
+
: 'The page must be loaded before you can navigate its history.',
|
|
169
|
+
[
|
|
170
|
+
"Use MCPBrowser's fetch_webpage tool to load the page first",
|
|
171
|
+
"Then retry MCPBrowser's navigate_history with the same URL"
|
|
172
|
+
]
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
const previousUrl = page.url();
|
|
178
|
+
|
|
179
|
+
// Navigate history
|
|
180
|
+
let response;
|
|
181
|
+
if (direction === 'forward') {
|
|
182
|
+
response = await page.goForward({ waitUntil: 'domcontentloaded', timeout: 30000 });
|
|
183
|
+
} else {
|
|
184
|
+
response = await page.goBack({ waitUntil: 'domcontentloaded', timeout: 30000 });
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// goBack/goForward return null if there's no history entry
|
|
188
|
+
if (response === null) {
|
|
189
|
+
logger.info(`navigate_history: No ${direction} history entry available`);
|
|
190
|
+
return new InformationalResponse(
|
|
191
|
+
`No ${direction} history entry available`,
|
|
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
|
+
[
|
|
194
|
+
direction === 'back'
|
|
195
|
+
? "Use MCPBrowser's fetch_webpage to navigate to a different URL"
|
|
196
|
+
: "Use MCPBrowser's navigate_history with direction='back' to go back instead",
|
|
197
|
+
"Use MCPBrowser's get_current_html to check the current page content"
|
|
198
|
+
]
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const currentUrl = page.url();
|
|
203
|
+
|
|
204
|
+
// Update domainPages if hostname changed after navigation
|
|
205
|
+
try {
|
|
206
|
+
const newHostname = new URL(currentUrl).hostname;
|
|
207
|
+
if (newHostname !== hostname) {
|
|
208
|
+
domainPages.delete(hostname);
|
|
209
|
+
domainPages.set(newHostname, page);
|
|
210
|
+
logger.info(`navigate_history: Updated domainPages mapping: ${hostname} → ${newHostname}`);
|
|
211
|
+
}
|
|
212
|
+
} catch {
|
|
213
|
+
// If URL parsing fails, keep existing mapping
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Extract HTML if requested
|
|
217
|
+
let html = null;
|
|
218
|
+
if (returnHtml) {
|
|
219
|
+
await waitForPageReady(page);
|
|
220
|
+
html = await extractAndProcessHtml(page, removeUnnecessaryHTML);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
logger.info(`navigate_history completed: ${direction} from ${previousUrl} to ${currentUrl}`);
|
|
224
|
+
|
|
225
|
+
return new NavigateHistorySuccessResponse(
|
|
226
|
+
direction,
|
|
227
|
+
previousUrl,
|
|
228
|
+
currentUrl,
|
|
229
|
+
html,
|
|
230
|
+
[
|
|
231
|
+
"Use MCPBrowser's navigate_history to go back or forward again",
|
|
232
|
+
"Use MCPBrowser's click_element to interact with elements on the page",
|
|
233
|
+
"Use MCPBrowser's get_current_html to re-read the page content",
|
|
234
|
+
"Use MCPBrowser's fetch_webpage to navigate to a new URL"
|
|
235
|
+
]
|
|
236
|
+
);
|
|
237
|
+
} catch (err) {
|
|
238
|
+
logger.error(`navigate_history failed: ${err.message}`);
|
|
239
|
+
return new InformationalResponse(
|
|
240
|
+
`Navigation ${direction} failed: ${err.message}`,
|
|
241
|
+
'The browser could not navigate. The page may have been closed or the connection was lost.',
|
|
242
|
+
[
|
|
243
|
+
"Try MCPBrowser's fetch_webpage to reload the page",
|
|
244
|
+
"Use MCPBrowser's close_tab and start fresh if needed"
|
|
245
|
+
]
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
}
|
package/src/mcp-browser.js
CHANGED
|
@@ -28,6 +28,7 @@ import { getCurrentHtml, GET_CURRENT_HTML_TOOL } from './actions/get-current-htm
|
|
|
28
28
|
import { takeScreenshot, TAKE_SCREENSHOT_TOOL } from './actions/take-screenshot.js';
|
|
29
29
|
import { scrollPage, SCROLL_PAGE_TOOL } from './actions/scroll-page.js';
|
|
30
30
|
import { executeJavascript, EXECUTE_JAVASCRIPT_TOOL } from './actions/execute-javascript.js';
|
|
31
|
+
import { navigateHistory, NAVIGATE_HISTORY_TOOL } from './actions/navigate-history.js';
|
|
31
32
|
|
|
32
33
|
// Import functions for testing exports
|
|
33
34
|
import { getBrowser, closeBrowser } from './core/browser.js';
|
|
@@ -67,7 +68,8 @@ async function main() {
|
|
|
67
68
|
CLOSE_TAB_TOOL,
|
|
68
69
|
GET_CURRENT_HTML_TOOL,
|
|
69
70
|
TAKE_SCREENSHOT_TOOL,
|
|
70
|
-
SCROLL_PAGE_TOOL
|
|
71
|
+
SCROLL_PAGE_TOOL,
|
|
72
|
+
NAVIGATE_HISTORY_TOOL
|
|
71
73
|
];
|
|
72
74
|
|
|
73
75
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));
|
|
@@ -121,6 +123,10 @@ async function main() {
|
|
|
121
123
|
case "scroll_page":
|
|
122
124
|
result = await scrollPage(safeArgs);
|
|
123
125
|
break;
|
|
126
|
+
|
|
127
|
+
case "navigate_history":
|
|
128
|
+
result = await navigateHistory(safeArgs);
|
|
129
|
+
break;
|
|
124
130
|
|
|
125
131
|
default:
|
|
126
132
|
throw new Error(`Unknown tool: ${name}`);
|
|
@@ -171,6 +177,7 @@ export {
|
|
|
171
177
|
getCurrentHtml,
|
|
172
178
|
takeScreenshot,
|
|
173
179
|
scrollPage,
|
|
180
|
+
navigateHistory,
|
|
174
181
|
handleAcceptEula
|
|
175
182
|
};
|
|
176
183
|
|