pms-analysis-reports-mcp-server
Version:
PMS analysis reports server handling maintenance reports, equipment analysis, compliance tracking, and performance metrics with ERP access for data extraction
239 lines • 9.32 kB
JavaScript
import { logger } from "../utils/logger.js";
import { SanitizationMiddleware } from "./sanitization-middleware.js";
export var ErrorCodes;
(function (ErrorCodes) {
ErrorCodes["VALIDATION_ERROR"] = "VALIDATION_ERROR";
ErrorCodes["AUTHENTICATION_ERROR"] = "AUTHENTICATION_ERROR";
ErrorCodes["AUTHORIZATION_ERROR"] = "AUTHORIZATION_ERROR";
ErrorCodes["RESOURCE_NOT_FOUND"] = "RESOURCE_NOT_FOUND";
ErrorCodes["DATABASE_ERROR"] = "DATABASE_ERROR";
ErrorCodes["EXTERNAL_API_ERROR"] = "EXTERNAL_API_ERROR";
ErrorCodes["RATE_LIMIT_EXCEEDED"] = "RATE_LIMIT_EXCEEDED";
ErrorCodes["INTERNAL_ERROR"] = "INTERNAL_ERROR";
ErrorCodes["INVALID_INPUT"] = "INVALID_INPUT";
ErrorCodes["TIMEOUT_ERROR"] = "TIMEOUT_ERROR";
})(ErrorCodes || (ErrorCodes = {}));
export class ErrorHandler {
// Central error processing method
static handleError(error, context = {}) {
const timestamp = new Date();
const requestId = context.requestId || this.generateRequestId();
// Log the full error for debugging (with sanitization)
const sanitizedContext = SanitizationMiddleware.sanitizeForLogging(context);
logger.error('Error occurred', {
error: {
message: error.message,
stack: error.stack,
name: error.name
},
context: sanitizedContext,
requestId,
timestamp
});
// Determine error type and create standardized response
const standardError = this.categorizeError(error, context);
standardError.timestamp = timestamp;
standardError.requestId = requestId;
return standardError;
}
// Categorize errors and return appropriate user-facing messages
static categorizeError(error, context) {
// MongoDB errors
if (error.name === 'MongoError' || error.name === 'MongoServerError') {
return {
code: ErrorCodes.DATABASE_ERROR,
message: 'Database operation failed. Please try again later.',
timestamp: new Date()
};
}
// Validation errors
if (error.name === 'ValidationError' || error.code === 'VALIDATION_ERROR') {
return {
code: ErrorCodes.VALIDATION_ERROR,
message: 'Invalid input provided. Please check your parameters.',
details: error.details ? SanitizationMiddleware.sanitizeForLogging(error.details) : undefined,
timestamp: new Date()
};
}
// Network/timeout errors
if (error.code === 'ECONNRESET' || error.code === 'ETIMEDOUT' || error.name === 'TimeoutError') {
return {
code: ErrorCodes.TIMEOUT_ERROR,
message: 'Request timed out. Please try again.',
timestamp: new Date()
};
}
// External API errors
if (error.response && error.response.status) {
const status = error.response.status;
if (status === 429) {
return {
code: ErrorCodes.RATE_LIMIT_EXCEEDED,
message: 'Rate limit exceeded. Please wait before making another request.',
timestamp: new Date()
};
}
else if (status === 404) {
return {
code: ErrorCodes.RESOURCE_NOT_FOUND,
message: 'Requested resource not found.',
timestamp: new Date()
};
}
else if (status >= 500) {
return {
code: ErrorCodes.EXTERNAL_API_ERROR,
message: 'External service unavailable. Please try again later.',
timestamp: new Date()
};
}
}
// Authentication/Authorization errors
if (error.name === 'UnauthorizedError' || error.status === 401) {
return {
code: ErrorCodes.AUTHENTICATION_ERROR,
message: 'Authentication required.',
timestamp: new Date()
};
}
if (error.name === 'ForbiddenError' || error.status === 403) {
return {
code: ErrorCodes.AUTHORIZATION_ERROR,
message: 'Access denied.',
timestamp: new Date()
};
}
// Default to internal error
return {
code: ErrorCodes.INTERNAL_ERROR,
message: 'An internal error occurred. Please try again later.',
timestamp: new Date()
};
}
// Handle validation errors specifically
static handleValidationError(errors, context = {}) {
const sanitizedErrors = errors.map(error => ({
field: error.field,
message: SanitizationMiddleware.sanitizeErrorMessage(error.message)
}));
logger.warn('Validation failed', {
errors: sanitizedErrors,
context: SanitizationMiddleware.sanitizeForLogging(context),
requestId: context.requestId
});
return {
code: ErrorCodes.VALIDATION_ERROR,
message: 'Input validation failed.',
details: sanitizedErrors,
timestamp: new Date(),
requestId: context.requestId
};
}
// Handle database connection errors
static handleDatabaseError(error, operation, context = {}) {
logger.error(`Database error during ${operation}`, {
error: {
message: error.message,
code: error.code,
name: error.name
},
operation,
context: SanitizationMiddleware.sanitizeForLogging(context)
});
return {
code: ErrorCodes.DATABASE_ERROR,
message: 'Database operation failed. Please try again later.',
timestamp: new Date(),
requestId: context.requestId
};
}
// Handle external API errors
static handleExternalAPIError(error, apiName, context = {}) {
const status = error.response?.status;
const statusText = error.response?.statusText;
logger.error(`External API error - ${apiName}`, {
error: {
message: error.message,
status,
statusText
},
apiName,
context: SanitizationMiddleware.sanitizeForLogging(context)
});
if (status === 429) {
return {
code: ErrorCodes.RATE_LIMIT_EXCEEDED,
message: `${apiName} rate limit exceeded. Please wait before retrying.`,
timestamp: new Date(),
requestId: context.requestId
};
}
return {
code: ErrorCodes.EXTERNAL_API_ERROR,
message: `${apiName} service unavailable. Please try again later.`,
timestamp: new Date(),
requestId: context.requestId
};
}
// Generate unique request ID for tracking
static generateRequestId() {
return `req_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
}
// Create a safe error response for tool execution
static createToolErrorResponse(error, toolName, args = {}) {
const context = {
toolName,
arguments: args,
timestamp: new Date()
};
const standardError = this.handleError(error, context);
return {
success: false,
error: {
code: standardError.code,
message: standardError.message,
timestamp: standardError.timestamp,
requestId: standardError.requestId
},
data: null
};
}
// Graceful error handling with fallback
static async safeExecute(operation, fallbackValue, context = {}) {
try {
return await operation();
}
catch (error) {
this.handleError(error, context);
return fallbackValue;
}
}
// Error recovery with retry logic
static async executeWithRetry(operation, maxRetries = 3, delayMs = 1000, context = {}) {
let lastError;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await operation();
}
catch (error) {
lastError = error;
if (attempt === maxRetries) {
break;
}
// Log retry attempt
logger.warn(`Retry attempt ${attempt}/${maxRetries}`, {
error: error.message,
context: SanitizationMiddleware.sanitizeForLogging(context)
});
// Wait before retry
await new Promise(resolve => setTimeout(resolve, delayMs * attempt));
}
}
const errorToThrow = lastError || new Error('Unknown error');
const standardError = this.handleError(errorToThrow, context);
const error = new Error(standardError.message);
error.code = standardError.code;
throw error;
}
}
//# sourceMappingURL=error-handler.js.map