defect-inspection-tools-mcp-server
Version:
Defect inspection tools server handling defect detection, analysis, and reporting with AI/ML capabilities for quality control
345 lines • 13.3 kB
JavaScript
import { logger } from '../utils/logger.js';
import { SanitizationMiddleware } from './sanitization-middleware.js';
import { randomUUID } from 'crypto';
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["TYPESENSE_ERROR"] = "TYPESENSE_ERROR";
ErrorCodes["MARITIME_VALIDATION_ERROR"] = "MARITIME_VALIDATION_ERROR";
})(ErrorCodes || (ErrorCodes = {}));
export class ErrorHandler {
// Main error handling method
static handleError(error, context) {
try {
const requestId = context.requestId || randomUUID();
const timestamp = new Date();
// Log the error with context
logger.error('Error occurred in tool execution', {
error: SanitizationMiddleware.sanitizeForLogging(error),
context: SanitizationMiddleware.sanitizeForLogging(context),
requestId,
timestamp
});
// Classify and format the error
const standardError = this.classifyError(error, requestId, timestamp);
// Additional context-specific processing
if (context.toolName) {
standardError.details = {
...standardError.details,
toolName: context.toolName
};
}
return standardError;
}
catch (handlingError) {
logger.error('Error in error handler:', handlingError);
return this.createFallbackError(context.requestId);
}
}
// Error classification based on error type and message
static classifyError(error, requestId, timestamp) {
// MongoDB/Database errors
if (this.isDatabaseError(error)) {
return {
code: ErrorCodes.DATABASE_ERROR,
message: 'Database operation failed',
details: { type: 'database' },
timestamp,
requestId
};
}
// Typesense errors
if (this.isTypesenseError(error)) {
return {
code: ErrorCodes.TYPESENSE_ERROR,
message: 'Search operation failed',
details: { type: 'search' },
timestamp,
requestId
};
}
// Validation errors
if (this.isValidationError(error)) {
return {
code: ErrorCodes.VALIDATION_ERROR,
message: 'Input validation failed',
details: { type: 'validation' },
timestamp,
requestId
};
}
// External API errors
if (this.isExternalApiError(error)) {
return {
code: ErrorCodes.EXTERNAL_API_ERROR,
message: 'External service unavailable',
details: { type: 'external_api' },
timestamp,
requestId
};
}
// Timeout errors
if (this.isTimeoutError(error)) {
return {
code: ErrorCodes.TIMEOUT_ERROR,
message: 'Operation timed out',
details: { type: 'timeout' },
timestamp,
requestId
};
}
// Maritime-specific validation errors
if (this.isMaritimeValidationError(error)) {
return {
code: ErrorCodes.MARITIME_VALIDATION_ERROR,
message: 'Maritime data validation failed',
details: { type: 'maritime' },
timestamp,
requestId
};
}
// Authentication errors
if (this.isAuthenticationError(error)) {
return {
code: ErrorCodes.AUTHENTICATION_ERROR,
message: 'Authentication required',
details: { type: 'auth' },
timestamp,
requestId
};
}
// Authorization errors
if (this.isAuthorizationError(error)) {
return {
code: ErrorCodes.AUTHORIZATION_ERROR,
message: 'Access denied',
details: { type: 'authorization' },
timestamp,
requestId
};
}
// Resource not found
if (this.isResourceNotFoundError(error)) {
return {
code: ErrorCodes.RESOURCE_NOT_FOUND,
message: 'Resource not found',
details: { type: 'not_found' },
timestamp,
requestId
};
}
// Rate limit errors
if (this.isRateLimitError(error)) {
return {
code: ErrorCodes.RATE_LIMIT_EXCEEDED,
message: 'Rate limit exceeded',
details: { type: 'rate_limit' },
timestamp,
requestId
};
}
// Default to internal error
return {
code: ErrorCodes.INTERNAL_ERROR,
message: 'Internal server error',
details: { type: 'internal' },
timestamp,
requestId
};
}
// Error type detection methods
static isDatabaseError(error) {
return (error?.name === 'MongoError' ||
error?.name === 'MongoServerError' ||
error?.name === 'MongoNetworkError' ||
error?.name === 'MongoTimeoutError' ||
error?.code === 11000 || // Duplicate key error
error?.code === 11001 ||
error?.message?.includes('mongo') ||
error?.message?.includes('database') ||
error?.message?.includes('collection'));
}
static isTypesenseError(error) {
return (error?.name === 'TypesenseError' ||
error?.message?.includes('typesense') ||
error?.message?.includes('search') ||
error?.response?.status === 400 ||
error?.response?.status === 404);
}
static isValidationError(error) {
return (error?.name === 'ValidationError' ||
error?.message?.includes('validation') ||
error?.message?.includes('invalid') ||
error?.code === 'VALIDATION_ERROR');
}
static isExternalApiError(error) {
return (error?.response?.status >= 400 ||
error?.code === 'ECONNREFUSED' ||
error?.code === 'ENOTFOUND' ||
error?.code === 'ETIMEDOUT' ||
error?.message?.includes('network') ||
error?.message?.includes('request failed'));
}
static isTimeoutError(error) {
return (error?.code === 'ETIMEDOUT' ||
error?.name === 'TimeoutError' ||
error?.message?.includes('timeout') ||
error?.message?.includes('timed out'));
}
static isMaritimeValidationError(error) {
return (error?.message?.includes('IMO') ||
error?.message?.includes('vessel') ||
error?.message?.includes('ship') ||
error?.message?.includes('maritime') ||
error?.message?.includes('OCIMF') ||
error?.message?.includes('SIRE') ||
error?.message?.includes('CDI') ||
error?.message?.includes('PSC'));
}
static isAuthenticationError(error) {
return (error?.status === 401 ||
error?.code === 'AUTHENTICATION_ERROR' ||
error?.message?.includes('authentication') ||
error?.message?.includes('unauthorized'));
}
static isAuthorizationError(error) {
return (error?.status === 403 ||
error?.code === 'AUTHORIZATION_ERROR' ||
error?.message?.includes('authorization') ||
error?.message?.includes('forbidden') ||
error?.message?.includes('access denied'));
}
static isResourceNotFoundError(error) {
return (error?.status === 404 ||
error?.code === 'RESOURCE_NOT_FOUND' ||
error?.message?.includes('not found') ||
error?.message?.includes('does not exist'));
}
static isRateLimitError(error) {
return (error?.status === 429 ||
error?.code === 'RATE_LIMIT_EXCEEDED' ||
error?.message?.includes('rate limit') ||
error?.message?.includes('too many requests'));
}
// Fallback error for when error handling itself fails
static createFallbackError(requestId = randomUUID()) {
return {
code: ErrorCodes.INTERNAL_ERROR,
message: 'An unexpected error occurred',
timestamp: new Date(),
requestId
};
}
// Safe execution with error handling
static async safeExecute(fn, fallbackValue, context) {
try {
return await fn();
}
catch (error) {
logger.warn('Safe execution failed, using fallback', {
error: SanitizationMiddleware.sanitizeForLogging(error),
context: SanitizationMiddleware.sanitizeForLogging(context)
});
return fallbackValue;
}
}
// Execute with retry logic
static async executeWithRetry(fn, context, maxRetries = this.MAX_RETRIES, delay = this.RETRY_DELAY_MS) {
let lastError;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await fn();
}
catch (error) {
lastError = error;
if (attempt === maxRetries) {
logger.error(`All retry attempts failed for ${context.toolName}`, {
error: SanitizationMiddleware.sanitizeForLogging(error),
attempts: attempt + 1,
context: SanitizationMiddleware.sanitizeForLogging(context)
});
break;
}
// Don't retry certain error types
if (this.isValidationError(error) || this.isAuthenticationError(error)) {
logger.warn(`Non-retryable error in ${context.toolName}`, {
error: SanitizationMiddleware.sanitizeForLogging(error),
context: SanitizationMiddleware.sanitizeForLogging(context)
});
break;
}
logger.warn(`Retrying ${context.toolName} (attempt ${attempt + 1}/${maxRetries + 1})`, {
error: SanitizationMiddleware.sanitizeForLogging(error),
context: SanitizationMiddleware.sanitizeForLogging(context)
});
// Exponential backoff
await new Promise(resolve => setTimeout(resolve, delay * Math.pow(2, attempt)));
}
}
throw lastError;
}
// Execute with timeout
static async executeWithTimeout(fn, timeout = this.TIMEOUT_MS, context) {
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => {
reject(new Error(`Operation timed out after ${timeout}ms`));
}, timeout);
});
try {
return await Promise.race([fn(), timeoutPromise]);
}
catch (error) {
logger.error(`Operation timed out for ${context.toolName}`, {
error: SanitizationMiddleware.sanitizeForLogging(error),
timeout,
context: SanitizationMiddleware.sanitizeForLogging(context)
});
throw error;
}
}
// Generate unique request ID
static generateRequestId() {
return randomUUID();
}
// Create error context
static createErrorContext(toolName, arguments_, userId, requestId) {
return {
toolName,
arguments: arguments_,
userId,
timestamp: new Date(),
requestId: requestId || this.generateRequestId()
};
}
// Format error for tool response
static formatErrorForToolResponse(error) {
return {
error: true,
code: error.code,
message: SanitizationMiddleware.sanitizeErrorMessage(error.message),
timestamp: error.timestamp,
requestId: error.requestId
};
}
// Format error for logging
static formatErrorForLogging(error, context) {
return {
error: SanitizationMiddleware.sanitizeForLogging(error),
context: SanitizationMiddleware.sanitizeForLogging(context),
sanitizedMessage: SanitizationMiddleware.sanitizeErrorMessage(error)
};
}
}
ErrorHandler.MAX_RETRIES = 3;
ErrorHandler.RETRY_DELAY_MS = 1000;
ErrorHandler.TIMEOUT_MS = 30000;
//# sourceMappingURL=error-handler.js.map