Skip to main content
Glama
ooples

MCP Console Automation Server

ErrorDetector.ts19.1 kB
import { ErrorPattern } from '../types/index.js'; import { ErrorPatterns, ExtendedErrorPattern, ParsedError, ErrorContext, } from '../patterns/ErrorPatterns.js'; export interface ErrorAnalysis { totalErrors: number; criticalErrors: number; highErrors: number; mediumErrors: number; lowErrors: number; categories: Record<string, number>; languages: Record<string, number>; correlatedErrors: CorrelatedError[]; rootCauseAnalysis: RootCauseAnalysis[]; suggestions: string[]; retryableErrors: ParsedError[]; } export interface CorrelatedError { primaryError: ParsedError; relatedErrors: ParsedError[]; confidence: number; description: string; } export interface RootCauseAnalysis { cause: string; affectedErrors: ParsedError[]; severity: 'low' | 'medium' | 'high' | 'critical'; remediation: string; confidence: number; } export interface ErrorReport { summary: { totalLines: number; errorsFound: number; severityBreakdown: Record<string, number>; categoryBreakdown: Record<string, number>; }; errors: ParsedError[]; analysis: ErrorAnalysis; recommendations: string[]; structuredOutput: any; } export class ErrorDetector { private patterns: ExtendedErrorPattern[]; private ansiRegex = /\x1b\[[0-9;]*m/g; constructor() { // Use the comprehensive patterns from ErrorPatterns this.patterns = ErrorPatterns.getAllPatterns(); } /** * Strip ANSI escape codes from text for cleaner parsing */ private stripAnsiCodes(text: string): string { return text.replace(this.ansiRegex, ''); } /** * Enhanced error detection with comprehensive pattern matching */ detect(text: string, customPatterns?: ExtendedErrorPattern[]): ParsedError[] { const cleanText = this.stripAnsiCodes(text); const patterns = customPatterns ? [...this.patterns, ...customPatterns] : this.patterns; const lines = cleanText.split('\n'); const detectedErrors: ParsedError[] = []; lines.forEach((line, lineNumber) => { let matchedOnThisLine = false; patterns.forEach((pattern) => { // Skip generic patterns if we already matched a more specific pattern if (matchedOnThisLine && pattern.category === 'generic') { return; } const match = line.match(pattern.pattern); if (match) { const parsedError: ParsedError = { pattern, match: line.trim(), line: lineNumber + 1, extractedInfo: this.extractErrorInfo( line, pattern, lines, lineNumber ), }; // Add context if available parsedError.context = this.extractContext(lines, lineNumber); detectedErrors.push(parsedError); // Mark that we found a match on this line if (pattern.category !== 'generic') { matchedOnThisLine = true; } } }); }); return detectedErrors; } /** * Extract additional error information from matched lines */ private extractErrorInfo( line: string, pattern: ExtendedErrorPattern, lines: string[], lineNumber: number ): any { const info: any = {}; // Extract file path if pattern supports it if (pattern.filePathPattern) { const fileMatch = line.match(pattern.filePathPattern); if (fileMatch) { info.filePath = fileMatch[1]; } } // Extract line number if pattern supports it if (pattern.lineNumberPattern) { const lineMatch = line.match(pattern.lineNumberPattern); if (lineMatch) { info.lineNumber = parseInt(lineMatch[1]); if (lineMatch[2]) { info.columnNumber = parseInt(lineMatch[2]); } } } // Extract error codes const errorCodeMatch = line.match(/(?:error|ERROR)\s*[:\s]*([A-Z0-9]+)/); if (errorCodeMatch) { info.errorCode = errorCodeMatch[1]; } // Extract stack trace for runtime errors if (pattern.category === 'runtime' && pattern.type === 'exception') { info.stackTrace = this.extractStackTrace(lines, lineNumber); } // Add suggestions based on pattern remediation if (pattern.remediation) { info.suggestion = pattern.remediation; } return info; } /** * Extract context around error lines */ private extractContext( lines: string[], errorLineIndex: number, contextLines: number = 3 ): ErrorContext { const context: ErrorContext = {}; const startIndex = Math.max(0, errorLineIndex - contextLines); const endIndex = Math.min(lines.length - 1, errorLineIndex + contextLines); context.beforeLines = lines.slice(startIndex, errorLineIndex); context.afterLines = lines.slice(errorLineIndex + 1, endIndex + 1); return context; } /** * Extract stack trace from surrounding lines */ private extractStackTrace(lines: string[], startIndex: number): string[] { const stackTrace: string[] = []; // Look for stack trace patterns in following lines for (let i = startIndex + 1; i < lines.length && i < startIndex + 20; i++) { const line = lines[i]; // Common stack trace patterns if ( line.match(/^\s+at\s+/) || // JavaScript/Node.js line.match(/^\s+File\s+"/) || // Python line.match(/^\s+#\d+/) || // C/C++ line.match(/^\s+\d+:/) // Rust ) { stackTrace.push(line.trim()); } else if (stackTrace.length > 0) { // Stop if we've started collecting and hit a non-stack line break; } } return stackTrace; } /** * Perform intelligent error analysis */ analyzeErrors(errors: ParsedError[]): ErrorAnalysis { const analysis: ErrorAnalysis = { totalErrors: errors.length, criticalErrors: 0, highErrors: 0, mediumErrors: 0, lowErrors: 0, categories: {}, languages: {}, correlatedErrors: [], rootCauseAnalysis: [], suggestions: [], retryableErrors: [], }; // Count by severity errors.forEach((error) => { switch (error.pattern.severity) { case 'critical': analysis.criticalErrors++; break; case 'high': analysis.highErrors++; break; case 'medium': analysis.mediumErrors++; break; case 'low': analysis.lowErrors++; break; } // Count by category analysis.categories[error.pattern.category] = (analysis.categories[error.pattern.category] || 0) + 1; // Count by language if (error.pattern.language) { analysis.languages[error.pattern.language] = (analysis.languages[error.pattern.language] || 0) + 1; } // Collect retryable errors if (error.pattern.retryable) { analysis.retryableErrors.push(error); } }); // Perform correlation analysis analysis.correlatedErrors = this.findCorrelatedErrors(errors); // Perform root cause analysis analysis.rootCauseAnalysis = this.performRootCauseAnalysis(errors); // Generate suggestions analysis.suggestions = this.generateSuggestions(errors, analysis); return analysis; } /** * Find correlated errors (errors that likely have the same root cause) */ private findCorrelatedErrors(errors: ParsedError[]): CorrelatedError[] { const correlations: CorrelatedError[] = []; const processed = new Set<number>(); for (let i = 0; i < errors.length; i++) { if (processed.has(i)) continue; const primaryError = errors[i]; const relatedErrors: ParsedError[] = []; // Look for errors in the same category within a few lines for (let j = i + 1; j < errors.length; j++) { if (processed.has(j)) continue; const secondaryError = errors[j]; const lineDifference = Math.abs( primaryError.line - secondaryError.line ); if ( primaryError.pattern.category === secondaryError.pattern.category && lineDifference <= 10 ) { relatedErrors.push(secondaryError); processed.add(j); } } if (relatedErrors.length > 0) { correlations.push({ primaryError, relatedErrors, confidence: Math.min(0.9, 0.5 + relatedErrors.length * 0.1), description: `${relatedErrors.length + 1} related ${primaryError.pattern.category} errors`, }); processed.add(i); } } return correlations; } /** * Perform root cause analysis */ private performRootCauseAnalysis(errors: ParsedError[]): RootCauseAnalysis[] { const rootCauses: RootCauseAnalysis[] = []; // Group errors by category const errorsByCategory = errors.reduce( (groups, error) => { const category = error.pattern.category; if (!groups[category]) { groups[category] = []; } groups[category].push(error); return groups; }, {} as Record<string, ParsedError[]> ); // Analyze each category Object.entries(errorsByCategory).forEach(([category, categoryErrors]) => { if (categoryErrors.length < 2) return; let cause = ''; let remediation = ''; let severity: 'low' | 'medium' | 'high' | 'critical' = 'medium'; switch (category) { case 'network': cause = 'Network connectivity or configuration issues'; remediation = 'Check network connectivity, DNS settings, and firewall configuration'; severity = 'high'; break; case 'database': cause = 'Database connection or query issues'; remediation = 'Verify database connectivity, check query syntax, and review connection pool settings'; severity = 'high'; break; case 'compilation': cause = 'Source code compilation errors'; remediation = 'Fix syntax errors, resolve dependencies, and check compiler settings'; severity = 'critical'; break; case 'runtime': cause = 'Runtime execution errors'; remediation = 'Debug application logic, check for null references, and handle exceptions properly'; severity = 'high'; break; case 'performance': cause = 'System resource constraints'; remediation = 'Monitor resource usage, optimize code performance, and increase system resources'; severity = 'medium'; break; case 'security': cause = 'Security or authentication issues'; remediation = 'Review authentication mechanisms, check permissions, and validate security tokens'; severity = 'critical'; break; case 'ssh': cause = 'SSH connection or authentication problems'; remediation = 'Verify SSH credentials, check network connectivity, and review SSH configuration'; severity = 'high'; break; case 'aws-ssm': cause = 'AWS Systems Manager session or configuration issues'; remediation = 'Check SSM agent status, IAM permissions, instance connectivity, and AWS credentials'; severity = 'high'; break; default: cause = `Multiple ${category} related issues`; remediation = 'Review logs and address individual error messages'; } rootCauses.push({ cause, affectedErrors: categoryErrors, severity, remediation, confidence: Math.min(0.9, 0.6 + categoryErrors.length * 0.05), }); }); return rootCauses; } /** * Generate actionable suggestions based on error analysis */ private generateSuggestions( errors: ParsedError[], analysis: ErrorAnalysis ): string[] { const suggestions: string[] = []; // Critical errors get priority if (analysis.criticalErrors > 0) { suggestions.push( `⚠️ Address ${analysis.criticalErrors} critical error(s) immediately` ); } // Category-specific suggestions Object.entries(analysis.categories).forEach(([category, count]) => { if (count >= 3) { switch (category) { case 'network': suggestions.push( `🌐 Multiple network errors detected - check connectivity and DNS settings` ); break; case 'database': suggestions.push( `🗄️ Database issues found - verify connection and query syntax` ); break; case 'compilation': suggestions.push( `🔨 Compilation errors present - fix syntax and dependency issues` ); break; case 'performance': suggestions.push( `⚡ Performance issues detected - monitor resource usage` ); break; case 'security': suggestions.push( `🔒 Security errors found - review authentication and permissions` ); break; case 'aws-ssm': suggestions.push( `☁️ AWS SSM errors detected - check SSM agent, IAM permissions, and connectivity` ); break; } } }); // Retryable errors suggestion if (analysis.retryableErrors.length > 0) { suggestions.push( `🔄 ${analysis.retryableErrors.length} error(s) may be resolved by retrying` ); } // Root cause suggestions analysis.rootCauseAnalysis.forEach((rootCause) => { if (rootCause.confidence > 0.7) { suggestions.push(`🎯 Root cause identified: ${rootCause.cause}`); } }); return suggestions; } /** * Generate comprehensive error report */ generateReport( text: string, customPatterns?: ExtendedErrorPattern[] ): ErrorReport { const lines = this.stripAnsiCodes(text).split('\n'); const errors = this.detect(text, customPatterns); const analysis = this.analyzeErrors(errors); const severityBreakdown = { critical: analysis.criticalErrors, high: analysis.highErrors, medium: analysis.mediumErrors, low: analysis.lowErrors, }; const recommendations: string[] = []; // Add high-level recommendations if (analysis.criticalErrors > 0) { recommendations.push( 'Immediately address critical errors to prevent system instability' ); } if (analysis.correlatedErrors.length > 0) { recommendations.push( 'Focus on correlated errors which may share common root causes' ); } if (analysis.retryableErrors.length > 0) { recommendations.push( 'Consider implementing retry mechanisms for transient errors' ); } return { summary: { totalLines: lines.length, errorsFound: errors.length, severityBreakdown, categoryBreakdown: analysis.categories, }, errors, analysis, recommendations: [...recommendations, ...analysis.suggestions], structuredOutput: { timestamp: new Date().toISOString(), version: '2.0.0', platform: process.platform, errors: errors.map((error) => ({ type: error.pattern.type, category: error.pattern.category, severity: error.pattern.severity, message: error.match, line: error.line, file: error.extractedInfo?.filePath, suggestion: error.extractedInfo?.suggestion, retryable: error.pattern.retryable || false, })), }, }; } /** * Add custom pattern */ addPattern(pattern: ExtendedErrorPattern): void { this.patterns.push(pattern); } /** * Remove pattern by regex */ removePattern(pattern: RegExp): void { this.patterns = this.patterns.filter( (p) => p.pattern.toString() !== pattern.toString() ); } /** * Get all patterns */ getPatterns(): ExtendedErrorPattern[] { return [...this.patterns]; } /** * Get patterns by category */ getPatternsByCategory(category: string): ExtendedErrorPattern[] { return ErrorPatterns.getPatternsByCategory(category); } /** * Get patterns by language */ getPatternsByLanguage(language: string): ExtendedErrorPattern[] { return ErrorPatterns.getPatternsByLanguage(language); } /** * Legacy method for backward compatibility */ analyzeStackTrace(text: string): { language?: string; frames: string[] } { const cleanText = this.stripAnsiCodes(text); const result: { language?: string; frames: string[] } = { frames: [] }; const pythonMatch = cleanText.match( /Traceback \(most recent call last\):([\s\S]*?)(?=\n\S|\n$)/ ); if (pythonMatch) { result.language = 'python'; const frames = pythonMatch[1] .split('\n') .filter((line) => line.trim().startsWith('File')); result.frames = frames.map((f) => f.trim()); return result; } const javaMatch = cleanText.match( /(?:Exception|Error) in thread[\s\S]*?\n((?:\s+at .*\n)+)/ ); if (javaMatch) { result.language = 'java'; result.frames = javaMatch[1] .split('\n') .filter((line) => line.trim().startsWith('at')) .map((f) => f.trim()); return result; } const nodeMatch = cleanText.match(/at .*? \(.*?\)|\n\s+at .*?\n/g); if (nodeMatch && nodeMatch.length > 0) { result.language = 'javascript'; result.frames = nodeMatch.map((f) => f.trim()); return result; } const genericStack = cleanText.match(/^\s*(#\d+|at|\d+:)\s+.*/gm); if (genericStack && genericStack.length > 0) { result.frames = genericStack.map((f) => f.trim()); } return result; } /** * Legacy method for backward compatibility */ getSeverityScore( errors: Array<{ pattern: ErrorPattern; match: string; line: number }> ): number { const severityWeights = { low: 1, medium: 2, high: 3, critical: 4, }; return errors.reduce((score, error) => { return score + severityWeights[error.pattern.severity]; }, 0); } /** * Add multiple patterns at once - convenience method */ addPatterns(patterns: ExtendedErrorPattern[]): void { patterns.forEach((pattern) => this.addPattern(pattern)); } /** * Remove multiple patterns by regex - convenience method */ removePatterns(patterns: RegExp[]): void { patterns.forEach((pattern) => this.removePattern(pattern)); } /** * Process output - alias for detect method to match interface expectations */ processOutput(output: any): ParsedError[] { if (typeof output === 'string') { return this.detect(output); } else if (output && typeof output.data === 'string') { return this.detect(output.data); } return []; } }

Latest Blog Posts

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/ooples/mcp-console-automation'

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