brave-real-browser-mcp-server 2.0.6 → 2.0.8

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
@@ -184,6 +184,73 @@ complex web automation tasks with human-like behavior.
184
184
  ```
185
185
  - Install xvfb for headless operation: `sudo apt-get install -y xvfb`
186
186
 
187
+ ## 🚀 Auto-Update Feature
188
+
189
+ This package **automatically updates all dependencies to their latest versions** whenever you run `npm install` during development. This ensures you always have the most recent bug fixes, security patches, and feature improvements from the underlying Brave browser packages.
190
+
191
+ ### How it works:
192
+ - ✅ Automatically checks for outdated dependencies on every `npm install`
193
+ - ✅ Updates core Brave packages (`brave-real-browser`, `brave-real-launcher`, `brave-real-puppeteer-core`) with `--force`
194
+ - ✅ Updates other dependencies (`@modelcontextprotocol/sdk`, `turndown`) separately
195
+ - ✅ Works in CI/CD environments (GitHub Actions workflow)
196
+ - ✅ Can be disabled if needed via environment variable
197
+ - ✅ Smart recursion prevention to avoid loops
198
+
199
+ ### For End Users (Using NPX):
200
+ If you're using this package via `npx` (as recommended in Claude Desktop config), you don't need to worry about updates - `npx` with `@latest` always fetches the newest version automatically. This auto-update feature is primarily for developers.
201
+
202
+ ### For Developers:
203
+
204
+ **Auto-update is enabled by default:**
205
+ ```bash
206
+ npm install
207
+ ```
208
+ This will automatically update all dependencies to their latest versions.
209
+
210
+ **To disable auto-update temporarily:**
211
+ ```bash
212
+ # On Windows (PowerShell)
213
+ $env:SKIP_AUTO_UPDATE="true"; npm install
214
+
215
+ # On Windows (CMD)
216
+ set SKIP_AUTO_UPDATE=true && npm install
217
+
218
+ # On macOS/Linux
219
+ SKIP_AUTO_UPDATE=true npm install
220
+ ```
221
+
222
+ **Manual update commands:**
223
+ ```bash
224
+ # Check for outdated dependencies
225
+ npm run check-updates
226
+
227
+ # Update all core Brave packages
228
+ npm run update-brave-packages
229
+
230
+ # Update using ensure-latest script (with verification)
231
+ npm run ensure-latest-packages
232
+
233
+ # Run auto-update script manually
234
+ npm run upgrade-all
235
+
236
+ # Complete fresh install with latest packages
237
+ npm run fresh-install
238
+ ```
239
+
240
+ **In CI/CD:**
241
+ The GitHub Actions workflow automatically updates dependencies during the build process using the `ensure-latest-packages` script.
242
+
243
+ ### Why Auto-Update?
244
+
245
+ The underlying `brave-real-browser` ecosystem is actively maintained with frequent updates for:
246
+ - 🐛 Bug fixes
247
+ - 🔒 Security patches
248
+ - 🎯 Anti-detection improvements
249
+ - 🚀 New features
250
+ - 🌐 Browser compatibility updates
251
+
252
+ Auto-updates ensure you're always running the most stable and secure version.
253
+
187
254
  ## Platform-Specific Installation
188
255
 
189
256
  > **🎯 Choose Your Platform:** Select the installation method that matches your operating system. Each section includes complete setup commands for both Brave Browser and MCP server configuration.
@@ -2,6 +2,7 @@ import { connect } from 'brave-real-browser';
2
2
  import * as fs from 'fs';
3
3
  import * as path from 'path';
4
4
  import * as net from 'net';
5
+ import { categorizeError as categorizeErrorMCP, ErrorCategory as MCPErrorCategory } from './errors/index.js';
5
6
  // Browser error categorization
6
7
  export var BrowserErrorType;
7
8
  (function (BrowserErrorType) {
@@ -52,28 +53,31 @@ export async function initializeBrowserForTesting(options) {
52
53
  return await initializeBrowser(options);
53
54
  }
54
55
  let sessionValidationInProgress = false;
55
- // Error handling functions
56
+ // Error handling functions - now uses centralized error system
57
+ // Legacy function kept for backward compatibility
56
58
  export function categorizeError(error) {
57
- const message = error.message.toLowerCase();
58
- if (message.includes('navigating frame was detached')) {
59
- return BrowserErrorType.FRAME_DETACHED;
60
- }
61
- if (message.includes('session closed')) {
62
- return BrowserErrorType.SESSION_CLOSED;
63
- }
64
- if (message.includes('target closed')) {
65
- return BrowserErrorType.TARGET_CLOSED;
66
- }
67
- if (message.includes('protocol error')) {
68
- return BrowserErrorType.PROTOCOL_ERROR;
69
- }
70
- if (message.includes('navigation timeout') || message.includes('timeout')) {
71
- return BrowserErrorType.NAVIGATION_TIMEOUT;
72
- }
73
- if (message.includes('element not found') || message.includes('no node found')) {
74
- return BrowserErrorType.ELEMENT_NOT_FOUND;
59
+ const mcpError = categorizeErrorMCP(error);
60
+ // Map MCPError categories to BrowserErrorType for backward compatibility
61
+ switch (mcpError.category) {
62
+ case MCPErrorCategory.FRAME_DETACHED:
63
+ return BrowserErrorType.FRAME_DETACHED;
64
+ case MCPErrorCategory.SESSION_CLOSED:
65
+ return BrowserErrorType.SESSION_CLOSED;
66
+ case MCPErrorCategory.TARGET_CLOSED:
67
+ return BrowserErrorType.TARGET_CLOSED;
68
+ case MCPErrorCategory.PROTOCOL_ERROR:
69
+ return BrowserErrorType.PROTOCOL_ERROR;
70
+ case MCPErrorCategory.NAVIGATION_TIMEOUT:
71
+ return BrowserErrorType.NAVIGATION_TIMEOUT;
72
+ case MCPErrorCategory.ELEMENT_NOT_FOUND:
73
+ return BrowserErrorType.ELEMENT_NOT_FOUND;
74
+ default:
75
+ return BrowserErrorType.UNKNOWN;
75
76
  }
76
- return BrowserErrorType.UNKNOWN;
77
+ }
78
+ // New function that returns MCPError for better error handling
79
+ export function categorizeBrowserError(error) {
80
+ return categorizeErrorMCP(error);
77
81
  }
78
82
  // Timeout wrapper for operations that may hang
79
83
  export async function withTimeout(operation, timeoutMs, context = 'unknown') {
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Constants for Brave Real Browser MCP Server
3
+ * Centralizes all magic numbers and configuration values
4
+ */
5
+ // Timeout configurations (in milliseconds)
6
+ export const TIMEOUTS = {
7
+ NAVIGATION: 30000, // 30 seconds for page navigation
8
+ ELEMENT_WAIT: 10000, // 10 seconds to wait for elements
9
+ BROWSER_LAUNCH: 60000, // 60 seconds for browser launch
10
+ CAPTCHA_SOLVE: 300000, // 5 minutes for captcha solving
11
+ SCRIPT_EXECUTION: 30000, // 30 seconds for script execution
12
+ PAGE_LOAD: 30000, // 30 seconds for page load
13
+ };
14
+ // Retry configurations
15
+ export const RETRIES = {
16
+ MAX_ATTEMPTS: 3,
17
+ BACKOFF_MULTIPLIER: 2,
18
+ INITIAL_DELAY: 1000, // 1 second initial delay
19
+ MAX_DELAY: 10000, // 10 seconds max delay
20
+ };
21
+ // Browser configurations
22
+ export const BROWSER = {
23
+ DEFAULT_VIEWPORT: {
24
+ width: 1920,
25
+ height: 1080,
26
+ },
27
+ USER_AGENT: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
28
+ DEFAULT_ARGS: [
29
+ '--no-sandbox',
30
+ '--disable-setuid-sandbox',
31
+ '--disable-dev-shm-usage',
32
+ '--disable-blink-features=AutomationControlled',
33
+ ],
34
+ };
35
+ // Content extraction limits
36
+ export const CONTENT = {
37
+ MAX_TEXT_LENGTH: 1000000, // 1MB max text content
38
+ MAX_HTML_LENGTH: 5000000, // 5MB max HTML content
39
+ MAX_SCREENSHOT_SIZE: 10000000, // 10MB max screenshot
40
+ MARKDOWN_MAX_LENGTH: 2000000, // 2MB max markdown
41
+ };
42
+ // Selector timeouts
43
+ export const SELECTORS = {
44
+ WAIT_TIMEOUT: 10000, // 10 seconds
45
+ POLL_INTERVAL: 100, // 100ms polling
46
+ MAX_RETRIES: 3,
47
+ };
48
+ // File operation limits
49
+ export const FILES = {
50
+ MAX_FILE_SIZE: 50000000, // 50MB max file size
51
+ ALLOWED_EXTENSIONS: ['.md', '.txt', '.html', '.json'],
52
+ TEMP_DIR: '.temp',
53
+ };
54
+ // Error codes
55
+ export const ERROR_CODES = {
56
+ // Browser errors (1xxx)
57
+ BROWSER_NOT_INITIALIZED: 'MCP_1001',
58
+ BROWSER_LAUNCH_FAILED: 'MCP_1002',
59
+ BROWSER_CONNECTION_LOST: 'MCP_1003',
60
+ // Navigation errors (2xxx)
61
+ NAVIGATION_FAILED: 'MCP_2001',
62
+ NAVIGATION_TIMEOUT: 'MCP_2002',
63
+ PAGE_NOT_FOUND: 'MCP_2003',
64
+ // Element errors (3xxx)
65
+ ELEMENT_NOT_FOUND: 'MCP_3001',
66
+ ELEMENT_NOT_VISIBLE: 'MCP_3002',
67
+ ELEMENT_NOT_CLICKABLE: 'MCP_3003',
68
+ // Content errors (4xxx)
69
+ CONTENT_TOO_LARGE: 'MCP_4001',
70
+ CONTENT_EXTRACTION_FAILED: 'MCP_4002',
71
+ INVALID_SELECTOR: 'MCP_4003',
72
+ // File errors (5xxx)
73
+ FILE_NOT_FOUND: 'MCP_5001',
74
+ FILE_TOO_LARGE: 'MCP_5002',
75
+ FILE_WRITE_FAILED: 'MCP_5003',
76
+ INVALID_FILE_TYPE: 'MCP_5004',
77
+ // General errors (9xxx)
78
+ UNKNOWN_ERROR: 'MCP_9001',
79
+ INVALID_ARGUMENTS: 'MCP_9002',
80
+ OPERATION_TIMEOUT: 'MCP_9003',
81
+ };
82
+ // HTTP status codes for common scenarios
83
+ export const HTTP_STATUS = {
84
+ OK: 200,
85
+ MOVED_PERMANENTLY: 301,
86
+ FOUND: 302,
87
+ BAD_REQUEST: 400,
88
+ UNAUTHORIZED: 401,
89
+ FORBIDDEN: 403,
90
+ NOT_FOUND: 404,
91
+ TIMEOUT: 408,
92
+ INTERNAL_ERROR: 500,
93
+ BAD_GATEWAY: 502,
94
+ SERVICE_UNAVAILABLE: 503,
95
+ };
96
+ // Logging levels
97
+ export const LOG_LEVELS = {
98
+ DEBUG: 0,
99
+ INFO: 1,
100
+ WARN: 2,
101
+ ERROR: 3,
102
+ SILENT: 4,
103
+ };
104
+ // MCP Server configuration
105
+ export const MCP_CONFIG = {
106
+ SERVER_NAME: 'brave-real-browser-mcp-server',
107
+ SERVER_VERSION: '2.0.0',
108
+ PROTOCOL_VERSION: '2024-11-05',
109
+ MAX_TOOLS: 50,
110
+ MAX_RESOURCES: 100,
111
+ };
112
+ // Performance monitoring thresholds
113
+ export const PERFORMANCE = {
114
+ SLOW_OPERATION_THRESHOLD: 5000, // 5 seconds
115
+ MEMORY_WARNING_THRESHOLD: 500 * 1024 * 1024, // 500MB
116
+ CPU_WARNING_THRESHOLD: 80, // 80%
117
+ };
118
+ // Type guards for constants
119
+ export function isValidErrorCode(code) {
120
+ return code in ERROR_CODES;
121
+ }
122
+ export function isValidTimeout(timeout) {
123
+ return timeout > 0 && timeout <= 300000; // Max 5 minutes
124
+ }
125
+ export function isValidRetryCount(count) {
126
+ return count >= 0 && count <= 10;
127
+ }
@@ -0,0 +1,385 @@
1
+ /**
2
+ * Centralized Error Handling System for MCP Server
3
+ *
4
+ * This module provides a comprehensive error handling framework with:
5
+ * - Custom error classes for different error categories
6
+ * - Error factory functions for consistent error creation
7
+ * - Error recovery strategies
8
+ * - Error serialization for MCP protocol
9
+ */
10
+ // ============================================================================
11
+ // ERROR CATEGORIES
12
+ // ============================================================================
13
+ export var ErrorCategory;
14
+ (function (ErrorCategory) {
15
+ // Browser-related errors
16
+ ErrorCategory["BROWSER_NOT_INITIALIZED"] = "BROWSER_NOT_INITIALIZED";
17
+ ErrorCategory["BROWSER_INITIALIZATION_FAILED"] = "BROWSER_INITIALIZATION_FAILED";
18
+ ErrorCategory["BROWSER_CONNECTION_FAILED"] = "BROWSER_CONNECTION_FAILED";
19
+ ErrorCategory["BROWSER_CLOSED"] = "BROWSER_CLOSED";
20
+ // Navigation errors
21
+ ErrorCategory["NAVIGATION_FAILED"] = "NAVIGATION_FAILED";
22
+ ErrorCategory["NAVIGATION_TIMEOUT"] = "NAVIGATION_TIMEOUT";
23
+ ErrorCategory["PAGE_LOAD_FAILED"] = "PAGE_LOAD_FAILED";
24
+ // Element interaction errors
25
+ ErrorCategory["ELEMENT_NOT_FOUND"] = "ELEMENT_NOT_FOUND";
26
+ ErrorCategory["ELEMENT_NOT_INTERACTABLE"] = "ELEMENT_NOT_INTERACTABLE";
27
+ ErrorCategory["SELECTOR_INVALID"] = "SELECTOR_INVALID";
28
+ ErrorCategory["CLICK_FAILED"] = "CLICK_FAILED";
29
+ ErrorCategory["TYPE_FAILED"] = "TYPE_FAILED";
30
+ // Content retrieval errors
31
+ ErrorCategory["CONTENT_RETRIEVAL_FAILED"] = "CONTENT_RETRIEVAL_FAILED";
32
+ ErrorCategory["SCREENSHOT_FAILED"] = "SCREENSHOT_FAILED";
33
+ // File operation errors
34
+ ErrorCategory["FILE_NOT_FOUND"] = "FILE_NOT_FOUND";
35
+ ErrorCategory["FILE_READ_ERROR"] = "FILE_READ_ERROR";
36
+ ErrorCategory["FILE_WRITE_ERROR"] = "FILE_WRITE_ERROR";
37
+ ErrorCategory["INVALID_FILE_PATH"] = "INVALID_FILE_PATH";
38
+ // Validation errors
39
+ ErrorCategory["INVALID_INPUT"] = "INVALID_INPUT";
40
+ ErrorCategory["VALIDATION_FAILED"] = "VALIDATION_FAILED";
41
+ ErrorCategory["WORKFLOW_VIOLATION"] = "WORKFLOW_VIOLATION";
42
+ // Protocol errors
43
+ ErrorCategory["FRAME_DETACHED"] = "FRAME_DETACHED";
44
+ ErrorCategory["SESSION_CLOSED"] = "SESSION_CLOSED";
45
+ ErrorCategory["TARGET_CLOSED"] = "TARGET_CLOSED";
46
+ ErrorCategory["PROTOCOL_ERROR"] = "PROTOCOL_ERROR";
47
+ // System errors
48
+ ErrorCategory["NETWORK_ERROR"] = "NETWORK_ERROR";
49
+ ErrorCategory["TIMEOUT_ERROR"] = "TIMEOUT_ERROR";
50
+ ErrorCategory["UNKNOWN_ERROR"] = "UNKNOWN_ERROR";
51
+ })(ErrorCategory || (ErrorCategory = {}));
52
+ // ============================================================================
53
+ // ERROR SEVERITY LEVELS
54
+ // ============================================================================
55
+ export var ErrorSeverity;
56
+ (function (ErrorSeverity) {
57
+ ErrorSeverity["LOW"] = "LOW";
58
+ ErrorSeverity["MEDIUM"] = "MEDIUM";
59
+ ErrorSeverity["HIGH"] = "HIGH";
60
+ ErrorSeverity["CRITICAL"] = "CRITICAL";
61
+ })(ErrorSeverity || (ErrorSeverity = {}));
62
+ // ============================================================================
63
+ // CUSTOM ERROR CLASSES
64
+ // ============================================================================
65
+ /**
66
+ * Base error class for all MCP Server errors
67
+ */
68
+ export class MCPError extends Error {
69
+ category;
70
+ severity;
71
+ isRecoverable;
72
+ timestamp;
73
+ context;
74
+ suggestedAction;
75
+ constructor(message, category, severity, isRecoverable = false, context, suggestedAction) {
76
+ super(message);
77
+ this.name = 'MCPError';
78
+ this.category = category;
79
+ this.severity = severity;
80
+ this.isRecoverable = isRecoverable;
81
+ this.timestamp = new Date();
82
+ this.context = context;
83
+ this.suggestedAction = suggestedAction;
84
+ // Maintains proper stack trace for where our error was thrown (only available on V8)
85
+ if (Error.captureStackTrace) {
86
+ Error.captureStackTrace(this, this.constructor);
87
+ }
88
+ }
89
+ /**
90
+ * Serialize error for MCP protocol response
91
+ */
92
+ toMCPResponse() {
93
+ let errorText = `❌ Error: ${this.message}\n\n`;
94
+ errorText += `📋 Category: ${this.category}\n`;
95
+ errorText += `⚠️ Severity: ${this.severity}\n`;
96
+ if (this.suggestedAction) {
97
+ errorText += `\n💡 Suggested Action:\n${this.suggestedAction}\n`;
98
+ }
99
+ if (this.context && Object.keys(this.context).length > 0) {
100
+ errorText += `\n🔍 Context:\n${JSON.stringify(this.context, null, 2)}\n`;
101
+ }
102
+ return {
103
+ content: [
104
+ {
105
+ type: 'text',
106
+ text: errorText,
107
+ },
108
+ ],
109
+ };
110
+ }
111
+ }
112
+ /**
113
+ * Browser initialization and lifecycle errors
114
+ */
115
+ export class BrowserError extends MCPError {
116
+ constructor(message, category = ErrorCategory.BROWSER_INITIALIZATION_FAILED, context, suggestedAction) {
117
+ const severity = category === ErrorCategory.BROWSER_NOT_INITIALIZED
118
+ ? ErrorSeverity.HIGH
119
+ : ErrorSeverity.CRITICAL;
120
+ const isRecoverable = category === ErrorCategory.BROWSER_NOT_INITIALIZED;
121
+ super(message, category, severity, isRecoverable, context, suggestedAction);
122
+ this.name = 'BrowserError';
123
+ }
124
+ }
125
+ /**
126
+ * Navigation-related errors
127
+ */
128
+ export class NavigationError extends MCPError {
129
+ constructor(message, category = ErrorCategory.NAVIGATION_FAILED, context, suggestedAction) {
130
+ const severity = category === ErrorCategory.NAVIGATION_TIMEOUT
131
+ ? ErrorSeverity.MEDIUM
132
+ : ErrorSeverity.HIGH;
133
+ super(message, category, severity, true, context, suggestedAction);
134
+ this.name = 'NavigationError';
135
+ }
136
+ }
137
+ /**
138
+ * Element interaction errors
139
+ */
140
+ export class InteractionError extends MCPError {
141
+ constructor(message, category = ErrorCategory.ELEMENT_NOT_FOUND, context, suggestedAction) {
142
+ super(message, category, ErrorSeverity.MEDIUM, true, context, suggestedAction);
143
+ this.name = 'InteractionError';
144
+ }
145
+ }
146
+ /**
147
+ * Content retrieval errors
148
+ */
149
+ export class ContentError extends MCPError {
150
+ constructor(message, category = ErrorCategory.CONTENT_RETRIEVAL_FAILED, context, suggestedAction) {
151
+ super(message, category, ErrorSeverity.MEDIUM, true, context, suggestedAction);
152
+ this.name = 'ContentError';
153
+ }
154
+ }
155
+ /**
156
+ * File operation errors
157
+ */
158
+ export class FileError extends MCPError {
159
+ constructor(message, category = ErrorCategory.FILE_WRITE_ERROR, context, suggestedAction) {
160
+ const severity = category === ErrorCategory.INVALID_FILE_PATH
161
+ ? ErrorSeverity.HIGH
162
+ : ErrorSeverity.MEDIUM;
163
+ super(message, category, severity, false, context, suggestedAction);
164
+ this.name = 'FileError';
165
+ }
166
+ }
167
+ /**
168
+ * Validation errors
169
+ */
170
+ export class ValidationError extends MCPError {
171
+ constructor(message, category = ErrorCategory.VALIDATION_FAILED, context, suggestedAction) {
172
+ super(message, category, ErrorSeverity.HIGH, false, context, suggestedAction);
173
+ this.name = 'ValidationError';
174
+ }
175
+ }
176
+ /**
177
+ * Protocol-level errors
178
+ */
179
+ export class ProtocolError extends MCPError {
180
+ constructor(message, category = ErrorCategory.PROTOCOL_ERROR, context, suggestedAction) {
181
+ const severity = [
182
+ ErrorCategory.FRAME_DETACHED,
183
+ ErrorCategory.SESSION_CLOSED,
184
+ ErrorCategory.TARGET_CLOSED
185
+ ].includes(category) ? ErrorSeverity.CRITICAL : ErrorSeverity.HIGH;
186
+ const isRecoverable = category === ErrorCategory.FRAME_DETACHED;
187
+ super(message, category, severity, isRecoverable, context, suggestedAction);
188
+ this.name = 'ProtocolError';
189
+ }
190
+ }
191
+ // ============================================================================
192
+ // ERROR FACTORY FUNCTIONS
193
+ // ============================================================================
194
+ /**
195
+ * Create browser not initialized error
196
+ */
197
+ export function createBrowserNotInitializedError() {
198
+ return new BrowserError('Browser is not initialized', ErrorCategory.BROWSER_NOT_INITIALIZED, {}, 'Please call browser_init first to initialize the browser instance.');
199
+ }
200
+ /**
201
+ * Create browser initialization failed error
202
+ */
203
+ export function createBrowserInitializationError(originalError, context) {
204
+ return new BrowserError(`Failed to initialize browser: ${originalError.message}`, ErrorCategory.BROWSER_INITIALIZATION_FAILED, { originalError: originalError.message, ...context }, 'Check browser installation and ensure all dependencies are installed. Try running with different configuration options.');
205
+ }
206
+ /**
207
+ * Create navigation error
208
+ */
209
+ export function createNavigationError(url, originalError) {
210
+ return new NavigationError(`Failed to navigate to ${url}: ${originalError.message}`, ErrorCategory.NAVIGATION_FAILED, { url, originalError: originalError.message }, 'Verify the URL is correct and accessible. Check network connectivity.');
211
+ }
212
+ /**
213
+ * Create navigation timeout error
214
+ */
215
+ export function createNavigationTimeoutError(url, timeout) {
216
+ return new NavigationError(`Navigation to ${url} timed out after ${timeout}ms`, ErrorCategory.NAVIGATION_TIMEOUT, { url, timeout }, 'Try increasing the timeout value or use a different waitUntil option.');
217
+ }
218
+ /**
219
+ * Create element not found error
220
+ */
221
+ export function createElementNotFoundError(selector, context) {
222
+ return new InteractionError(`Element not found: ${selector}`, ErrorCategory.ELEMENT_NOT_FOUND, { selector, ...context }, 'Use get_content to analyze the page first, then use find_selector to locate elements by text content.');
223
+ }
224
+ /**
225
+ * Create element not interactable error
226
+ */
227
+ export function createElementNotInteractableError(selector, reason) {
228
+ return new InteractionError(`Element not interactable: ${selector}. Reason: ${reason}`, ErrorCategory.ELEMENT_NOT_INTERACTABLE, { selector, reason }, 'Wait for the element to become visible and enabled using the wait tool.');
229
+ }
230
+ /**
231
+ * Create invalid selector error
232
+ */
233
+ export function createInvalidSelectorError(selector, originalError) {
234
+ return new InteractionError(`Invalid CSS selector: ${selector}. ${originalError.message}`, ErrorCategory.SELECTOR_INVALID, { selector, originalError: originalError.message }, 'Ensure the selector follows valid CSS syntax. Use find_selector to generate selectors from text content.');
235
+ }
236
+ /**
237
+ * Create content retrieval error
238
+ */
239
+ export function createContentRetrievalError(originalError, context) {
240
+ return new ContentError(`Failed to retrieve content: ${originalError.message}`, ErrorCategory.CONTENT_RETRIEVAL_FAILED, { originalError: originalError.message, ...context }, 'Ensure the page is fully loaded. Try waiting for specific elements before retrieving content.');
241
+ }
242
+ /**
243
+ * Create file operation error
244
+ */
245
+ export function createFileWriteError(filePath, originalError) {
246
+ return new FileError(`Failed to write file ${filePath}: ${originalError.message}`, ErrorCategory.FILE_WRITE_ERROR, { filePath, originalError: originalError.message }, 'Check file permissions and ensure the directory exists.');
247
+ }
248
+ /**
249
+ * Create invalid file path error
250
+ */
251
+ export function createInvalidFilePathError(filePath, reason) {
252
+ return new FileError(`Invalid file path: ${filePath}. ${reason}`, ErrorCategory.INVALID_FILE_PATH, { filePath, reason }, 'Ensure the file path is absolute and uses the correct format for your operating system.');
253
+ }
254
+ /**
255
+ * Create validation error
256
+ */
257
+ export function createValidationError(fieldName, value, expectedType) {
258
+ return new ValidationError(`Validation failed for field '${fieldName}': expected ${expectedType}, got ${typeof value}`, ErrorCategory.INVALID_INPUT, { fieldName, value, expectedType }, `Provide a valid ${expectedType} value for ${fieldName}.`);
259
+ }
260
+ /**
261
+ * Create workflow violation error
262
+ */
263
+ export function createWorkflowViolationError(toolName, currentState, requiredState) {
264
+ return new ValidationError(`Workflow violation: Cannot execute '${toolName}' in current state '${currentState}'`, ErrorCategory.WORKFLOW_VIOLATION, { toolName, currentState, requiredState }, `Execute the following steps first: ${requiredState}`);
265
+ }
266
+ /**
267
+ * Create protocol error from puppeteer error
268
+ */
269
+ export function createProtocolErrorFromPuppeteer(originalError) {
270
+ const message = originalError.message.toLowerCase();
271
+ let category = ErrorCategory.PROTOCOL_ERROR;
272
+ let suggestedAction = 'Try refreshing the page or reinitializing the browser.';
273
+ if (message.includes('frame') && message.includes('detached')) {
274
+ category = ErrorCategory.FRAME_DETACHED;
275
+ suggestedAction = 'The page frame was detached. Navigate to a new page or refresh.';
276
+ }
277
+ else if (message.includes('session closed')) {
278
+ category = ErrorCategory.SESSION_CLOSED;
279
+ suggestedAction = 'Browser session was closed. Reinitialize the browser using browser_init.';
280
+ }
281
+ else if (message.includes('target closed')) {
282
+ category = ErrorCategory.TARGET_CLOSED;
283
+ suggestedAction = 'Browser target was closed. Reinitialize the browser using browser_init.';
284
+ }
285
+ return new ProtocolError(originalError.message, category, { originalError: originalError.message }, suggestedAction);
286
+ }
287
+ // ============================================================================
288
+ // ERROR CATEGORIZATION UTILITIES
289
+ // ============================================================================
290
+ /**
291
+ * Categorize a generic error into appropriate MCP error
292
+ */
293
+ export function categorizeError(error) {
294
+ // Already an MCP error
295
+ if (error instanceof MCPError) {
296
+ return error;
297
+ }
298
+ // Convert to Error if not already
299
+ const err = error instanceof Error ? error : new Error(String(error));
300
+ const message = err.message.toLowerCase();
301
+ // Browser errors
302
+ if (message.includes('browser not initialized')) {
303
+ return createBrowserNotInitializedError();
304
+ }
305
+ // Navigation errors
306
+ if (message.includes('navigation') || message.includes('navigate')) {
307
+ if (message.includes('timeout')) {
308
+ return new NavigationError(err.message, ErrorCategory.NAVIGATION_TIMEOUT);
309
+ }
310
+ return new NavigationError(err.message, ErrorCategory.NAVIGATION_FAILED);
311
+ }
312
+ // Protocol errors
313
+ if (message.includes('frame') && message.includes('detached')) {
314
+ return createProtocolErrorFromPuppeteer(err);
315
+ }
316
+ if (message.includes('session closed') || message.includes('target closed')) {
317
+ return createProtocolErrorFromPuppeteer(err);
318
+ }
319
+ // Element errors
320
+ if (message.includes('element') || message.includes('selector')) {
321
+ if (message.includes('not found') || message.includes('no node')) {
322
+ return new InteractionError(err.message, ErrorCategory.ELEMENT_NOT_FOUND);
323
+ }
324
+ if (message.includes('not visible') || message.includes('not interactable')) {
325
+ return new InteractionError(err.message, ErrorCategory.ELEMENT_NOT_INTERACTABLE);
326
+ }
327
+ }
328
+ // File errors
329
+ if (message.includes('enoent') || message.includes('file not found')) {
330
+ return new FileError(err.message, ErrorCategory.FILE_NOT_FOUND);
331
+ }
332
+ if (message.includes('eacces') || message.includes('permission')) {
333
+ return new FileError(err.message, ErrorCategory.FILE_WRITE_ERROR);
334
+ }
335
+ // Network errors
336
+ if (message.includes('network') || message.includes('econnrefused') || message.includes('enotfound')) {
337
+ return new MCPError(err.message, ErrorCategory.NETWORK_ERROR, ErrorSeverity.HIGH, true, { originalError: err.message }, 'Check network connectivity and firewall settings.');
338
+ }
339
+ // Timeout errors
340
+ if (message.includes('timeout')) {
341
+ return new MCPError(err.message, ErrorCategory.TIMEOUT_ERROR, ErrorSeverity.MEDIUM, true, { originalError: err.message }, 'Try increasing timeout values or check if the operation is blocking.');
342
+ }
343
+ // Default unknown error
344
+ return new MCPError(err.message, ErrorCategory.UNKNOWN_ERROR, ErrorSeverity.MEDIUM, false, { originalError: err.message, stack: err.stack });
345
+ }
346
+ // ============================================================================
347
+ // ERROR RECOVERY UTILITIES
348
+ // ============================================================================
349
+ /**
350
+ * Determine if an error is recoverable
351
+ */
352
+ export function isRecoverableError(error) {
353
+ if (error instanceof MCPError) {
354
+ return error.isRecoverable;
355
+ }
356
+ const categorized = categorizeError(error);
357
+ return categorized.isRecoverable;
358
+ }
359
+ /**
360
+ * Get recovery strategy for an error
361
+ */
362
+ export function getRecoveryStrategy(error) {
363
+ if (!error.isRecoverable) {
364
+ return null;
365
+ }
366
+ switch (error.category) {
367
+ case ErrorCategory.BROWSER_NOT_INITIALIZED:
368
+ return 'initialize_browser';
369
+ case ErrorCategory.NAVIGATION_TIMEOUT:
370
+ case ErrorCategory.NAVIGATION_FAILED:
371
+ return 'retry_navigation';
372
+ case ErrorCategory.ELEMENT_NOT_FOUND:
373
+ return 'use_self_healing_locators';
374
+ case ErrorCategory.FRAME_DETACHED:
375
+ return 'refresh_page';
376
+ case ErrorCategory.TIMEOUT_ERROR:
377
+ return 'retry_with_increased_timeout';
378
+ default:
379
+ return 'retry_operation';
380
+ }
381
+ }
382
+ // ============================================================================
383
+ // EXPORTS
384
+ // ============================================================================
385
+ export { MCPError as default, };