UNPKG

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
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