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
JavaScript
/**
* 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