Skip to main content
Glama

WebSee MCP Server

by 1AQuantum
error-intelligence-tools.js19.8 kB
/** * Error Intelligence Tools * Advanced error analysis and debugging tools for WebSee MCP Server * * @module error-intelligence-tools */ import { z } from 'zod'; import { SourceIntelligenceLayer } from '../index.js'; // ============================================================================ // Tool Schemas // ============================================================================ export const ErrorResolveStackSchema = z.object({ url: z.string().url().describe('The page URL where the error occurred'), errorStack: z.string().describe('The minified error stack trace to resolve'), }); export const ErrorGetContextSchema = z.object({ url: z.string().url().describe('The page URL to analyze'), }); export const ErrorTraceCauseSchema = z.object({ url: z.string().url().describe('The page URL where the error occurred'), errorMessage: z.string().describe('The error message to trace'), }); export const ErrorGetSimilarSchema = z.object({ url: z.string().url().describe('The page URL to analyze'), errorMessage: z.string().describe('The error message to find similar errors for'), }); // ============================================================================ // Tool Implementations // ============================================================================ /** * Resolve minified error stack traces to original source locations */ export async function errorResolveStack(page, params) { const intelligence = new SourceIntelligenceLayer(); try { await intelligence.initialize(page); await page.goto(params.url, { waitUntil: 'networkidle' }); const stackLines = params.errorStack.split('\n').filter(line => line.trim()); const resolved = []; for (const line of stackLines) { const frame = { original: line, }; // Try to parse location from stack trace line // Supports formats: // - at functionName (file.js:line:column) // - at file.js:line:column // - functionName@file.js:line:column const match = line.match(/(?:at\s+(?:.*?\s+)?\(?|@)(.*?):(\d+):(\d+)\)?$/); if (match) { const [, fileUrl, lineStr, colStr] = match; const lineNum = parseInt(lineStr, 10); const colNum = parseInt(colStr, 10); try { const sourceLocation = await intelligence.resolveSourceLocation(fileUrl, lineNum, colNum); if (sourceLocation) { frame.resolved = { file: sourceLocation.file, line: sourceLocation.line, column: sourceLocation.column, content: sourceLocation.content, }; } } catch (error) { // Failed to resolve this frame, keep original console.warn(`Failed to resolve stack frame: ${line}`, error); } } resolved.push(frame); } const resolvedCount = resolved.filter(f => f.resolved).length; const message = `Resolved ${resolvedCount} of ${stackLines.length} stack frames to original source`; return { original: stackLines, resolved, message, }; } finally { await intelligence.destroy(); } } /** * Get comprehensive error context including console, network, and component state */ export async function errorGetContext(page, params) { const intelligence = new SourceIntelligenceLayer(); const context = { errors: [], warnings: [], components: [], network: [], }; try { // Capture console messages const consoleMessages = []; page.on('console', msg => { consoleMessages.push({ type: msg.type(), text: msg.text(), location: msg.location(), timestamp: Date.now(), }); }); // Capture page errors const pageErrors = []; page.on('pageerror', error => { pageErrors.push({ message: error.message, stack: error.stack, timestamp: Date.now(), }); }); await intelligence.initialize(page); await page.goto(params.url, { waitUntil: 'networkidle' }); // Wait a bit to capture any async errors await page.waitForTimeout(2000); // Process console messages consoleMessages.forEach(msg => { const entry = { type: msg.type, message: msg.text, timestamp: msg.timestamp, location: msg.location?.url ? `${msg.location.url}:${msg.location.lineNumber}:${msg.location.columnNumber}` : undefined, }; if (msg.type === 'error') { context.errors.push(entry); } else if (msg.type === 'warning') { context.warnings.push(entry); } }); // Process page errors pageErrors.forEach(error => { context.errors.push({ type: 'pageerror', message: error.message, timestamp: error.timestamp, location: error.stack ? error.stack.split('\n')[1]?.trim() : undefined, }); }); // Get component tree const components = await intelligence.getComponentTree(); context.components = components.map(comp => ({ name: comp.name, framework: comp.type, state: comp.state, props: comp.props, })); // Get network activity const networkTraces = intelligence.getNetworkTraces(); context.network = networkTraces.map(trace => ({ url: trace.url, method: trace.method, status: trace.status, duration: trace.duration, timestamp: trace.timestamp, })); return context; } finally { await intelligence.destroy(); } } /** * Trace an error to its root cause with AI-powered analysis */ export async function errorTraceCause(page, params) { const intelligence = new SourceIntelligenceLayer(); try { const errors = []; const errorTimeline = []; // Capture all errors with timestamps page.on('pageerror', error => { errors.push(error); errorTimeline.push({ error, timestamp: Date.now(), }); }); await intelligence.initialize(page); await page.goto(params.url, { waitUntil: 'networkidle' }); // Wait for errors to occur await page.waitForTimeout(2000); // Find the matching error const targetError = errors.find(e => e.message.includes(params.errorMessage) || params.errorMessage.includes(e.message)); if (!targetError) { return { rootCause: `No error matching "${params.errorMessage}" was found on the page`, confidence: 'low', stackTrace: [], relatedErrors: [], recommendations: [ 'Verify the error message is correct', 'Try interacting with the page to trigger the error', 'Check if the error occurs during page load or user interaction', ], }; } // Get error intelligence const errorIntel = await intelligence.getErrorIntelligence(targetError); // Parse stack trace const stackTrace = []; if (errorIntel.originalStack) { for (const stackLine of errorIntel.originalStack) { stackTrace.push({ original: stackLine, resolved: stackLine.includes(':') ? { file: stackLine.split(':')[0].replace(/^at\s+/, ''), line: parseInt(stackLine.split(':')[1] || '0'), column: parseInt(stackLine.split(':')[2] || '0'), } : undefined, }); } } // Analyze related errors const relatedErrors = errorTimeline .filter(e => e.error !== targetError) .map(e => ({ message: e.error.message, timestamp: e.timestamp, correlation: calculateCorrelation(targetError, e.error), })) .filter(e => e.correlation > 0.3) .sort((a, b) => b.correlation - a.correlation) .slice(0, 5); // Determine root cause const rootCause = analyzeRootCause(targetError, errorIntel, relatedErrors); // Generate recommendations const recommendations = generateRecommendations(targetError, errorIntel); return { rootCause: rootCause.description, confidence: rootCause.confidence, stackTrace, relatedErrors, recommendations, }; } finally { await intelligence.destroy(); } } /** * Find similar errors in the error timeline */ export async function errorGetSimilar(page, params) { const intelligence = new SourceIntelligenceLayer(); try { const errorMap = new Map(); // Capture all errors page.on('pageerror', error => { const pattern = extractErrorPattern(error.message); const existing = errorMap.get(pattern); if (existing) { existing.count++; existing.lastSeen = Date.now(); } else { errorMap.set(pattern, { message: error.message, count: 1, firstSeen: Date.now(), lastSeen: Date.now(), stackTrace: error.stack, pattern, }); } }); await intelligence.initialize(page); await page.goto(params.url, { waitUntil: 'networkidle' }); // Wait to collect errors await page.waitForTimeout(3000); // Find similar errors const targetPattern = extractErrorPattern(params.errorMessage); const similar = []; for (const [pattern, error] of errorMap.entries()) { if (pattern === targetPattern) { similar.push(error); } else { const similarity = calculateSimilarity(targetPattern, pattern); if (similarity > 0.5) { similar.push({ ...error, message: error.message + ` (${Math.round(similarity * 100)}% similar)`, }); } } } // Sort by count (most frequent first) similar.sort((a, b) => b.count - a.count); return { similar }; } finally { await intelligence.destroy(); } } // ============================================================================ // Helper Functions // ============================================================================ /** * Calculate correlation between two errors */ function calculateCorrelation(error1, error2) { let score = 0; // Same error type if (error1.name === error2.name) { score += 0.3; } // Similar messages const similarity = calculateSimilarity(error1.message, error2.message); score += similarity * 0.5; // Similar stack traces if (error1.stack && error2.stack) { const stack1Lines = error1.stack.split('\n').slice(0, 3); const stack2Lines = error2.stack.split('\n').slice(0, 3); const commonLines = stack1Lines.filter(line => stack2Lines.some(l => l.includes(line.split(':')[0]))); score += (commonLines.length / Math.max(stack1Lines.length, 1)) * 0.2; } return Math.min(score, 1); } /** * Calculate similarity between two strings */ function calculateSimilarity(str1, str2) { const words1 = new Set(str1.toLowerCase().split(/\s+/)); const words2 = new Set(str2.toLowerCase().split(/\s+/)); const intersection = new Set([...words1].filter(w => words2.has(w))); const union = new Set([...words1, ...words2]); return union.size > 0 ? intersection.size / union.size : 0; } /** * Extract error pattern for grouping similar errors */ function extractErrorPattern(message) { // Remove dynamic parts like numbers, IDs, timestamps return message .replace(/\d+/g, 'N') .replace(/0x[0-9a-fA-F]+/g, '0xHEX') .replace(/'[^']*'/g, "'STRING'") .replace(/"[^"]*"/g, '"STRING"') .replace(/\bat\s+.*$/gm, '') .trim(); } /** * Analyze root cause of an error */ function analyzeRootCause(error, intelligence, relatedErrors) { const message = error.message.toLowerCase(); // Network-related errors if (message.includes('fetch') || message.includes('network') || message.includes('ajax') || message.includes('xhr')) { return { description: `Network request failure: ${error.message}. Check network connectivity, API endpoints, and CORS configuration.`, confidence: 'high', }; } // Type errors if (message.includes('is not a function') || message.includes('undefined')) { return { description: `Type error: ${error.message}. Likely caused by accessing a property or method on an undefined/null object. Check initialization order and data flow.`, confidence: 'high', }; } // Reference errors if (message.includes('is not defined') || message.includes('not found')) { return { description: `Reference error: ${error.message}. Variable or function not in scope. Check imports, declarations, and scope.`, confidence: 'high', }; } // Component-related errors if (intelligence.components && intelligence.components.length > 0 && (message.includes('render') || message.includes('component'))) { return { description: `Component rendering error: ${error.message}. Check component props, state, and lifecycle methods.`, confidence: 'medium', }; } // Multiple related errors suggest cascading failure if (relatedErrors.length > 2) { return { description: `Cascading failure detected. Root error: ${error.message}. This triggered ${relatedErrors.length} related errors. Fix the root cause first.`, confidence: 'medium', }; } // Default return { description: `Error occurred: ${error.message}. Review the stack trace and surrounding code for more context.`, confidence: 'low', }; } /** * Generate recommendations based on error analysis */ function generateRecommendations(error, intelligence) { const recommendations = []; const message = error.message.toLowerCase(); // General recommendations recommendations.push('Review the resolved stack trace to identify the exact location'); // Network-specific if (message.includes('fetch') || message.includes('network')) { recommendations.push('Check network tab in browser DevTools'); recommendations.push('Verify API endpoint URLs and request format'); recommendations.push('Check CORS headers if cross-origin request'); recommendations.push('Add error handling for network failures'); } // Type error specific if (message.includes('undefined') || message.includes('null')) { recommendations.push('Add null checks before accessing properties'); recommendations.push('Use optional chaining (?.) for safer property access'); recommendations.push('Verify data is loaded before component renders'); } // Component-specific if (intelligence.components && intelligence.components.length > 0) { recommendations.push('Inspect component state and props in the error context'); recommendations.push('Check if component is properly mounted'); } // Build-specific if (intelligence.buildInfo) { recommendations.push(`Review module '${intelligence.buildInfo.name}' in your bundle`); } // Add source maps recommendation if not available if (!intelligence.originalStack || intelligence.originalStack.length === 0) { recommendations.push('Enable source maps in your build configuration for better debugging'); } return recommendations; } // ============================================================================ // Tool Exports // ============================================================================ export const errorIntelligenceTools = { error_resolve_stack: { name: 'error_resolve_stack', description: 'Resolve minified error stack traces to original source code locations using source maps', inputSchema: { type: 'object', properties: { url: { type: 'string', description: 'The page URL where the error occurred', }, errorStack: { type: 'string', description: 'The minified error stack trace to resolve', }, }, required: ['url', 'errorStack'], }, handler: errorResolveStack, schema: ErrorResolveStackSchema, }, error_get_context: { name: 'error_get_context', description: 'Get comprehensive error context including console errors, warnings, component state, and network activity', inputSchema: { type: 'object', properties: { url: { type: 'string', description: 'The page URL to analyze', }, }, required: ['url'], }, handler: errorGetContext, schema: ErrorGetContextSchema, }, error_trace_cause: { name: 'error_trace_cause', description: 'Trace an error to its root cause with AI-powered analysis, including related errors and recommendations', inputSchema: { type: 'object', properties: { url: { type: 'string', description: 'The page URL where the error occurred', }, errorMessage: { type: 'string', description: 'The error message to trace', }, }, required: ['url', 'errorMessage'], }, handler: errorTraceCause, schema: ErrorTraceCauseSchema, }, error_get_similar: { name: 'error_get_similar', description: 'Find similar errors in the error timeline to identify patterns and recurring issues', inputSchema: { type: 'object', properties: { url: { type: 'string', description: 'The page URL to analyze', }, errorMessage: { type: 'string', description: 'The error message to find similar errors for', }, }, required: ['url', 'errorMessage'], }, handler: errorGetSimilar, schema: ErrorGetSimilarSchema, }, }; //# sourceMappingURL=error-intelligence-tools.js.map

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/1AQuantum/websee-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server