mcpbrowser 0.3.17 → 0.3.19

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 CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  > ⚠️ **Security Notice:** MCPBrowser extracts webpage content and provides it to your AI agent (e.g., GitHub Copilot, Claude), which then sends it to the LLM provider it uses (e.g., Anthropic, OpenAI, GitHub) for processing. Make sure you trust both your agent and the LLM provider — especially when accessing pages with sensitive or private data.
9
9
 
10
- **MCPBrowser is an MCP browser server that gives AI assistants the ability to browse web pages using a real Chrome or Edge browser.** This browser-based MCP server fetches any web page — especially those protected by authentication, CAPTCHAs, anti-bot protection, or requiring JavaScript rendering. Uses your real Chrome/Edge browser for web automation so you can log in normally, then automatically extracts content. Works with corporate SSO, login forms, Cloudflare, and JavaScript-heavy sites (SPAs, dashboards).
10
+ **MCPBrowser is an MCP browser server that gives AI assistants the ability to browse web pages using a real Chrome, Edge, or Brave browser.** This browser-based MCP server fetches any web page — especially those protected by authentication, CAPTCHAs, anti-bot protection, or requiring JavaScript rendering. Uses your real browser for web automation so you can log in normally, then automatically extracts content. Works with corporate SSO, login forms, Cloudflare, and JavaScript-heavy sites (SPAs, dashboards).
11
11
 
12
12
  This is an [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) server using [stdio transport](https://modelcontextprotocol.io/docs/concepts/transports#stdio). Your AI assistant uses this web browser MCP server when standard HTTP requests fail — pages requiring authentication, CAPTCHA protection, or heavy JavaScript (SPAs). Once connected, the browser MCP server can navigate through websites, interact with elements, and send HTML back to the AI assistant. This gives your AI the ability to browse the web just like you do.
13
13
 
@@ -15,10 +15,9 @@ Example workflow for AI assistant to use MCPBrowser
15
15
 
16
16
  ```
17
17
  1. fetch_webpage → Load the login page
18
- 2. type_text → Enter username
19
- 3. type_text Enter password
20
- 4. click_element Click "Sign In"
21
- 5. get_current_html → Extract the content after login
18
+ 2. type_text → Enter username and password (multiple fields at once)
19
+ 3. click_element Click "Sign In"
20
+ 4. get_current_html Extract the content after login
22
21
  ```
23
22
 
24
23
 
@@ -35,6 +34,8 @@ Example workflow for AI assistant to use MCPBrowser
35
34
  - [click_element](#click_element)
36
35
  - [type_text](#type_text)
37
36
  - [get_current_html](#get_current_html)
37
+ - [scroll_page](#scroll_page)
38
+ - [take_screenshot](#take_screenshot)
38
39
  - [close_tab](#close_tab)
39
40
  - [Configuration](#configuration-optional)
40
41
  - [Troubleshooting](#troubleshooting)
@@ -42,7 +43,7 @@ Example workflow for AI assistant to use MCPBrowser
42
43
 
43
44
  ## Requirements
44
45
 
45
- - Chrome or Edge browser
46
+ - Chrome, Edge, or Brave browser
46
47
  - [Node.js 18+](https://nodejs.org/) (includes npm)
47
48
 
48
49
  > **Note:** Node.js must be installed on your system. The VS Code extension and npm package both require Node.js to run the MCP server. Download from [nodejs.org](https://nodejs.org/) if not already installed.
@@ -53,8 +54,9 @@ Example workflow for AI assistant to use MCPBrowser
53
54
  |---|----------|------------|
54
55
  | 1 | [VS Code Extension](#option-1-vs-code-extension) | One Click |
55
56
  | 2 | [Claude Code](#option-2-claude-code) | One Command |
56
- | 3 | [Claude Desktop](#option-3-claude-desktop) | Manual |
57
- | 4 | [npm Package](#option-4-npm-package) | Manual |
57
+ | 3 | [OpenClaw](#option-3-openclaw) | One Command |
58
+ | 4 | [Claude Desktop](#option-4-claude-desktop) | Manual |
59
+ | 5 | [npm Package](#option-5-npm-package) | Manual |
58
60
 
59
61
  ### Option 1: VS Code Extension
60
62
 
@@ -84,7 +86,22 @@ mcpbrowser: npx -y mcpbrowser@latest - ✓ Connected
84
86
  That's it! Ask Claude to fetch any protected page:
85
87
  > "Fetch https://portal.azure.com using mcpbrowser"
86
88
 
87
- ### Option 3: Claude Desktop
89
+ ### Option 3: OpenClaw
90
+
91
+ [OpenClaw](https://openclaw.ai/) is a personal AI assistant that runs on your devices. Add MCPBrowser to give it browser automation capabilities:
92
+
93
+ ```bash
94
+ openclaw mcp add mcpbrowser -- npx -y mcpbrowser@latest
95
+ ```
96
+
97
+ Verify it's working:
98
+ ```bash
99
+ openclaw mcp list
100
+ ```
101
+
102
+ Now OpenClaw can browse authenticated pages, fill forms, and interact with web apps using your existing browser sessions.
103
+
104
+ ### Option 4: Claude Desktop
88
105
 
89
106
  Add to your config file:
90
107
 
@@ -104,7 +121,7 @@ Add to your config file:
104
121
 
105
122
  Restart Claude Desktop after saving.
106
123
 
107
- ### Option 4: npm Package
124
+ ### Option 5: npm Package
108
125
 
109
126
  For VS Code (GitHub Copilot) manual setup, add to your `mcp.json`:
110
127
 
@@ -182,36 +199,55 @@ Clicks on any clickable element (buttons, links, divs with onclick handlers, etc
182
199
 
183
200
  ### `type_text`
184
201
 
185
- Types text into an input field, textarea, or other editable element. Simulates human-like typing with configurable delay between keystrokes. Automatically clears existing text by default.
202
+ Types text into one or more input fields in a single call. Supports filling entire forms at once for efficient automation. Automatically clears existing text by default.
186
203
 
187
204
  **⚠️ Note:** Page must be already loaded via `fetch_webpage` first.
188
205
 
189
206
  **Parameters:**
190
207
  - `url` (string, required) - The URL of the page (must match a previously fetched page)
191
- - `selector` (string, required) - CSS selector for the input element (e.g., `#username`, `input[name="email"]`)
192
- - `text` (string, required) - Text to type into the field
193
- - `clear` (boolean, optional, default: `true`) - Whether to clear existing text first
194
- - `typeDelay` (number, optional, default: `50`) - Delay between keystrokes in milliseconds (simulates human typing)
208
+ - `fields` (array, required) - Array of fields to fill. Each field object contains:
209
+ - `selector` (string, required) - CSS selector for the input element (e.g., `#username`, `input[name="email"]`)
210
+ - `text` (string, required) - Text to type into the field
211
+ - `clear` (boolean, optional, default: `true`) - Whether to clear existing text first
212
+ - `waitForElementTimeout` (number, optional, default: `5000`) - Maximum time to wait for element in milliseconds
195
213
  - `returnHtml` (boolean, optional, default: `true`) - Whether to wait for stability and return HTML after typing
196
214
  - `removeUnnecessaryHTML` (boolean, optional, default: `true`) - Remove unnecessary HTML for size reduction. Only used when `returnHtml` is `true`
197
215
  - `postTypeWait` (number, optional, default: `1000`) - Milliseconds to wait after typing for SPAs to render dynamic content
198
- - `waitForElementTimeout` (number, optional, default: `5000`) - Maximum time to wait for element in milliseconds
199
216
 
200
217
  **Examples:**
201
218
  ```javascript
202
- // Basic text input
203
- { url: "https://example.com", selector: "#email", text: "user@example.com" }
204
-
205
- // Append text without clearing
206
- { url: "https://example.com", selector: "#search", text: " advanced", clear: false }
219
+ // Fill multiple fields at once (login form)
220
+ {
221
+ url: "https://example.com/login",
222
+ fields: [
223
+ { selector: "#username", text: "john@example.com" },
224
+ { selector: "#password", text: "secretpass123" }
225
+ ]
226
+ }
207
227
 
208
- // Fast typing without human simulation
209
- { url: "https://example.com", selector: "#username", text: "john", typeDelay: 0 }
228
+ // Single field input
229
+ { url: "https://example.com", fields: [{ selector: "#search", text: "query" }] }
210
230
 
211
- // Type without waiting for HTML return (faster)
212
- { url: "https://example.com", selector: "#field", text: "value", returnHtml: false }
231
+ // Append text without clearing
232
+ { url: "https://example.com", fields: [{ selector: "#notes", text: " additional text", clear: false }] }
233
+
234
+ // Fast form fill without HTML return
235
+ {
236
+ url: "https://example.com/signup",
237
+ fields: [
238
+ { selector: "#firstName", text: "John" },
239
+ { selector: "#lastName", text: "Doe" },
240
+ { selector: "#email", text: "john@example.com" }
241
+ ],
242
+ returnHtml: false
243
+ }
213
244
  ```
214
245
 
246
+ **Error handling:** If a field fails, the response indicates:
247
+ - Which field number failed (e.g., "Failed on field 2 of 3")
248
+ - Which fields were successfully filled
249
+ - Clear guidance to NOT re-type already filled fields
250
+
215
251
  ---
216
252
 
217
253
  ### `get_current_html`
@@ -239,6 +275,74 @@ Gets the current HTML from an already-loaded page **WITHOUT** navigating or relo
239
275
 
240
276
  ---
241
277
 
278
+ ### `scroll_page`
279
+
280
+ Scrolls within an already-loaded page. Use before `take_screenshot` to capture different parts of the page, or to bring elements into view before interaction. Supports multiple scroll modes:
281
+
282
+ - **By direction**: Scroll up/down/left/right by pixel amount
283
+ - **To element**: Scroll until a specific element is visible
284
+ - **To position**: Scroll to absolute coordinates
285
+
286
+ **⚠️ Note:** Page must be already loaded via `fetch_webpage` first.
287
+
288
+ **Parameters:**
289
+ - `url` (string, required) - The URL of the page (must match a previously fetched page)
290
+ - `direction` (string, optional) - Direction to scroll: `up`, `down`, `left`, `right`. Use with `amount`.
291
+ - `amount` (number, optional, default: `500`) - Pixels to scroll in the specified direction (~half a viewport)
292
+ - `selector` (string, optional) - CSS selector of element to scroll into view. Ignores direction/amount.
293
+ - `x` (number, optional) - Absolute horizontal scroll position. Use with `y`.
294
+ - `y` (number, optional) - Absolute vertical scroll position. Use with `x`.
295
+
296
+ **Examples:**
297
+ ```javascript
298
+ // Scroll down by 500px (default)
299
+ { url: "https://example.com", direction: "down" }
300
+
301
+ // Scroll down by 1000px
302
+ { url: "https://example.com", direction: "down", amount: 1000 }
303
+
304
+ // Scroll an element into view
305
+ { url: "https://example.com", selector: "#footer" }
306
+
307
+ // Scroll to specific position
308
+ { url: "https://example.com", x: 0, y: 2000 }
309
+
310
+ // Scroll to top of page
311
+ { url: "https://example.com", x: 0, y: 0 }
312
+ ```
313
+
314
+ **Returns:** Current scroll position, page dimensions, and viewport size — useful for understanding where you are on the page.
315
+
316
+ ---
317
+
318
+ ### `take_screenshot`
319
+
320
+ Takes a screenshot of an already-loaded page for visual analysis. **Useful when HTML parsing is insufficient** — for example, pages with charts, images, complex layouts, popups, or visual content that's hard to understand from HTML alone. Returns a PNG image.
321
+
322
+ **⚠️ Note:** Page must be already loaded via `fetch_webpage` first.
323
+
324
+ **Parameters:**
325
+ - `url` (string, required) - The URL of the page (must match a previously fetched page)
326
+ - `fullPage` (boolean, optional, default: `false`) - Capture the full scrollable page instead of just the viewport
327
+
328
+ **Examples:**
329
+ ```javascript
330
+ // Capture viewport screenshot (default)
331
+ { url: "https://example.com" }
332
+
333
+ // Capture full scrollable page
334
+ { url: "https://dashboard.example.com", fullPage: true }
335
+ ```
336
+
337
+ **Use cases:**
338
+ - Visualize page layout when HTML is hard to parse
339
+ - Capture charts, graphs, or data visualizations
340
+ - Debug popups, modals, or overlays
341
+ - Understand visual feedback (highlights, animations)
342
+ - See what's blocking an element click
343
+
344
+ ---
345
+
242
346
  ### `close_tab`
243
347
 
244
348
  Closes the browser tab for the given URL's hostname. Removes the page from the tab pool and forces a fresh session on the next visit to that hostname. Useful for clearing authentication state, managing memory, or starting fresh with a domain.
@@ -299,7 +403,7 @@ Logs go to `stderr` so they don't interfere with MCP protocol on `stdout`.
299
403
  ## Troubleshooting
300
404
 
301
405
  **Browser doesn't open?**
302
- - Make sure Chrome or Edge is installed
406
+ - Make sure Chrome, Edge, or Brave is installed
303
407
  - Try setting `CHROME_PATH` explicitly
304
408
 
305
409
  **Can't connect to browser?**
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "mcpbrowser",
3
- "version": "0.3.17",
3
+ "version": "0.3.19",
4
4
  "mcpName": "io.github.cherchyk/mcpbrowser",
5
5
  "type": "module",
6
- "description": "MCP browser server - fetch web pages using real Chrome/Edge browser. Handles authentication, SSO, CAPTCHAs, and anti-bot protection. Browser automation for AI assistants.",
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.",
7
7
  "main": "src/mcp-browser.js",
8
8
  "bin": {
9
9
  "mcpbrowser": "src/mcp-browser.js"
@@ -24,9 +24,9 @@
24
24
  * - Flexible: Can disable waiting for fast form interactions
25
25
  */
26
26
 
27
- import { getBrowser, domainPages } from '../core/browser.js';
27
+ import { getBrowser, getValidatedPage } from '../core/browser.js';
28
28
  import { extractAndProcessHtml, waitForPageStability } from '../core/page.js';
29
- import { MCPResponse, ErrorResponse } from '../core/responses.js';
29
+ import { MCPResponse, InformationalResponse } from '../core/responses.js';
30
30
  import logger from '../core/logger.js';
31
31
 
32
32
  /**
@@ -171,15 +171,36 @@ export async function clickElement({ url, selector, text, waitForElementTimeout
171
171
  throw new Error(`Invalid URL: ${url}`);
172
172
  }
173
173
 
174
- const browser = await getBrowser();
175
- let page = domainPages.get(hostname);
174
+ // Ensure browser connection (triggers domain map rebuild on reconnect)
175
+ try {
176
+ await getBrowser();
177
+ } catch (err) {
178
+ logger.error(`click_element: Failed to connect to browser: ${err.message}`);
179
+ return new InformationalResponse(
180
+ `Browser connection failed: ${err.message}`,
181
+ 'The browser must be running with remote debugging enabled.',
182
+ [
183
+ 'Ensure the browser is installed and running',
184
+ 'Check that remote debugging is enabled (--remote-debugging-port)',
185
+ 'Try restarting the MCP server'
186
+ ]
187
+ );
188
+ }
189
+
190
+ // Validate page exists and is usable
191
+ const { page, error: pageError } = await getValidatedPage(hostname);
176
192
 
177
- if (!page || page.isClosed()) {
178
- logger.error(`No open page found for ${hostname}`);
179
- return new ErrorResponse(
180
- `No open page found for ${hostname}. Please fetch the page first using fetch_webpage.`,
193
+ if (!page) {
194
+ const isConnectionLost = pageError && pageError.includes('connection');
195
+ logger.info(`click_element: ${pageError || 'No page found for ' + hostname}`);
196
+ return new InformationalResponse(
197
+ isConnectionLost ? `Page connection lost for ${hostname}` : `No open page found for ${hostname}`,
198
+ isConnectionLost
199
+ ? 'The browser tab was closed or the connection was lost. The page needs to be reloaded.'
200
+ : 'The page must be loaded before you can interact with elements on it',
181
201
  [
182
- "Use fetch_webpage to load the page first"
202
+ "Use MCPBrowser's fetch_webpage tool to load the page first",
203
+ "Then retry MCPBrowser's click_element with the same URL"
183
204
  ]
184
205
  );
185
206
  }
@@ -218,10 +239,12 @@ export async function clickElement({ url, selector, text, waitForElementTimeout
218
239
  }
219
240
 
220
241
  if (!elementHandle || !elementHandle.asElement()) {
221
- return new ErrorResponse(
242
+ return new InformationalResponse(
222
243
  selector ? `Element not found: ${selector}` : `Element with text "${text}" not found`,
244
+ 'The element could not be located on the page. It may be hidden, dynamically loaded, or the selector/text may be incorrect.',
223
245
  [
224
- "Use get_current_html to verify page content",
246
+ "Use MCPBrowser's get_current_html to verify page content",
247
+ "Use MCPBrowser's take_screenshot to see the visual layout if HTML is unclear",
225
248
  "Try a different selector or text",
226
249
  "Check if the element is visible on the page"
227
250
  ]
@@ -260,10 +283,11 @@ export async function clickElement({ url, selector, text, waitForElementTimeout
260
283
  selector ? `Clicked element: ${selector}` : `Clicked element with text: "${text}"`,
261
284
  html,
262
285
  [
263
- "Use click_element again to navigate further",
264
- "Use type_text to fill forms if needed",
265
- "Use get_current_html to refresh page state",
266
- "Use close_tab when finished"
286
+ "Use MCPBrowser's click_element again to navigate further",
287
+ "Use MCPBrowser's type_text to fill forms if needed",
288
+ "Use MCPBrowser's get_current_html to refresh page state",
289
+ "Use MCPBrowser's take_screenshot if page has popups or visual content that's hard to parse from HTML",
290
+ "Use MCPBrowser's close_tab when finished"
267
291
  ]
268
292
  );
269
293
  } else {
@@ -285,20 +309,23 @@ export async function clickElement({ url, selector, text, waitForElementTimeout
285
309
  selector ? `Clicked element: ${selector}` : `Clicked element with text: "${text}"`,
286
310
  null,
287
311
  [
288
- "Use get_current_html to see updated page state",
289
- "Use click_element or type_text for more interactions",
290
- "Use close_tab when finished"
312
+ "Use MCPBrowser's get_current_html to see updated page state",
313
+ "Use MCPBrowser's take_screenshot if the page has popups, modals, or visual content",
314
+ "Use MCPBrowser's click_element or MCPBrowser's type_text for more interactions",
315
+ "Use MCPBrowser's close_tab when finished"
291
316
  ]
292
317
  );
293
318
  }
294
319
  } catch (err) {
295
320
  logger.error(`click_element failed: ${err.message}`);
296
- return new ErrorResponse(
321
+ return new InformationalResponse(
297
322
  `Failed to click element: ${err.message}`,
323
+ 'The element was found but could not be clicked. It may be covered by another element, not interactable, or the page may have changed.',
298
324
  [
299
- "Use get_current_html to check current page state",
325
+ "Use MCPBrowser's get_current_html to check current page state",
326
+ "Use MCPBrowser's take_screenshot to see what's visually blocking the element",
300
327
  "Verify the selector or text is correct",
301
- "Try fetch_webpage to reload if page is stale"
328
+ "Try MCPBrowser's fetch_webpage to reload if page is stale"
302
329
  ]
303
330
  );
304
331
  }
@@ -145,7 +145,7 @@ export async function closeTab({ url }) {
145
145
  'No open tab found for this hostname',
146
146
  hostname,
147
147
  [
148
- "Use fetch_webpage to open a new page if needed"
148
+ "Use MCPBrowser's fetch_webpage to open a new page if needed"
149
149
  ]
150
150
  );
151
151
  }
@@ -164,7 +164,7 @@ export async function closeTab({ url }) {
164
164
  'Tab was already closed',
165
165
  hostname,
166
166
  [
167
- "Use fetch_webpage to open a new page if needed"
167
+ "Use MCPBrowser's fetch_webpage to open a new page if needed"
168
168
  ]
169
169
  );
170
170
  }
@@ -181,7 +181,7 @@ export async function closeTab({ url }) {
181
181
  `Successfully closed tab for ${hostname}`,
182
182
  hostname,
183
183
  [
184
- "Use fetch_webpage to open a new page if needed"
184
+ "Use MCPBrowser's fetch_webpage to open a new page if needed"
185
185
  ]
186
186
  );
187
187
 
@@ -6,7 +6,7 @@
6
6
  import { getBrowser, domainPages } from '../core/browser.js';
7
7
  import { getOrCreatePage, queueRequest, navigateToUrl, waitForPageReady, extractAndProcessHtml, waitForPageStability } from '../core/page.js';
8
8
  import { detectRedirectType, waitForAutoAuth, waitForManualAuth } from '../core/auth.js';
9
- import { MCPResponse, ErrorResponse } from '../core/responses.js';
9
+ import { MCPResponse, ErrorResponse, HttpStatusResponse, InformationalResponse } from '../core/responses.js';
10
10
  import logger from '../core/logger.js';
11
11
 
12
12
  /**
@@ -156,14 +156,29 @@ async function doFetchPage({ url, hostname, browser, removeUnnecessaryHTML, post
156
156
  const authCompletionTimeout = 600000;
157
157
  const reuseLastKeptPage = true;
158
158
 
159
- const browserInstance = await getBrowser(browser);
159
+ // Ensure browser connection
160
+ let browserInstance;
161
+ try {
162
+ browserInstance = await getBrowser(browser);
163
+ } catch (err) {
164
+ logger.error(`fetch_webpage: Failed to connect to browser: ${err.message}`);
165
+ return new InformationalResponse(
166
+ `Browser connection failed: ${err.message}`,
167
+ 'The browser must be running with remote debugging enabled.',
168
+ [
169
+ 'Ensure the browser is installed and running',
170
+ 'Check that remote debugging is enabled (--remote-debugging-port)',
171
+ 'Try restarting the MCP server'
172
+ ]
173
+ );
174
+ }
160
175
 
161
176
  try {
162
177
  // Get or create page for this domain (simple - no locks needed)
163
178
  let page = await getOrCreatePage(browserInstance, hostname, reuseLastKeptPage);
164
179
 
165
180
  // Navigate to URL (pure navigation)
166
- await navigateToUrl(page, url, waitUntil, navigationTimeout);
181
+ const { statusCode, statusText } = await navigateToUrl(page, url, waitUntil, navigationTimeout);
167
182
 
168
183
  // Wait for page content to be ready (handles SPAs automatically)
169
184
  await waitForPageReady(page);
@@ -227,8 +242,8 @@ async function doFetchPage({ url, hostname, browser, removeUnnecessaryHTML, post
227
242
  manualAuthResult.error,
228
243
  [
229
244
  "Complete authentication in the browser window",
230
- "Call fetch_webpage again with the same URL to retry",
231
- "Use close_tab to reset the session if authentication fails"
245
+ "Call MCPBrowser's fetch_webpage again with the same URL to retry",
246
+ "Use MCPBrowser's close_tab to reset the session if authentication fails"
232
247
  ]
233
248
  );
234
249
  }
@@ -257,14 +272,26 @@ async function doFetchPage({ url, hostname, browser, removeUnnecessaryHTML, post
257
272
 
258
273
  logger.info(`fetch_webpage completed: ${page.url()}`);
259
274
 
275
+ // Check for non-2xx HTTP status codes - return informational response (not red error)
276
+ if (statusCode && (statusCode >= 400 && statusCode < 600)) {
277
+ logger.info(`HTTP ${statusCode} ${statusText} - returning as informational response`);
278
+ return new HttpStatusResponse(
279
+ page.url(),
280
+ statusCode,
281
+ statusText,
282
+ processedHtml
283
+ );
284
+ }
285
+
260
286
  return new FetchPageSuccessResponse(
261
287
  page.url(),
262
288
  processedHtml,
263
289
  [
264
- "Use click_element to interact with buttons/links on the page",
265
- "Use type_text to fill in form fields",
266
- "Use get_current_html to re-check page state after interactions",
267
- "Use close_tab when finished to free browser resources"
290
+ "Use MCPBrowser's click_element to interact with buttons/links on the page",
291
+ "Use MCPBrowser's type_text to fill in form fields",
292
+ "Use MCPBrowser's get_current_html to re-check page state after interactions",
293
+ "Use MCPBrowser's take_screenshot if page has charts, images, or complex visual layout that's hard to understand from HTML",
294
+ "Use MCPBrowser's close_tab when finished to free browser resources"
268
295
  ]
269
296
  );
270
297
  } catch (err) {
@@ -273,8 +300,8 @@ async function doFetchPage({ url, hostname, browser, removeUnnecessaryHTML, post
273
300
  err.message || String(err),
274
301
  [
275
302
  "Complete authentication in the browser if prompted",
276
- "Call fetch_webpage again with the same URL to retry",
277
- "Use close_tab to reset the session if needed"
303
+ "Call MCPBrowser's fetch_webpage again with the same URL to retry",
304
+ "Use MCPBrowser's close_tab to reset the session if needed"
278
305
  ]
279
306
  );
280
307
  }
@@ -2,9 +2,9 @@
2
2
  * get-current-html.js - Get current HTML from an already-loaded page
3
3
  */
4
4
 
5
- import { getBrowser, domainPages } from '../core/browser.js';
5
+ import { getBrowser, getValidatedPage } from '../core/browser.js';
6
6
  import { extractAndProcessHtml } from '../core/page.js';
7
- import { MCPResponse, ErrorResponse } from '../core/responses.js';
7
+ import { MCPResponse, InformationalResponse } from '../core/responses.js';
8
8
  import logger from '../core/logger.js';
9
9
 
10
10
  /**
@@ -113,15 +113,36 @@ export async function getCurrentHtml({ url, removeUnnecessaryHTML = true }) {
113
113
  throw new Error(`Invalid URL: ${url}`);
114
114
  }
115
115
 
116
- const browser = await getBrowser();
117
- let page = domainPages.get(hostname);
116
+ // Ensure browser connection (triggers domain map rebuild on reconnect)
117
+ try {
118
+ await getBrowser();
119
+ } catch (err) {
120
+ logger.error(`get_current_html: Failed to connect to browser: ${err.message}`);
121
+ return new InformationalResponse(
122
+ `Browser connection failed: ${err.message}`,
123
+ 'The browser must be running with remote debugging enabled.',
124
+ [
125
+ 'Ensure the browser is installed and running',
126
+ 'Check that remote debugging is enabled (--remote-debugging-port)',
127
+ 'Try restarting the MCP server'
128
+ ]
129
+ );
130
+ }
131
+
132
+ // Validate page exists and is usable
133
+ const { page, error: pageError } = await getValidatedPage(hostname);
118
134
 
119
- if (!page || page.isClosed()) {
120
- logger.error(`get_current_html: No open page found for ${hostname}`);
121
- return new ErrorResponse(
122
- `No open page found for ${hostname}. Please fetch the page first using fetch_webpage.`,
135
+ if (!page) {
136
+ const isConnectionLost = pageError && pageError.includes('connection');
137
+ logger.info(`get_current_html: ${pageError || 'No page found for ' + hostname}`);
138
+ return new InformationalResponse(
139
+ isConnectionLost ? `Page connection lost for ${hostname}` : `No open page found for ${hostname}`,
140
+ isConnectionLost
141
+ ? 'The browser tab was closed or the connection was lost. The page needs to be reloaded.'
142
+ : 'The page must be loaded before you can get its current HTML',
123
143
  [
124
- "Use fetch_webpage to load the page first"
144
+ "Use MCPBrowser's fetch_webpage tool to load the page first",
145
+ "Then retry MCPBrowser's get_current_html with the same URL"
125
146
  ]
126
147
  );
127
148
  }
@@ -136,18 +157,20 @@ export async function getCurrentHtml({ url, removeUnnecessaryHTML = true }) {
136
157
  currentUrl,
137
158
  html,
138
159
  [
139
- "Use click_element to interact with elements",
140
- "Use type_text to fill forms",
141
- "Use close_tab to free resources when done"
160
+ "Use MCPBrowser's click_element to interact with elements",
161
+ "Use MCPBrowser's type_text to fill forms",
162
+ "Use MCPBrowser's take_screenshot if page layout or visual content is hard to understand from HTML",
163
+ "Use MCPBrowser's close_tab to free resources when done"
142
164
  ]
143
165
  );
144
166
  } catch (err) {
145
167
  logger.error(`get_current_html failed: ${err.message}`);
146
- return new ErrorResponse(
168
+ return new InformationalResponse(
147
169
  `Failed to get HTML: ${err.message}`,
170
+ 'Could not extract HTML from the page. The page may have navigated away or the connection was lost.',
148
171
  [
149
- "Try fetch_webpage to reload the page",
150
- "Use close_tab and start fresh if needed"
172
+ "Try MCPBrowser's fetch_webpage to reload the page",
173
+ "Use MCPBrowser's close_tab and start fresh if needed"
151
174
  ]
152
175
  );
153
176
  }