// Error handling utilities for Ultimate Elementor MCP
import { logger } from './logger.js';
export enum ErrorCategory {
AUTHENTICATION = 'AUTHENTICATION',
VALIDATION = 'VALIDATION',
NETWORK = 'NETWORK',
WORDPRESS_API = 'WORDPRESS_API',
ELEMENTOR_DATA = 'ELEMENTOR_DATA',
FILE_OPERATION = 'FILE_OPERATION',
CONFIGURATION = 'CONFIGURATION',
INTERNAL = 'INTERNAL',
}
export class MCPError extends Error {
category: ErrorCategory;
code: string;
details?: any;
statusCode: number | undefined;
constructor(
message: string,
category: ErrorCategory,
code: string,
details?: any,
statusCode?: number
) {
super(message);
this.name = 'MCPError';
this.category = category;
this.code = code;
this.details = details;
this.statusCode = statusCode;
Error.captureStackTrace(this, this.constructor);
}
toJSON() {
return {
name: this.name,
message: this.message,
category: this.category,
code: this.code,
details: this.details,
statusCode: this.statusCode,
};
}
}
export class ErrorHandler {
static handle(error: any): MCPError {
// If it's already our custom error, record and return it
if (error instanceof MCPError) {
logger.error(`MCPError: ${error.message}`, error);
errorStats.recordError(error);
return error;
}
// Handle axios errors
if (error.response) {
const status = error.response.status;
const data = error.response.data;
if (status === 401 || status === 403) {
return new MCPError(
'Authentication failed. Please check your WordPress credentials.',
ErrorCategory.AUTHENTICATION,
'AUTH_FAILED',
data,
status
);
}
return new MCPError(
data.message || 'WordPress API request failed',
ErrorCategory.WORDPRESS_API,
`HTTP_${status}`,
data,
status
);
}
// Handle network errors
if (error.request) {
return new MCPError(
'Network error. Cannot reach WordPress server.',
ErrorCategory.NETWORK,
'NETWORK_ERROR',
{ originalError: error.message }
);
}
// Handle validation errors
if (error.name === 'ValidationError' || error.name === 'ZodError') {
return new MCPError(
'Validation failed',
ErrorCategory.VALIDATION,
'VALIDATION_ERROR',
error.errors || error.issues
);
}
// Generic error handler
logger.error('Unhandled error', error);
const mcpError = new MCPError(
error.message || 'An unexpected error occurred',
ErrorCategory.INTERNAL,
'INTERNAL_ERROR',
{ originalError: error.toString(), stack: error.stack }
);
errorStats.recordError(mcpError);
return mcpError;
}
static getErrorStatistics(): any {
return errorStats.getStatistics();
}
static clearErrorStatistics(): void {
errorStats.clearStatistics();
}
static isRecoverableError(error: MCPError): boolean {
// Determine if an error is recoverable
const recoverableCategories = [
ErrorCategory.NETWORK,
ErrorCategory.VALIDATION
];
return recoverableCategories.includes(error.category);
}
static getSuggestion(error: MCPError): string {
// Provide helpful suggestions based on error type
switch (error.category) {
case ErrorCategory.AUTHENTICATION:
return 'Check your WordPress credentials and ensure Application Password is correct';
case ErrorCategory.NETWORK:
return 'Verify WordPress URL is correct and server is reachable';
case ErrorCategory.VALIDATION:
return 'Check the input parameters and try again';
case ErrorCategory.FILE_OPERATION:
return 'Verify file path exists and has proper permissions';
case ErrorCategory.CONFIGURATION:
return 'Review configuration settings and environment variables';
default:
return 'Check the error details for more information';
}
}
static async wrapAsync<T>(
fn: () => Promise<T>,
errorContext?: string
): Promise<T> {
try {
return await fn();
} catch (error) {
const mcpError = ErrorHandler.handle(error);
if (errorContext) {
mcpError.details = { ...mcpError.details, context: errorContext };
}
throw mcpError;
}
}
}
export function createErrorResponse(error: MCPError) {
return {
content: [{
type: 'text',
text: JSON.stringify({
error: true,
message: error.message,
category: error.category,
code: error.code,
details: error.details,
suggestion: ErrorHandler.getSuggestion(error),
recoverable: ErrorHandler.isRecoverableError(error)
}, null, 2)
}],
isError: true,
};
}
// Error Statistics Tracker
class ErrorStatistics {
private errors: Map<string, number> = new Map();
private errorHistory: Array<{ timestamp: Date; category: string; code: string; message: string }> = [];
private maxHistorySize: number = 100;
recordError(error: MCPError): void {
const key = `${error.category}:${error.code}`;
this.errors.set(key, (this.errors.get(key) || 0) + 1);
this.errorHistory.unshift({
timestamp: new Date(),
category: error.category,
code: error.code,
message: error.message
});
// Keep history size manageable
if (this.errorHistory.length > this.maxHistorySize) {
this.errorHistory = this.errorHistory.slice(0, this.maxHistorySize);
}
}
getStatistics(): any {
const byCategory: Record<string, number> = {};
const byCode: Record<string, number> = {};
this.errors.forEach((count, key) => {
const parts = key.split(':');
const category = parts[0] || 'unknown';
const code = parts[1] || 'unknown';
byCategory[category] = (byCategory[category] || 0) + count;
byCode[code] = count;
});
return {
totalErrors: Array.from(this.errors.values()).reduce((sum, count) => sum + count, 0),
byCategory,
byCode,
recentErrors: this.errorHistory.slice(0, 10)
};
}
clearStatistics(): void {
this.errors.clear();
this.errorHistory = [];
}
}
export const errorStats = new ErrorStatistics();
// Enhanced ErrorHandler