@boundless-oss/atlas
Version:
Atlas - MCP Server for comprehensive startup project management
835 lines • 32.8 kB
JavaScript
import { randomUUID } from 'crypto';
import { createTool, createSuccessResult, createErrorResult } from '../../core/tool-framework.js';
/**
* Get detailed error information
*/
const getErrorDetailsTool = createTool({
name: 'get_error_details',
description: 'Get detailed information about a specific error including context, root cause analysis, and recovery suggestions.',
category: 'error-analysis',
inputSchema: {
type: 'object',
properties: {
errorId: {
type: 'string',
description: 'Unique identifier for the error to analyze',
minLength: 1
},
includeContext: {
type: 'boolean',
default: true,
description: 'Include detailed error context and environment information'
},
includeSuggestions: {
type: 'boolean',
default: true,
description: 'Include recovery suggestions and next steps'
}
},
required: ['errorId'],
additionalProperties: false
},
async execute(input, context) {
try {
// Check if error exists
const errorResult = await context.db.get(`SELECT * FROM error_logs WHERE id = ? AND project_id = ?`, [input.errorId, context.projectId || 'default']);
if (!errorResult.success || !errorResult.data) {
return createErrorResult({
code: 'RESOURCE_NOT_FOUND',
message: 'Error not found',
details: { errorId: input.errorId },
category: 'validation'
});
}
const error = errorResult.data;
// Parse JSON fields
const errorData = {
errorId: error.id,
type: error.error_type,
severity: error.severity,
message: error.message,
timestamp: new Date(error.timestamp).toISOString(),
tool: error.tool_name,
context: JSON.parse(error.context || '{}'),
stackTrace: error.stack_trace,
rootCause: JSON.parse(error.root_cause || '{}'),
suggestions: JSON.parse(error.suggestions || '[]'),
relatedErrors: []
};
// Get related errors if needed
if (input.includeContext) {
const relatedResult = await context.db.query(`SELECT id, error_type, message, timestamp
FROM error_logs
WHERE project_id = ?
AND tool_name = ?
AND id != ?
AND timestamp BETWEEN ? AND ?
ORDER BY timestamp DESC
LIMIT 5`, [
context.projectId || 'default',
error.tool_name,
input.errorId,
error.timestamp - 3600000, // 1 hour before
error.timestamp + 3600000 // 1 hour after
]);
if (relatedResult.success && relatedResult.data) {
errorData.relatedErrors = relatedResult.data.map((e) => ({
id: e.id,
type: e.error_type,
message: e.message,
timestamp: new Date(e.timestamp).toISOString()
}));
}
}
return createSuccessResult({
errorDetails: errorData,
message: 'Error details retrieved successfully'
});
}
catch (error) {
return createErrorResult({
code: 'EXECUTION_ERROR',
message: `Failed to get error details: ${error instanceof Error ? error.message : 'Unknown error'}`,
category: 'execution'
});
}
}
});
/**
* Analyze error patterns
*/
const analyzeErrorPatternsTool = createTool({
name: 'analyze_error_patterns',
description: 'Analyze error patterns and trends to identify recurring issues and recommend systematic improvements.',
category: 'error-analysis',
inputSchema: {
type: 'object',
properties: {
timeRange: {
type: 'string',
enum: ['1h', '24h', '7d', '30d'],
default: '24h',
description: 'Time range for error pattern analysis'
},
errorTypes: {
type: 'array',
items: {
type: 'string',
enum: ['validation', 'network', 'permission', 'resource', 'logic', 'external']
},
description: 'Filter analysis by specific error types'
},
minOccurrences: {
type: 'number',
minimum: 1,
default: 2,
description: 'Minimum occurrences required to identify a pattern'
},
groupBy: {
type: 'string',
enum: ['type', 'tool', 'time', 'severity'],
default: 'type',
description: 'How to group errors for pattern analysis'
}
},
required: [],
additionalProperties: false
},
async execute(input, context) {
try {
const now = Date.now();
const timeRangeMs = {
'1h': 3600000,
'24h': 86400000,
'7d': 604800000,
'30d': 2592000000
}[input.timeRange || '24h'];
const startTime = now - timeRangeMs;
// Build query based on filters
let query = `
SELECT
error_type,
tool_name,
severity,
COUNT(*) as count,
COUNT(DISTINCT tool_name) as affected_tools_count,
GROUP_CONCAT(DISTINCT tool_name) as affected_tools,
AVG(CASE WHEN recovery_successful THEN 1 ELSE 0 END) as recovery_rate
FROM error_logs
WHERE project_id = ? AND timestamp >= ?
`;
const params = [context.projectId || 'default', startTime];
if (input.errorTypes && input.errorTypes.length > 0) {
query += ` AND error_type IN (${input.errorTypes.map(() => '?').join(',')})`;
params.push(...input.errorTypes);
}
const groupByColumn = {
'type': 'error_type',
'tool': 'tool_name',
'severity': 'severity',
'time': "strftime('%Y-%m-%d %H:00:00', timestamp/1000, 'unixepoch')"
}[input.groupBy || 'type'];
query += ` GROUP BY ${groupByColumn}`;
query += ` HAVING COUNT(*) >= ?`;
params.push(input.minOccurrences || 2);
query += ` ORDER BY count DESC`;
const patternsResult = await context.db.query(query, params);
if (!patternsResult.success) {
throw new Error('Failed to analyze error patterns');
}
// Get total error count
const totalResult = await context.db.get(`SELECT COUNT(*) as total FROM error_logs WHERE project_id = ? AND timestamp >= ?`, [context.projectId || 'default', startTime]);
const totalErrors = totalResult.data?.total || 0;
const patterns = (patternsResult.data || []).map((p) => ({
title: p.error_type || p.tool_name || p.severity,
count: p.count,
frequency: p.count > 10 ? 'Frequent' : p.count > 5 ? 'Regular' : 'Occasional',
severity: p.severity || 'medium',
description: `${p.count} occurrences in ${input.timeRange}`,
affectedTools: p.affected_tools ? p.affected_tools.split(',') : [],
recoveryRate: p.recovery_rate || 0
}));
// Generate recommendations based on patterns
const recommendations = [];
for (const pattern of patterns) {
if (pattern.recoveryRate < 0.5) {
recommendations.push({
title: `Improve recovery for ${pattern.title} errors`,
priority: 'high',
impact: 'High',
action: 'Implement better error recovery mechanisms'
});
}
if (pattern.count > totalErrors * 0.3) {
recommendations.push({
title: `Address frequent ${pattern.title} errors`,
priority: 'critical',
impact: 'Critical',
action: 'This error type represents over 30% of all errors'
});
}
}
return createSuccessResult({
totalErrors,
patternsFound: patterns,
recommendations,
insights: patterns.length > 0 ? [
`Found ${patterns.length} error patterns in the last ${input.timeRange}`,
`Most common error type: ${patterns[0]?.title || 'None'}`
] : ['No significant error patterns detected'],
timeRange: input.timeRange,
groupBy: input.groupBy
});
}
catch (error) {
return createErrorResult({
code: 'EXECUTION_ERROR',
message: `Failed to analyze error patterns: ${error instanceof Error ? error.message : 'Unknown error'}`,
category: 'execution'
});
}
}
});
/**
* Get error timeline
*/
const getErrorTimelineTool = createTool({
name: 'get_error_timeline',
description: 'Get chronological timeline of errors with context about system state and user actions.',
category: 'error-analysis',
readOnly: true,
inputSchema: {
type: 'object',
properties: {
timeRange: {
type: 'string',
enum: ['1h', '6h', '24h', '7d'],
default: '24h',
description: 'Time range for error timeline'
},
toolName: {
type: 'string',
description: 'Filter timeline to specific tool (optional)'
},
severity: {
type: 'string',
enum: ['low', 'medium', 'high', 'critical'],
description: 'Minimum severity level to include'
},
includeContext: {
type: 'boolean',
default: true,
description: 'Include system context and user actions around each error'
}
},
required: [],
additionalProperties: false
},
async execute(input, context) {
try {
const now = Date.now();
const timeRangeMs = {
'1h': 3600000,
'6h': 21600000,
'24h': 86400000,
'7d': 604800000
}[input.timeRange || '24h'];
const startTime = now - timeRangeMs;
let query = `
SELECT
id,
timestamp,
tool_name,
error_type,
severity,
message,
context,
recovery_method,
recovery_successful
FROM error_logs
WHERE project_id = ? AND timestamp >= ?
`;
const params = [context.projectId || 'default', startTime];
if (input.toolName) {
query += ` AND tool_name = ?`;
params.push(input.toolName);
}
if (input.severity) {
const severityOrder = ['low', 'medium', 'high', 'critical'];
const minIndex = severityOrder.indexOf(input.severity);
const validSeverities = severityOrder.slice(minIndex);
query += ` AND severity IN (${validSeverities.map(() => '?').join(',')})`;
params.push(...validSeverities);
}
query += ` ORDER BY timestamp DESC LIMIT 100`;
const timelineResult = await context.db.query(query, params);
if (!timelineResult.success) {
throw new Error('Failed to get error timeline');
}
const timeline = (timelineResult.data || []).map((entry) => ({
timestamp: new Date(entry.timestamp).toISOString(),
tool: entry.tool_name,
type: entry.error_type,
severity: entry.severity,
message: entry.message,
context: input.includeContext ? JSON.parse(entry.context || '{}') : undefined,
recovery: entry.recovery_successful ?
(entry.recovery_method || 'Automatic recovery') :
'No recovery attempted'
}));
return createSuccessResult({
timeline,
count: timeline.length,
timeRange: input.timeRange,
filters: {
toolName: input.toolName || null,
severity: input.severity || null
}
});
}
catch (error) {
return createErrorResult({
code: 'EXECUTION_ERROR',
message: `Failed to get error timeline: ${error instanceof Error ? error.message : 'Unknown error'}`,
category: 'execution'
});
}
}
});
/**
* Generate error report
*/
const generateErrorReportTool = createTool({
name: 'generate_error_report',
description: 'Generate comprehensive error analysis report for debugging, monitoring, or team review.',
category: 'error-analysis',
inputSchema: {
type: 'object',
properties: {
reportType: {
type: 'string',
enum: ['summary', 'detailed', 'debugging', 'trends'],
default: 'summary',
description: 'Type of error report to generate'
},
timeRange: {
type: 'string',
enum: ['24h', '7d', '30d'],
default: '24h',
description: 'Time range for report data'
},
includeRecommendations: {
type: 'boolean',
default: true,
description: 'Include improvement recommendations'
},
outputFormat: {
type: 'string',
enum: ['markdown', 'json', 'csv'],
default: 'markdown',
description: 'Format for the generated report'
},
saveToFile: {
type: 'boolean',
default: false,
description: 'Save report to file in .atlas/reports/'
}
},
required: [],
additionalProperties: false
},
async execute(input, context) {
try {
const now = Date.now();
const timeRangeMs = {
'24h': 86400000,
'7d': 604800000,
'30d': 2592000000
}[input.timeRange || '24h'];
const startTime = now - timeRangeMs;
// Get summary statistics
const summaryResult = await context.db.get(`SELECT
COUNT(*) as total_errors,
COUNT(DISTINCT tool_name) as affected_tools,
COUNT(DISTINCT error_type) as error_types,
SUM(CASE WHEN severity = 'critical' THEN 1 ELSE 0 END) as critical_errors,
SUM(CASE WHEN severity = 'high' THEN 1 ELSE 0 END) as high_errors,
SUM(CASE WHEN recovery_successful = 1 THEN 1 ELSE 0 END) as recovered_errors
FROM error_logs
WHERE project_id = ? AND timestamp >= ?`, [context.projectId || 'default', startTime]);
const summary = summaryResult.data || {};
// Get top errors by type
const topErrorsResult = await context.db.query(`SELECT
error_type,
COUNT(*) as count,
AVG(CASE WHEN recovery_successful THEN 1 ELSE 0 END) as recovery_rate
FROM error_logs
WHERE project_id = ? AND timestamp >= ?
GROUP BY error_type
ORDER BY count DESC
LIMIT 5`, [context.projectId || 'default', startTime]);
let content;
if (input.outputFormat === 'markdown') {
content = generateMarkdownReport(summary, topErrorsResult.data || [], input.reportType || 'summary', input.timeRange || '24h', input.includeRecommendations || true);
}
else if (input.outputFormat === 'json') {
content = {
summary,
topErrors: topErrorsResult.data || [],
timeRange: input.timeRange,
generatedAt: new Date().toISOString()
};
}
else {
// CSV format
const headers = ['Type', 'Count', 'Recovery Rate'];
const rows = (topErrorsResult.data || []).map((e) => [e.error_type, e.count, `${Math.round(e.recovery_rate * 100)}%`]);
content = [headers, ...rows].map(row => row.join(',')).join('\n');
}
let filePath;
if (input.saveToFile) {
// In a real implementation, this would save to the file system
// For now, we'll just indicate where it would be saved
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const extension = input.outputFormat === 'markdown' ? 'md' : input.outputFormat;
filePath = `.atlas/reports/error-report-${timestamp}.${extension}`;
}
return createSuccessResult({
content,
filePath,
reportType: input.reportType,
timeRange: input.timeRange,
outputFormat: input.outputFormat,
summary: {
totalErrors: summary.total_errors || 0,
criticalErrors: summary.critical_errors || 0,
recoveryRate: summary.total_errors > 0 ?
(summary.recovered_errors || 0) / summary.total_errors : 0
}
});
}
catch (error) {
return createErrorResult({
code: 'EXECUTION_ERROR',
message: `Failed to generate error report: ${error instanceof Error ? error.message : 'Unknown error'}`,
category: 'execution'
});
}
}
});
/**
* Resolve error suggestion
*/
const resolveErrorSuggestionTool = createTool({
name: 'resolve_error_suggestion',
description: 'Mark an error resolution suggestion as implemented and track its effectiveness.',
category: 'error-analysis',
inputSchema: {
type: 'object',
properties: {
errorId: {
type: 'string',
description: 'ID of the error that was addressed',
minLength: 1
},
suggestionId: {
type: 'string',
description: 'ID of the suggestion that was implemented',
minLength: 1
},
implementation: {
type: 'string',
description: 'Description of how the suggestion was implemented'
},
effectiveness: {
type: 'number',
minimum: 1,
maximum: 10,
description: 'Effectiveness rating (1-10) of the implemented solution'
},
notes: {
type: 'string',
description: 'Additional notes about the implementation'
}
},
required: ['errorId', 'suggestionId'],
additionalProperties: false
},
async execute(input, context) {
try {
const resolutionId = randomUUID();
const now = Date.now();
// Insert resolution record
const result = await context.db.run(`INSERT INTO error_resolutions
(id, error_id, suggestion_id, project_id, implementation,
effectiveness, notes, resolved_by, resolved_at, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
resolutionId,
input.errorId,
input.suggestionId,
context.projectId || 'default',
input.implementation || '',
input.effectiveness || null,
input.notes || '',
context.userId || 'system',
now,
now
]);
if (!result.success) {
return createErrorResult({
code: 'DATABASE_ERROR',
message: 'Failed to track error resolution',
details: { error: result.error },
category: 'system'
});
}
// Update error log to mark as resolved
await context.db.run(`UPDATE error_logs SET resolution_id = ? WHERE id = ?`, [resolutionId, input.errorId]);
return createSuccessResult({
errorId: input.errorId,
suggestionId: input.suggestionId,
resolutionId,
implementation: input.implementation,
effectiveness: input.effectiveness,
notes: input.notes,
outcome: {
success: true,
message: 'Resolution tracked successfully'
}
});
}
catch (error) {
return createErrorResult({
code: 'EXECUTION_ERROR',
message: `Failed to resolve error suggestion: ${error instanceof Error ? error.message : 'Unknown error'}`,
category: 'execution'
});
}
}
});
/**
* Simulate error recovery
*/
const simulateErrorRecoveryTool = createTool({
name: 'simulate_error_recovery',
description: 'Simulate error recovery procedures to test and validate error handling mechanisms.',
category: 'error-analysis',
inputSchema: {
type: 'object',
properties: {
errorType: {
type: 'string',
enum: ['validation', 'network', 'permission', 'resource', 'logic', 'external'],
description: 'Type of error to simulate'
},
severity: {
type: 'string',
enum: ['low', 'medium', 'high', 'critical'],
default: 'medium',
description: 'Severity level for simulation'
},
context: {
type: 'object',
description: 'Simulation context and parameters'
},
dryRun: {
type: 'boolean',
default: true,
description: 'Run simulation without triggering actual errors'
}
},
required: ['errorType'],
additionalProperties: false
},
async execute(input, context) {
try {
const simulationId = randomUUID();
const startTime = Date.now();
// Define recovery steps based on error type
const recoverySteps = getRecoverySteps(input.errorType, input.severity || 'medium');
const steps = [];
let overallSuccess = true;
// Execute simulation steps
for (const step of recoverySteps) {
const stepStart = Date.now();
const stepResult = {
name: step.name,
action: step.action,
success: true,
simulated: input.dryRun || true,
duration: 0,
message: ''
};
// Simulate step execution
await new Promise(resolve => setTimeout(resolve, step.simulatedDuration || 100));
stepResult.duration = Date.now() - stepStart;
// Simulate potential failures based on error type and severity
if (!input.dryRun && Math.random() < step.failureChance) {
stepResult.success = false;
stepResult.message = `Step failed: ${step.failureMessage}`;
overallSuccess = false;
}
steps.push(stepResult);
// Stop if step failed and it's critical
if (!stepResult.success && step.critical) {
break;
}
}
const totalDuration = Date.now() - startTime;
const effectiveness = overallSuccess ? 95 : 50;
// Log simulation
await context.db.run(`INSERT INTO error_simulations
(id, project_id, error_type, severity, dry_run, steps,
success, duration, effectiveness, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
simulationId,
context.projectId || 'default',
input.errorType,
input.severity || 'medium',
input.dryRun ? 1 : 0,
JSON.stringify(steps),
overallSuccess ? 1 : 0,
totalDuration,
effectiveness,
Date.now()
]);
return createSuccessResult({
errorType: input.errorType,
severity: input.severity || 'medium',
steps,
results: {
success: overallSuccess,
recoverable: true,
issues: steps.filter(s => !s.success).map(s => s.message),
recommendations: overallSuccess ?
['Recovery mechanisms are working correctly'] :
['Review failed recovery steps', 'Implement fallback mechanisms']
},
recoveryTime: `${totalDuration}ms`,
effectiveness: `${effectiveness}%`,
dryRun: input.dryRun || true
});
}
catch (error) {
return createErrorResult({
code: 'EXECUTION_ERROR',
message: `Failed to simulate error recovery: ${error instanceof Error ? error.message : 'Unknown error'}`,
category: 'execution'
});
}
}
});
// Helper functions
function generateMarkdownReport(summary, topErrors, reportType, timeRange, includeRecommendations) {
let report = `# Error Analysis Report\n\n`;
report += `**Time Range**: ${timeRange}\n`;
report += `**Report Type**: ${reportType}\n`;
report += `**Generated**: ${new Date().toISOString()}\n\n`;
report += `## Summary\n`;
report += `- **Total Errors**: ${summary.total_errors || 0}\n`;
report += `- **Critical Errors**: ${summary.critical_errors || 0}\n`;
report += `- **High Priority Errors**: ${summary.high_errors || 0}\n`;
report += `- **Affected Tools**: ${summary.affected_tools || 0}\n`;
report += `- **Error Types**: ${summary.error_types || 0}\n`;
report += `- **Recovery Rate**: ${summary.total_errors > 0 ?
Math.round((summary.recovered_errors || 0) / summary.total_errors * 100) : 0}%\n\n`;
if (topErrors.length > 0) {
report += `## Top Error Types\n`;
topErrors.forEach((error, i) => {
report += `${i + 1}. **${error.error_type}** - ${error.count} occurrences`;
report += ` (${Math.round(error.recovery_rate * 100)}% recovery rate)\n`;
});
report += '\n';
}
if (includeRecommendations) {
report += `## Recommendations\n`;
if (summary.critical_errors > 0) {
report += `- Address ${summary.critical_errors} critical errors immediately\n`;
}
if (summary.total_errors > 0 && (summary.recovered_errors || 0) / summary.total_errors < 0.8) {
report += `- Improve error recovery mechanisms (current rate: ${Math.round((summary.recovered_errors || 0) / summary.total_errors * 100)}%)\n`;
}
if (topErrors.length > 0 && topErrors[0].count > summary.total_errors * 0.3) {
report += `- Focus on reducing ${topErrors[0].error_type} errors (${Math.round(topErrors[0].count / summary.total_errors * 100)}% of all errors)\n`;
}
}
return report;
}
function getRecoverySteps(errorType, severity) {
const baseSteps = [
{
name: 'Detection',
action: 'Detect and classify error',
simulatedDuration: 50,
failureChance: 0.05,
failureMessage: 'Error detection failed',
critical: true
},
{
name: 'Isolation',
action: 'Isolate affected components',
simulatedDuration: 100,
failureChance: 0.1,
failureMessage: 'Component isolation failed',
critical: false
}
];
const typeSpecificSteps = {
validation: [
{
name: 'Data Cleanup',
action: 'Clean and validate input data',
simulatedDuration: 150,
failureChance: 0.15,
failureMessage: 'Data cleanup failed',
critical: false
}
],
network: [
{
name: 'Connection Retry',
action: 'Retry network connection',
simulatedDuration: 200,
failureChance: 0.2,
failureMessage: 'Connection retry failed',
critical: false
},
{
name: 'Fallback',
action: 'Switch to fallback endpoint',
simulatedDuration: 100,
failureChance: 0.1,
failureMessage: 'Fallback activation failed',
critical: false
}
],
permission: [
{
name: 'Permission Check',
action: 'Re-validate permissions',
simulatedDuration: 100,
failureChance: 0.1,
failureMessage: 'Permission validation failed',
critical: true
}
],
resource: [
{
name: 'Resource Release',
action: 'Release locked resources',
simulatedDuration: 150,
failureChance: 0.15,
failureMessage: 'Resource release failed',
critical: false
},
{
name: 'Resource Allocation',
action: 'Allocate new resources',
simulatedDuration: 200,
failureChance: 0.2,
failureMessage: 'Resource allocation failed',
critical: true
}
],
logic: [
{
name: 'State Reset',
action: 'Reset to known good state',
simulatedDuration: 100,
failureChance: 0.1,
failureMessage: 'State reset failed',
critical: false
}
],
external: [
{
name: 'External Service Check',
action: 'Verify external service status',
simulatedDuration: 300,
failureChance: 0.25,
failureMessage: 'External service unavailable',
critical: false
}
]
};
const finalSteps = [
{
name: 'Verification',
action: 'Verify system recovery',
simulatedDuration: 100,
failureChance: 0.05,
failureMessage: 'Recovery verification failed',
critical: true
},
{
name: 'Logging',
action: 'Log recovery details',
simulatedDuration: 50,
failureChance: 0.02,
failureMessage: 'Logging failed',
critical: false
}
];
return [
...baseSteps,
...(typeSpecificSteps[errorType] || []),
...finalSteps
];
}
/**
* Setup error analysis tools
*/
export async function setupErrorAnalysisTools() {
return {
module: 'error-analysis',
tools: [
getErrorDetailsTool,
analyzeErrorPatternsTool,
getErrorTimelineTool,
generateErrorReportTool,
resolveErrorSuggestionTool,
simulateErrorRecoveryTool
]
};
}
//# sourceMappingURL=tools.js.map