UNPKG

insite-mcp

Version:

MCP server for browser automation with 52 tools using Playwright

1,434 lines (1,433 loc) 66.8 kB
#!/usr/bin/env node /** * InSite Server - Main entry point */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import { BrowserManager } from './browser-manager.js'; import { registerTools } from './tools/index.js'; import { BrowserAutomationError } from './types.js'; import path from 'path'; import os from 'os'; /** * InSite Server class */ class InSiteServer { server; browserManager; tempDir; constructor() { this.server = new Server({ name: 'insite-server', version: '0.2.0', }, { capabilities: { tools: {}, }, }); this.browserManager = BrowserManager.getInstance({ headless: process.env['HEADLESS'] !== 'false', viewport: { width: parseInt(process.env['VIEWPORT_WIDTH'] ?? '1280'), height: parseInt(process.env['VIEWPORT_HEIGHT'] ?? '720'), }, timeout: parseInt(process.env['TIMEOUT'] ?? '30000'), }); this.tempDir = path.join(os.tmpdir(), 'insite-screenshots'); this.setupHandlers(); } /** * Set up MCP request handlers */ setupHandlers() { // List available tools this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: registerTools(), }; }); // Handle tool calls with comprehensive error handling this.server.setRequestHandler(CallToolRequestSchema, async (request) => { try { // Validate request structure if (!request.params || !request.params.name) { return this.createSafeErrorResponse('INVALID_REQUEST', 'Missing tool name in request'); } // Add timeout to prevent hanging const timeoutPromise = new Promise((_, reject) => { setTimeout(() => reject(new Error('Tool call timeout')), 60000); // 60 second timeout }); const toolCallPromise = this.handleToolCall(request); const result = await Promise.race([toolCallPromise, timeoutPromise]); // Ensure result is properly formatted if (!result || typeof result !== 'object') { return this.createSafeErrorResponse('INVALID_RESPONSE', 'Tool returned invalid response format'); } return result; } catch (error) { console.error('Tool call error:', error); // Always return a safe, well-formed response if (error instanceof BrowserAutomationError) { return this.createSafeErrorResponse(error.type, error.message, error.details); } // Handle timeout specifically if (error && error.message === 'Tool call timeout') { return this.createSafeErrorResponse('TIMEOUT_ERROR', 'Tool call exceeded maximum execution time'); } // Catch-all for unexpected errors const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; return this.createSafeErrorResponse('JAVASCRIPT_ERROR', errorMessage); } }); } /** * Create a safe, well-formed error response that won't break Claude */ createSafeErrorResponse(errorType, message, details) { return { content: [ { type: 'text', text: JSON.stringify({ success: false, error: { type: errorType, message: message, ...(details ? { details } : {}), timestamp: new Date().toISOString(), }, }), }, ], }; } /** * Handle individual tool calls */ async handleToolCall(request) { const { name, arguments: args } = request.params; // Ensure browser is initialized for all operations except close_browser if (name !== 'close_browser' && !this.browserManager.isInitialized()) { try { await this.browserManager.initialize(); } catch (initError) { console.error('Browser initialization failed:', initError); return this.createSafeErrorResponse('BROWSER_INIT_ERROR', 'Failed to initialize browser', { originalError: initError instanceof Error ? initError.message : 'Unknown error' }); } } // Add browser health check for critical operations if (['load_page', 'screenshot', 'click_element', 'type_text'].includes(name)) { if (!this.browserManager.getCurrentUrl() && name !== 'load_page') { return this.createSafeErrorResponse('BROWSER_STATE_ERROR', 'No page loaded. Use load_page first.', { suggestions: ['Call load_page with a URL before performing page interactions'] }); } } let result; switch (name) { case 'load_page': result = await this.loadPage(args); break; case 'screenshot': result = await this.takeScreenshot(args); break; case 'scroll_to_element_and_screenshot': result = await this.scrollToElementAndScreenshot(args); break; case 'capture_full_scrollable_page': result = await this.captureFullScrollablePage(args); break; case 'get_current_url': result = await this.getCurrentUrl(); break; case 'close_browser': result = await this.closeBrowser(); break; case 'get_viewport_info': result = await this.getViewportInfo(); break; case 'get_dom': result = await this.getDOMContent(args); break; case 'get_page_title': result = await this.getPageTitle(); break; case 'click_element': result = await this.clickElement(args); break; case 'type_text': result = await this.typeText(args); break; case 'hover_element': result = await this.hoverElement(args); break; case 'scroll_page': result = await this.scrollPage(args); break; case 'press_key': result = await this.pressKey(args); break; case 'wait_for_element': result = await this.waitForElement(args); break; case 'wait_for_navigation': result = await this.waitForNavigation(args); break; case 'go_back': result = await this.goBack(args); break; case 'go_forward': result = await this.goForward(args); break; case 'reload_page': result = await this.reloadPage(args); break; case 'evaluate_js': result = await this.evaluateJS(args); break; case 'evaluate_js_on_element': result = await this.evaluateJSOnElement(args); break; case 'get_element_info': result = await this.getElementInfo(args); break; case 'get_console_logs': result = await this.getConsoleLogs(args); break; case 'clear_console_logs': result = await this.clearConsoleLogs(); break; case 'get_network_logs': result = await this.getNetworkLogs(args); break; case 'clear_network_logs': result = await this.clearNetworkLogs(); break; case 'set_viewport_size': result = await this.setViewportSize(args); break; case 'set_user_agent': result = await this.setUserAgent(args); break; case 'set_geolocation': result = await this.setGeolocation(args); break; case 'switch_browser': result = await this.switchBrowser(args); break; case 'get_browser_info': result = await this.getBrowserInfo(); break; case 'list_available_browsers': result = await this.listAvailableBrowsers(); break; // Advanced Debugging Tools case 'highlight_element': result = await this.highlightElement(args); break; case 'trace_execution': result = await this.traceExecution(args); break; case 'capture_performance_timeline': result = await this.capturePerformanceTimeline(args); break; case 'debug_mode': result = await this.enableDebugMode(args); break; // Security & Validation Tools case 'handle_csp': result = await this.handleCSP(args); break; case 'manage_certificates': result = await this.manageCertificates(args); break; case 'validate_security': result = await this.validateSecurity(args); break; // Advanced Monitoring & Analytics Tools case 'usage_analytics': result = await this.manageUsageAnalytics(args); break; case 'error_tracking': result = await this.manageErrorTracking(args); break; case 'performance_monitoring': result = await this.managePerformanceMonitoring(args); break; case 'session_analytics': result = await this.manageSessionAnalytics(args); break; case 'playwright_test_adapter': result = await this.handlePlaywrightTestAdapter(args); break; case 'jest_adapter': result = await this.handleJestAdapter(args); break; case 'mocha_adapter': result = await this.handleMochaAdapter(args); break; case 'test_reporter': result = await this.handleTestReporter(args); break; default: result = { success: false, error: { type: 'JAVASCRIPT_ERROR', message: `Unknown tool: ${name}`, details: { toolName: name }, }, }; } // Ensure result is safely formatted try { const jsonString = JSON.stringify(result); return { content: [ { type: 'text', text: jsonString, }, ], }; } catch (jsonError) { console.error('JSON serialization error:', jsonError); return this.createSafeErrorResponse('SERIALIZATION_ERROR', 'Failed to serialize tool result'); } } /** * Load page tool implementation */ async loadPage(args) { try { // Ensure browser is initialized before navigation if (!this.browserManager.isInitialized()) { await this.browserManager.initialize(); } const waitCondition = args.waitUntil ?? 'domcontentloaded'; await this.browserManager.navigateToUrl(args.url, waitCondition); return { success: true, data: { url: args.url, currentUrl: this.browserManager.getCurrentUrl(), }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * Screenshot tool implementation */ async takeScreenshot(args) { try { const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const filename = `screenshot-${timestamp}.png`; const filePath = `./temp/${filename}`; const savedPath = await this.browserManager.takeScreenshot({ fullPage: args.fullPage ?? true, ...(args.quality ? { quality: args.quality } : {}), format: 'png', filePath, }); return { success: true, data: { filePath: savedPath, filename, fullPage: args.fullPage ?? true, }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * Get current URL tool implementation */ async getCurrentUrl() { try { const url = this.browserManager.getCurrentUrl(); return { success: true, data: { url }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * Close browser tool implementation */ async closeBrowser() { try { await this.browserManager.close(); return { success: true, data: { message: 'Browser closed successfully' }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * Get viewport info tool implementation */ async getViewportInfo() { try { const viewportInfo = this.browserManager.getViewportInfo(); return { success: true, data: viewportInfo, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * Get DOM content tool implementation */ async getDOMContent(args) { try { const dom = await this.browserManager.getDOMContent(args.selector); return { success: true, data: { dom, selector: args.selector, }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * Get page title tool implementation */ async getPageTitle() { try { const title = await this.browserManager.getPageTitle(); return { success: true, data: { title }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * Scroll to element and screenshot tool implementation */ async scrollToElementAndScreenshot(args) { try { const timestamp = Date.now(); const filePath = path.join(this.tempDir, `element-screenshot-${timestamp}.${args.format || 'png'}`); const screenshotPath = await this.browserManager.scrollToElementAndScreenshot(args.selector, { filePath, ...(args.quality !== undefined ? { quality: args.quality } : {}), ...(args.format !== undefined ? { format: args.format } : {}), ...(args.timeout !== undefined ? { timeout: args.timeout } : {}), }); return { success: true, data: { screenshot: screenshotPath, selector: args.selector, format: args.format || 'png', message: 'Element screenshot captured successfully', }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * Capture full scrollable page tool implementation */ async captureFullScrollablePage(args) { try { const timestamp = Date.now(); const filePath = path.join(this.tempDir, `full-page-${timestamp}.${args.format || 'png'}`); const screenshotPath = await this.browserManager.captureFullScrollablePage({ filePath, ...(args.quality !== undefined ? { quality: args.quality } : {}), ...(args.format !== undefined ? { format: args.format } : {}), ...(args.timeout !== undefined ? { timeout: args.timeout } : {}), }); return { success: true, data: { screenshot: screenshotPath, format: args.format || 'png', message: 'Full scrollable page captured successfully', }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * Click element tool implementation */ async clickElement(args) { try { await this.browserManager.clickElement(args.selector, { ...(args.timeout ? { timeout: args.timeout } : {}), }); return { success: true, data: { selector: args.selector, message: 'Element clicked successfully', }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * Type text tool implementation */ async typeText(args) { try { const typeOptions = {}; if (args.delay !== undefined) typeOptions.delay = args.delay; if (args.clear !== undefined) typeOptions.clear = args.clear; if (args.timeout !== undefined) typeOptions.timeout = args.timeout; await this.browserManager.typeText(args.selector, args.text, typeOptions); return { success: true, data: { selector: args.selector, text: args.text, message: 'Text typed successfully', }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * Hover element tool implementation */ async hoverElement(args) { try { const hoverOptions = {}; if (args.timeout !== undefined) hoverOptions.timeout = args.timeout; if (args.force !== undefined) hoverOptions.force = args.force; await this.browserManager.hoverElement(args.selector, hoverOptions); return { success: true, data: { selector: args.selector, message: 'Element hovered successfully', }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * Scroll page tool implementation */ async scrollPage(args) { try { const scrollOptions = {}; if (args.direction) scrollOptions.direction = args.direction; if (args.amount !== undefined) scrollOptions.amount = args.amount; if (args.selector !== undefined) scrollOptions.selector = args.selector; await this.browserManager.scrollPage(scrollOptions); return { success: true, data: { direction: args.direction || 'down', amount: args.amount || 500, selector: args.selector, message: 'Page scrolled successfully', }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * Press key tool implementation */ async pressKey(args) { try { const keyOptions = {}; if (args.selector !== undefined) keyOptions.selector = args.selector; if (args.timeout !== undefined) keyOptions.timeout = args.timeout; await this.browserManager.pressKey(args.key, keyOptions); return { success: true, data: { key: args.key, selector: args.selector, message: 'Key pressed successfully', }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * Wait for element tool implementation */ async waitForElement(args) { try { const waitOptions = {}; if (args.state) waitOptions.state = args.state; if (args.timeout !== undefined) waitOptions.timeout = args.timeout; await this.browserManager.waitForElement(args.selector, waitOptions); return { success: true, data: { selector: args.selector, state: args.state || 'visible', message: 'Element wait completed successfully', }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * Wait for navigation tool implementation */ async waitForNavigation(args) { try { const navOptions = {}; if (args.url !== undefined) navOptions.url = args.url; if (args.timeout !== undefined) navOptions.timeout = args.timeout; if (args.waitUntil) navOptions.waitUntil = args.waitUntil; await this.browserManager.waitForNavigation(navOptions); return { success: true, data: { url: args.url, waitUntil: args.waitUntil || 'load', currentUrl: this.browserManager.getCurrentUrl(), message: 'Navigation wait completed successfully', }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * Go back tool implementation */ async goBack(args) { try { const backOptions = {}; if (args.timeout !== undefined) backOptions.timeout = args.timeout; if (args.waitUntil) backOptions.waitUntil = args.waitUntil; await this.browserManager.goBack(backOptions); return { success: true, data: { currentUrl: this.browserManager.getCurrentUrl(), message: 'Navigated back successfully', }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * Go forward tool implementation */ async goForward(args) { try { const forwardOptions = {}; if (args.timeout !== undefined) forwardOptions.timeout = args.timeout; if (args.waitUntil) forwardOptions.waitUntil = args.waitUntil; await this.browserManager.goForward(forwardOptions); return { success: true, data: { currentUrl: this.browserManager.getCurrentUrl(), message: 'Navigated forward successfully', }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * Reload page tool implementation */ async reloadPage(args) { try { const reloadOptions = {}; if (args.ignoreCache !== undefined) reloadOptions.ignoreCache = args.ignoreCache; if (args.timeout !== undefined) reloadOptions.timeout = args.timeout; if (args.waitUntil) reloadOptions.waitUntil = args.waitUntil; await this.browserManager.reloadPage(reloadOptions); return { success: true, data: { currentUrl: this.browserManager.getCurrentUrl(), ignoreCache: args.ignoreCache ?? false, message: `Page reloaded successfully${args.ignoreCache ? ' (hard refresh)' : ''}`, }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * Evaluate JavaScript tool implementation */ async evaluateJS(args) { try { const jsOptions = {}; if (args.timeout !== undefined) jsOptions.timeout = args.timeout; const result = await this.browserManager.evaluateJavaScript(args.code, jsOptions); return { success: true, data: { code: args.code, result: result, type: typeof result, message: 'JavaScript executed successfully', }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * Evaluate JavaScript on element tool implementation */ async evaluateJSOnElement(args) { try { const jsOptions = {}; if (args.timeout !== undefined) jsOptions.timeout = args.timeout; const result = await this.browserManager.evaluateJavaScriptOnElement(args.selector, args.code, jsOptions); return { success: true, data: { selector: args.selector, code: args.code, result: result, type: typeof result, message: 'JavaScript executed on element successfully', }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * Get element info tool implementation */ async getElementInfo(args) { try { const infoOptions = {}; if (args.timeout !== undefined) infoOptions.timeout = args.timeout; const elementInfo = await this.browserManager.getElementInfo(args.selector, infoOptions); return { success: true, data: { selector: args.selector, elementInfo: elementInfo, message: 'Element information retrieved successfully', }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * Get console logs tool implementation */ async getConsoleLogs(args) { try { const options = {}; if (args.level !== undefined) options.level = args.level; if (args.limit !== undefined) options.limit = args.limit; const logs = this.browserManager.getConsoleLogs(options); return { success: true, data: { logs: logs, count: logs.length, level: args.level || 'all', message: `Retrieved ${logs.length} console log${logs.length === 1 ? '' : 's'}`, }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * Clear console logs tool implementation */ async clearConsoleLogs() { try { this.browserManager.clearConsoleLogs(); return { success: true, data: { message: 'Console logs cleared successfully', }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * Get network logs tool implementation */ async getNetworkLogs(args) { try { const options = {}; if (args.method !== undefined) options.method = args.method; if (args.status !== undefined) options.status = args.status; if (args.url_pattern !== undefined) options.urlPattern = args.url_pattern; if (args.limit !== undefined) options.limit = args.limit; const logs = this.browserManager.getNetworkLogs(options); return { success: true, data: { logs: logs, count: logs.length, filters: { method: args.method || 'all', status: args.status || 'all', urlPattern: args.url_pattern || 'all', }, message: `Retrieved ${logs.length} network log${logs.length === 1 ? '' : 's'}`, }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * Clear network logs tool implementation */ async clearNetworkLogs() { try { this.browserManager.clearNetworkLogs(); return { success: true, data: { message: 'Network logs cleared successfully', }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * Set viewport size tool implementation */ async setViewportSize(args) { try { await this.browserManager.setViewportSize(args.width, args.height); return { success: true, data: { width: args.width, height: args.height, message: `Viewport size set to ${args.width}x${args.height} successfully`, }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * Set user agent tool implementation */ async setUserAgent(args) { try { await this.browserManager.setUserAgent(args.userAgent); return { success: true, data: { userAgent: args.userAgent, message: 'User agent set successfully', }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * Set geolocation tool implementation */ async setGeolocation(args) { try { await this.browserManager.setGeolocation(args.latitude, args.longitude, args.accuracy); return { success: true, data: { latitude: args.latitude, longitude: args.longitude, accuracy: args.accuracy ?? 100, message: `Geolocation set to ${args.latitude}, ${args.longitude} successfully`, }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * Switch browser engine tool implementation */ async switchBrowser(args) { try { await this.browserManager.switchBrowserEngine(args.engine); const browserInfo = this.browserManager.getBrowserInfo(); return { success: true, data: { engine: browserInfo.engine, version: browserInfo.version, message: `Successfully switched to ${args.engine} browser engine`, }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * Get browser info tool implementation */ async getBrowserInfo() { try { const browserInfo = this.browserManager.getBrowserInfo(); return { success: true, data: { engine: browserInfo.engine, version: browserInfo.version, isInitialized: browserInfo.isInitialized, message: `Current browser: ${browserInfo.engine}${browserInfo.version ? ` v${browserInfo.version}` : ''}`, }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * List available browsers tool implementation */ async listAvailableBrowsers() { try { const availableEngines = BrowserManager.getAvailableEngines(); return { success: true, data: { engines: availableEngines, count: availableEngines.length, message: `${availableEngines.length} browser engines available: ${availableEngines.join(', ')}`, }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } // ====== ADVANCED DEBUGGING TOOL HANDLERS ====== /** * Highlight element tool implementation */ async highlightElement(args) { try { const options = {}; if (args.style !== undefined) options.style = args.style; if (args.duration !== undefined) options.duration = args.duration; if (args.showInfo !== undefined) options.showInfo = args.showInfo; if (args.timeout !== undefined) options.timeout = args.timeout; const result = await this.browserManager.highlightElement(args.selector, options); return { success: true, data: { highlightedCount: result.highlightedCount, ...(result.elementInfo ? { elementInfo: result.elementInfo } : {}), message: `Highlighted ${result.highlightedCount} element${result.highlightedCount === 1 ? '' : 's'} matching "${args.selector}"`, }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * Trace execution tool implementation */ async traceExecution(args) { try { const tracingOptions = {}; if (args.enabled !== undefined) tracingOptions.enabled = args.enabled; if (args.options?.screenshots !== undefined) tracingOptions.screenshots = args.options.screenshots; if (args.options?.snapshots !== undefined) tracingOptions.snapshots = args.options.snapshots; if (args.options?.sources !== undefined) tracingOptions.sources = args.options.sources; if (args.options?.network !== undefined) tracingOptions.network = args.options.network; if (args.options?.console !== undefined) tracingOptions.console = args.options.console; if (args.path !== undefined) tracingOptions.path = args.path; const result = await this.browserManager.enableTracing(tracingOptions); return { success: true, data: { status: result.status, ...(result.tracePath ? { tracePath: result.tracePath } : {}), message: result.status, }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false, error: { type: error.type, message: error.message, ...(error.details ? { details: error.details } : {}), }, }; } throw error; } } /** * Capture performance timeline tool implementation */ async capturePerformanceTimeline(args) { try { const perfOptions = {}; if (args.categories !== undefined) perfOptions.categories = args.categories; if (args.duration !== undefined) perfOptions.duration = args.duration; if (args.includeMetrics !== undefined) perfOptions.includeMetrics = args.includeMetrics; if (args.format !== undefined) perfOptions.format = args.format; const result = await this.browserManager.capturePerformanceTimeline(perfOptions); return { success: true, data: { format: result.format, data: result.data, ...(result.metrics ? { metrics: result.metrics } : {}), timestamp: result.timestamp, message: `Captured performance timeline in ${result.format} format`, }, }; } catch (error) { if (error instanceof BrowserAutomationError) { return { success: false,