claude-expert-workflow-mcp
Version:
Production-ready MCP server for AI-powered product development consultation through specialized expert roles. Enterprise-grade with memory management, monitoring, and Claude Code integration.
519 lines (466 loc) • 16.5 kB
text/typescript
// Centralized Error Handler for Claude Expert Workflow MCP
// Standardizes error responses across all system components
import { randomUUID } from 'crypto';
import { correlationTracker } from './correlationTracker';
// Standard MCP Response interface
export interface MCPResponse {
content: Array<{
type: "text";
text: string;
}>;
}
// Error context for enhanced debugging
export interface ErrorContext {
operation: string;
component: string;
processingMode?: 'subscription' | 'api';
conversationId?: string;
timestamp: number;
userInput?: string;
systemState?: any;
}
// Standardized error response structure
export interface StandardErrorResponse {
success: false;
error: string;
errorType: string;
correlationId: string;
context: ErrorContext;
timestamp: number;
retryable: boolean;
suggestions?: string[];
}
// Error categories for systematic handling
export enum ErrorType {
// System-level errors
SYSTEM_ERROR = 'SYSTEM_ERROR',
MEMORY_ERROR = 'MEMORY_ERROR',
CONFIGURATION_ERROR = 'CONFIGURATION_ERROR',
// Integration errors
ANTHROPIC_API_ERROR = 'ANTHROPIC_API_ERROR',
TASKMASTER_API_ERROR = 'TASKMASTER_API_ERROR',
MCP_TRANSPORT_ERROR = 'MCP_TRANSPORT_ERROR',
// Business logic errors
INVALID_INPUT_ERROR = 'INVALID_INPUT_ERROR',
STATE_ERROR = 'STATE_ERROR',
WORKFLOW_ERROR = 'WORKFLOW_ERROR',
// Extended Thinking specific errors
EXTENDED_THINKING_ERROR = 'EXTENDED_THINKING_ERROR',
THINKING_BLOCK_ERROR = 'THINKING_BLOCK_ERROR',
// Processing mode errors
MODE_COMPATIBILITY_ERROR = 'MODE_COMPATIBILITY_ERROR',
FEATURE_UNAVAILABLE_ERROR = 'FEATURE_UNAVAILABLE_ERROR'
}
/**
* Centralized Error Handler
* Provides consistent error response formatting across all system components
*/
export class MCPErrorHandler {
private static correlationMap = new Map<string, ErrorContext>();
/**
* Create a standardized MCP error response
*/
static formatResponse(
error: Error | string,
context: ErrorContext,
errorType: ErrorType = ErrorType.SYSTEM_ERROR,
correlationId?: string
): MCPResponse {
const id = correlationId || this.generateCorrelationId();
const errorMessage = error instanceof Error ? error.message : error;
// Store context for debugging
this.correlationMap.set(id, {
...context,
timestamp: Date.now()
});
const errorResponse: StandardErrorResponse = {
success: false,
error: errorMessage,
errorType,
correlationId: id,
context: {
...context,
timestamp: Date.now()
},
timestamp: Date.now(),
retryable: this.isRetryableError(errorType),
suggestions: this.getSuggestions(errorType, context)
};
// Log the error with full context
this.logError(error, context, id, errorType);
// Link with correlation tracker for enhanced debugging
this.linkWithCorrelationTracker(id, context, errorType);
return {
content: [{
type: "text",
text: JSON.stringify(errorResponse, null, 2)
}]
};
}
/**
* Handle Extended Thinking API errors specifically
* Critical fix for the exception propagation issue identified in analysis
*/
static handleExtendedThinkingError(
error: Error | string,
context: ErrorContext,
userInput: string,
correlationId?: string
): MCPResponse {
const enhancedContext: ErrorContext = {
...context,
operation: 'Extended Thinking API Call',
component: 'anthropicUtils',
userInput,
timestamp: Date.now()
};
// Provide specific suggestions for Extended Thinking failures
const suggestions = [
"Check ANTHROPIC_API_KEY configuration",
"Verify network connectivity to Anthropic API",
"Check if Extended Thinking is enabled (ENABLE_EXTENDED_THINKING=true)",
"Try again without Extended Thinking if issue persists",
"Contact support if API key is valid but calls continue failing"
];
const errorResponse: StandardErrorResponse = {
success: false,
error: error instanceof Error ? error.message : error,
errorType: ErrorType.EXTENDED_THINKING_ERROR,
correlationId: correlationId || this.generateCorrelationId(),
context: enhancedContext,
timestamp: Date.now(),
retryable: true, // Extended Thinking errors are often transient
suggestions
};
this.logError(error, enhancedContext, errorResponse.correlationId, ErrorType.EXTENDED_THINKING_ERROR);
return {
content: [{
type: "text",
text: JSON.stringify(errorResponse, null, 2)
}]
};
}
/**
* Handle Task Master AI integration errors
*/
static handleTaskMasterError(
error: Error | string,
context: ErrorContext,
documentType?: string,
correlationId?: string
): MCPResponse {
const enhancedContext: ErrorContext = {
...context,
operation: 'Task Master AI Integration',
component: 'taskmasterIntegration',
systemState: { documentType },
timestamp: Date.now()
};
const suggestions = [
"Check if Task Master AI integration is enabled (TASKMASTER_INTEGRATION_ENABLED=true)",
"Verify task-master-ai package is installed: npm install task-master-ai",
"Check TASKMASTER_API_ENDPOINT configuration",
"Ensure API processing mode is active (Task Master AI requires API mode)",
"Try with a different document type if current type is unsupported"
];
const errorResponse: StandardErrorResponse = {
success: false,
error: error instanceof Error ? error.message : error,
errorType: ErrorType.TASKMASTER_API_ERROR,
correlationId: correlationId || this.generateCorrelationId(),
context: enhancedContext,
timestamp: Date.now(),
retryable: false, // Task Master errors usually require configuration fixes
suggestions
};
this.logError(error, enhancedContext, errorResponse.correlationId, ErrorType.TASKMASTER_API_ERROR);
return {
content: [{
type: "text",
text: JSON.stringify(errorResponse, null, 2)
}]
};
}
/**
* Handle processing mode compatibility errors
*/
static handleModeCompatibilityError(
requestedFeature: string,
currentMode: 'subscription' | 'api',
requiredMode: 'subscription' | 'api',
context: ErrorContext,
correlationId?: string
): MCPResponse {
const enhancedContext: ErrorContext = {
...context,
operation: 'Mode Compatibility Check',
component: 'dual-mode-server',
processingMode: currentMode,
systemState: { requestedFeature, currentMode, requiredMode },
timestamp: Date.now()
};
const suggestions = [
`${requestedFeature} requires ${requiredMode} processing mode`,
currentMode === 'subscription' && requiredMode === 'api'
? "Set ANTHROPIC_API_KEY to enable API processing mode"
: "Use setProcessingMode tool to switch modes (requires restart)",
"Check system status with getSystemStatus tool",
`Alternative: Use ${currentMode} mode compatible features instead`
];
const errorResponse: StandardErrorResponse = {
success: false,
error: `Feature '${requestedFeature}' is not available in ${currentMode} processing mode. Requires ${requiredMode} mode.`,
errorType: ErrorType.MODE_COMPATIBILITY_ERROR,
correlationId: correlationId || this.generateCorrelationId(),
context: enhancedContext,
timestamp: Date.now(),
retryable: false, // Mode compatibility requires configuration change
suggestions
};
this.logError(
new Error(`Mode compatibility error: ${requestedFeature} requires ${requiredMode} mode`),
enhancedContext,
errorResponse.correlationId,
ErrorType.MODE_COMPATIBILITY_ERROR
);
return {
content: [{
type: "text",
text: JSON.stringify(errorResponse, null, 2)
}]
};
}
/**
* Log errors with full context for debugging
*/
private static logError(
error: Error | string,
context: ErrorContext,
correlationId: string,
errorType: ErrorType
): void {
const errorMessage = error instanceof Error ? error.message : error;
const stack = error instanceof Error ? error.stack : 'No stack trace';
console.error(`[ERROR] ${errorType} [${correlationId}]`);
console.error(`Message: ${errorMessage}`);
console.error(`Operation: ${context.operation}`);
console.error(`Component: ${context.component}`);
console.error(`Processing Mode: ${context.processingMode || 'unknown'}`);
console.error(`Conversation ID: ${context.conversationId || 'none'}`);
console.error(`Timestamp: ${new Date(context.timestamp).toISOString()}`);
console.error(`Stack: ${stack}`);
if (context.userInput) {
console.error(`User Input: ${context.userInput.substring(0, 200)}...`);
}
if (context.systemState) {
console.error(`System State: ${JSON.stringify(context.systemState)}`);
}
}
/**
* Generate correlation ID for error tracking
*/
private static generateCorrelationId(): string {
return `err_${Date.now()}_${randomUUID().substring(0, 8)}`;
}
/**
* Link error correlation ID with main correlation tracker for enhanced debugging
*/
private static linkWithCorrelationTracker(
errorCorrelationId: string,
context: ErrorContext,
errorType: ErrorType
): void {
try {
// Try to find an active request that matches this error context
const activeRequests = correlationTracker.getActiveRequests();
for (const request of activeRequests) {
// Match by conversation ID, tool name, or operation type
if (
(context.conversationId && request.conversationId === context.conversationId) ||
(context.component && request.operationType.includes(context.component)) ||
(request.toolName && context.operation.toLowerCase().includes(request.toolName.toLowerCase()))
) {
// Update the correlation tracker with error information
correlationTracker.updateRequest(request.correlationId, {
metadata: {
...request.metadata,
errorCorrelationId,
errorType: errorType.toString(),
errorContext: context,
errorOccurredAt: Date.now()
}
});
console.error(`[CORRELATION-LINK] ${request.correlationId} → ${errorCorrelationId} | ${errorType}`);
break;
}
}
} catch (linkError) {
// Don't let correlation linking errors affect the main error flow
console.error(`[CORRELATION-LINK-ERROR] Failed to link ${errorCorrelationId}:`, linkError);
}
}
/**
* Determine if an error type is retryable
*/
private static isRetryableError(errorType: ErrorType): boolean {
const retryableErrors = [
ErrorType.ANTHROPIC_API_ERROR,
ErrorType.EXTENDED_THINKING_ERROR,
ErrorType.MCP_TRANSPORT_ERROR,
ErrorType.MEMORY_ERROR
];
return retryableErrors.includes(errorType);
}
/**
* Provide context-specific suggestions for error resolution
*/
private static getSuggestions(errorType: ErrorType, context: ErrorContext): string[] {
const baseSuggestions: Record<ErrorType, string[]> = {
[ErrorType.SYSTEM_ERROR]: [
"Check system resources and memory usage",
"Verify all dependencies are installed",
"Check logs for additional error context"
],
[ErrorType.MEMORY_ERROR]: [
"System may be running low on memory",
"Consider restarting the MCP server",
"Check for memory leaks in conversation state"
],
[ErrorType.CONFIGURATION_ERROR]: [
"Verify environment variables are set correctly",
"Check .env file configuration",
"Ensure all required settings are present"
],
[ErrorType.ANTHROPIC_API_ERROR]: [
"Check ANTHROPIC_API_KEY is valid and has credits",
"Verify network connectivity",
"Try again - may be temporary API issue"
],
[ErrorType.TASKMASTER_API_ERROR]: [
"Check Task Master AI configuration",
"Verify task-master-ai package is installed",
"Ensure API processing mode is active"
],
[ErrorType.MCP_TRANSPORT_ERROR]: [
"Check MCP transport connection",
"Verify Claude Code is connected properly",
"Try reconnecting the MCP server"
],
[ErrorType.INVALID_INPUT_ERROR]: [
"Check input parameters are valid",
"Verify required fields are provided",
"Review input format requirements"
],
[ErrorType.STATE_ERROR]: [
"Check conversation state integrity",
"Try starting a new conversation",
"Clear conversation state if corrupted"
],
[ErrorType.WORKFLOW_ERROR]: [
"Check workflow stage and progression",
"Verify expert consultation flow",
"Review workflow state data"
],
[ErrorType.EXTENDED_THINKING_ERROR]: [
"Check Extended Thinking configuration",
"Verify ANTHROPIC_API_KEY supports Extended Thinking",
"Try without Extended Thinking if issue persists"
],
[ErrorType.THINKING_BLOCK_ERROR]: [
"Check thinking block preservation logic",
"Verify memory limits for thinking blocks",
"Clear thinking history if corrupted"
],
[ErrorType.MODE_COMPATIBILITY_ERROR]: [
"Check current processing mode",
"Switch to required mode if needed",
"Use mode-compatible alternatives"
],
[ErrorType.FEATURE_UNAVAILABLE_ERROR]: [
"Feature may not be available in current mode",
"Check system configuration",
"Try alternative approaches"
]
};
return baseSuggestions[errorType] || ["Contact support for assistance"];
}
/**
* Get error context by correlation ID for debugging
*/
static getErrorContext(correlationId: string): ErrorContext | null {
return this.correlationMap.get(correlationId) || null;
}
/**
* Clean up old error contexts to prevent memory leaks
*/
static cleanupErrorContexts(maxAge: number = 3600000): void { // 1 hour default
const now = Date.now();
for (const [id, context] of this.correlationMap.entries()) {
if (now - context.timestamp > maxAge) {
this.correlationMap.delete(id);
}
}
}
/**
* Get system-wide error statistics for monitoring
*/
static getErrorStatistics(): {
totalErrors: number;
errorsByType: Record<string, number>;
recentErrors: number;
} {
const now = Date.now();
const recentThreshold = 300000; // 5 minutes
const errorsByType: Record<string, number> = {};
let recentErrors = 0;
for (const context of this.correlationMap.values()) {
// This is a simplified version - in practice, we'd need to track error types
if (now - context.timestamp < recentThreshold) {
recentErrors++;
}
}
return {
totalErrors: this.correlationMap.size,
errorsByType,
recentErrors
};
}
}
/**
* Convenience functions for common error scenarios
*/
// Extended Thinking error wrapper - critical fix for identified issue
export function handleExtendedThinkingError(error: Error | string, userInput: string, correlationId?: string): MCPResponse {
return MCPErrorHandler.handleExtendedThinkingError(error, {
operation: 'Extended Thinking API Call',
component: 'anthropicUtils',
timestamp: Date.now()
}, userInput, correlationId);
}
// Task Master AI error wrapper
export function handleTaskMasterError(error: Error | string, documentType?: string, correlationId?: string): MCPResponse {
return MCPErrorHandler.handleTaskMasterError(error, {
operation: 'Task Master AI Operation',
component: 'taskmasterIntegration',
timestamp: Date.now()
}, documentType, correlationId);
}
// Mode compatibility error wrapper
export function handleModeCompatibilityError(
requestedFeature: string,
currentMode: 'subscription' | 'api',
requiredMode: 'subscription' | 'api',
correlationId?: string
): MCPResponse {
return MCPErrorHandler.handleModeCompatibilityError(
requestedFeature,
currentMode,
requiredMode,
{
operation: 'Mode Compatibility Check',
component: 'dual-mode-server',
timestamp: Date.now()
},
correlationId
);
}