UNPKG

ai-debug-local-mcp

Version:

🎯 ENHANCED AI GUIDANCE v4.1.2: Dramatically improved tool descriptions help AI users choose the right tools instead of 'close enough' options. Ultra-fast keyboard automation (10x speed), universal recording, multi-ecosystem debugging support, and compreh

363 lines 13.6 kB
/** * Enhanced Debug Handler * * Integrates the new enhanced console capture and visual validation tools * into the AI-Debug MCP server. */ import { BaseHandler } from './base-handler.js'; import { EnhancedConsoleCapture } from '../utils/enhanced-console-capture.js'; import { VisualValidationTools } from '../utils/visual-validation-tools.js'; import { SessionManager } from '../session-manager.js'; export class EnhancedDebugHandler extends BaseHandler { consoleCaptureMap = new Map(); sessionManager = new SessionManager(); getMetadata() { return { id: 'enhanced-debug', name: 'Enhanced Debug Handler', version: '1.0.0', timeout: 30000, resource: { lazyLoad: false } }; } async handle(name, args) { switch (name) { case 'start_console_capture': return this.startConsoleCapture(args); case 'stop_console_capture': return this.stopConsoleCapture(args); case 'get_console_analysis': return this.getConsoleAnalysis(args); case 'validate_component': return this.validateComponent(args); case 'validate_map': return this.validateMap(args); case 'visual_regression': return this.visualRegression(args); default: throw new Error(`Unknown tool: ${name}`); } } /** * Start enhanced console capture for a session */ async startConsoleCapture(args) { const { sessionId } = args; if (!sessionId) { throw new Error('sessionId is required'); } const session = this.sessionManager.getSession(sessionId); if (!session) { throw new Error(`Session ${sessionId} not found`); } // Check if already capturing if (this.consoleCaptureMap.has(sessionId)) { return { content: [{ type: 'text', text: '⚠️ Console capture is already active for this session' }] }; } // Create and start capture const capture = new EnhancedConsoleCapture(session.page); await capture.startCapture(); this.consoleCaptureMap.set(sessionId, capture); return { content: [{ type: 'text', text: `🎯 Enhanced console capture started for session ${sessionId}\n\n` + `This will capture:\n` + `- JavaScript errors with stack traces\n` + `- Network errors and CORS issues\n` + `- Framework-specific warnings\n` + `- Performance issues\n` + `- All console.log/warn/error messages\n\n` + `Use \`get_console_analysis\` to get detailed analysis.` }] }; } /** * Stop console capture */ async stopConsoleCapture(args) { const { sessionId } = args; if (!sessionId) { throw new Error('sessionId is required'); } const capture = this.consoleCaptureMap.get(sessionId); if (!capture) { return { content: [{ type: 'text', text: '⚠️ No active console capture found for this session' }] }; } capture.stopCapture(); const analysis = capture.analyze(); // Clean up this.consoleCaptureMap.delete(sessionId); return { content: [{ type: 'text', text: `🛑 Console capture stopped\n\n` + `**Summary:**\n` + `- Total logs: ${analysis.summary.totalLogs}\n` + `- Errors: ${analysis.summary.errorCount}\n` + `- Warnings: ${analysis.summary.warningCount}\n` }] }; } /** * Get detailed console analysis */ async getConsoleAnalysis(args) { const { sessionId, format = 'markdown', includeFullReport = true } = args; if (!sessionId) { throw new Error('sessionId is required'); } const capture = this.consoleCaptureMap.get(sessionId); if (!capture) { throw new Error('No active console capture found. Use start_console_capture first.'); } const analysis = capture.analyze(); const report = capture.getFormattedReport({ format, includeAnalysis: true }); // Create a concise summary for the response let summary = `## Console Analysis Summary\n\n`; summary += `**Total Logs:** ${analysis.summary.totalLogs}\n`; summary += `**Errors:** ${analysis.summary.errorCount} 🔴\n`; summary += `**Warnings:** ${analysis.summary.warningCount} 🟡\n`; summary += `**Info:** ${analysis.summary.infoCount} 🔵\n\n`; if (analysis.criticalErrors.length > 0) { summary += `### Critical Errors Found:\n\n`; for (const error of analysis.criticalErrors.slice(0, 3)) { summary += `**${new Date(error.timestamp).toLocaleTimeString()}**: ${error.text}\n`; if (error.location) { summary += ` _at ${error.location.url}:${error.location.lineNumber}_\n`; } summary += '\n'; } } if (analysis.recommendations.length > 0) { summary += `### Recommendations:\n\n`; for (const rec of analysis.recommendations) { summary += `- ${rec}\n`; } } const response = { content: [{ type: 'text', text: summary }], analysis: analysis }; // Include full report if requested if (includeFullReport) { response.fullReport = report; } return response; } /** * Validate a UI component */ async validateComponent(args) { const { sessionId, selector, options = {} } = args; if (!sessionId) { throw new Error('sessionId is required'); } if (!selector) { throw new Error('selector is required'); } const session = this.sessionManager.getSession(sessionId); if (!session) { throw new Error(`Session ${sessionId} not found`); } const result = await VisualValidationTools.validateComponent(session.page, selector, options); let message = `## Component Validation: ${selector}\n\n`; message += `**Result:** ${result.passed ? '✅ PASSED' : '❌ FAILED'}\n\n`; if (result.metrics) { message += `**Metrics:**\n`; if (result.metrics.renderTime) { message += `- Render time: ${result.metrics.renderTime}ms\n`; } if (result.metrics.visibleArea) { message += `- Visible area: ${result.metrics.visibleArea}px²\n`; } if (result.metrics.interactiveElements !== undefined) { message += `- Interactive elements: ${result.metrics.interactiveElements}\n`; } message += '\n'; } if (result.issues.length > 0) { message += `**Issues Found:**\n`; for (const issue of result.issues) { const icon = { critical: '🔴', warning: '🟡', info: '🔵' }[issue.severity]; message += `${icon} **${issue.type}**: ${issue.message}\n`; if (issue.expected !== undefined && issue.actual !== undefined) { message += ` Expected: ${issue.expected}, Actual: ${issue.actual}\n`; } } } const response = { content: [{ type: 'text', text: message }], validation: result }; // Include screenshot if available if (result.screenshots.current) { response.content.push({ type: 'image', data: result.screenshots.current, mimeType: 'image/png' }); } return response; } /** * Validate a map component (Google Maps, etc.) */ async validateMap(args) { const { sessionId, selector, options = {} } = args; if (!sessionId) { throw new Error('sessionId is required'); } const session = this.sessionManager.getSession(sessionId); if (!session) { throw new Error(`Session ${sessionId} not found`); } const result = await VisualValidationTools.validateMap(session.page, selector, options); let message = `## Map Validation\n\n`; message += `**Result:** ${result.passed ? '✅ PASSED' : '❌ FAILED'}\n`; message += `**Provider:** ${result.mapSpecific.mapProvider || 'Unknown'}\n`; message += `**Map Loaded:** ${result.mapSpecific.hasMapTiles ? 'Yes' : 'No'}\n`; message += `**Markers:** ${result.mapSpecific.markerCount}\n`; if (result.mapSpecific.apiKeyStatus) { const statusIcon = { valid: '✅', invalid: '❌', missing: '⚠️' }[result.mapSpecific.apiKeyStatus]; message += `**API Key Status:** ${statusIcon} ${result.mapSpecific.apiKeyStatus}\n`; } message += '\n'; if (result.mapSpecific.loadErrors.length > 0) { message += `**Load Errors:**\n`; for (const error of result.mapSpecific.loadErrors) { message += `- 🔴 ${error}\n`; } message += '\n'; } if (result.issues.length > 0) { message += `**Validation Issues:**\n`; for (const issue of result.issues) { const icon = { critical: '🔴', warning: '🟡', info: '🔵' }[issue.severity]; message += `${icon} ${issue.message}\n`; } } const response = { content: [{ type: 'text', text: message }], validation: result }; // Include screenshot if (result.screenshots.current) { response.content.push({ type: 'image', data: result.screenshots.current, mimeType: 'image/png' }); } return response; } /** * Perform visual regression testing */ async visualRegression(args) { const { sessionId, selector, baselineImage, options = {} } = args; if (!sessionId) { throw new Error('sessionId is required'); } if (!selector) { throw new Error('selector is required'); } const session = this.sessionManager.getSession(sessionId); if (!session) { throw new Error(`Session ${sessionId} not found`); } const result = await VisualValidationTools.visualRegression(session.page, selector, baselineImage, options); let message = `## Visual Regression Test: ${selector}\n\n`; message += `**Result:** ${result.passed ? '✅ PASSED' : '❌ FAILED'}\n`; if (result.metrics?.visualDifference !== undefined) { message += `**Visual Difference:** ${result.metrics.visualDifference.toFixed(2)}%\n`; message += `**Pixels Different:** ${result.metrics.pixelsDifferent || 0}\n`; message += `**Threshold:** ${options.threshold || 5}%\n`; } message += '\n'; if (result.issues.length > 0) { message += `**Issues:**\n`; for (const issue of result.issues) { message += `- ${issue.message}\n`; } } const response = { content: [{ type: 'text', text: message }], validation: result }; // Include screenshots if (result.screenshots.current) { response.content.push({ type: 'text', text: '\n**Current Screenshot:**' }); response.content.push({ type: 'image', data: result.screenshots.current, mimeType: 'image/png' }); } if (result.screenshots.diff) { response.content.push({ type: 'text', text: '\n**Difference Visualization:**' }); response.content.push({ type: 'image', data: result.screenshots.diff, mimeType: 'image/png' }); } return response; } /** * Clean up resources when handler is unloaded */ async cleanup() { // Stop all console captures for (const [sessionId, capture] of this.consoleCaptureMap) { capture.stopCapture(); } this.consoleCaptureMap.clear(); } } //# sourceMappingURL=enhanced-debug-handler.js.map