/** * Centralized error handling for tools * Provides consistent error responses and logging */ /** * Handle and format tool errors * @param {Error|string} error - The error object or message * @param {string} context - Context where the error occurred (e.g., "save_module", "create_record") * @param {Object} additionalInfo - Additional information to include in response * @returns {Object} Formatted error response */ export function handleToolError(error, context = "unknown", additionalInfo = {}) { // Log error to console console.error(`[Tool Error - ${context}]`, error instanceof Error ? error.message : error); if (error instanceof Error && error.stack) { console.error(`Stack:`, error.stack); } // Extract error message let errorMessage = error instanceof Error ? error.message : String(error); let errorCode = "UNKNOWN_ERROR"; let statusCode = 500; // Handle specific error types if (error.response) { // Axios error with response statusCode = error.response.status || 500; errorMessage = error.response.data?.message || error.response.data?.error || errorMessage; errorCode = `HTTP_${statusCode}`; } else if (error.code) { // Error with code (like ENOTFOUND, ECONNREFUSED, etc.) errorCode = error.code; errorMessage = `${error.code}: ${errorMessage}`; } // Return formatted error response return { content: [{ type: "text", text: JSON.stringify({ success: false, error: { code: errorCode, message: errorMessage, context: context, ...additionalInfo } }, null, 2) }], isError: true }; } /** * Handle API response errors (when response contains error indication) * @param {Object} data - Response data from API * @param {string} context - Context where error occurred * @returns {Object|null} Error response or null if no error */ export function handleApiResponse(data, context = "unknown") { // Check for common error patterns in Acai CMS responses /*if (!data) { return handleToolError("Empty response from API", context, { details: "API returned null or undefined" }); }*/ // PHP/Acai error responses typically have error field or PHPSyntax errors if (data.error || data.Error) { return handleToolError(data.error || data.Error, context, { details: data }); } if (data.PHPSyntax) { return handleToolError(`PHP Syntax Error: ${data.PHPSyntax}`, context, { details: data }); } // If it's a string response with error indicators if (typeof data === 'string' && data.trim().length > 0) { // Check for common error patterns if (data.toLowerCase().includes('error') || data.toLowerCase().includes('fatal') || data.toLowerCase().includes('undefined') || data.toLowerCase().includes('syntax')) { return handleToolError(data, context, { details: "API returned error string" }); } } // If success field exists and is false if (data.success === false) { return handleToolError(data.message || "API returned success: false", context, { details: data }); } // No error detected return null; } /** * Validate required parameters * @param {Object} params - Parameters object * @param {string[]} requiredFields - Array of required field names * @param {string} context - Context where validation occurs * @returns {Object|null} Error response or null if all valid */ export function validateRequired(params, requiredFields, context = "unknown") { const missingFields = []; requiredFields.forEach(field => { const value = params[field]; if (value === null || value === undefined || (typeof value === 'string' && value.trim() === '')) { missingFields.push(field); } }); if (missingFields.length > 0) { return handleToolError( `Missing required parameters: ${missingFields.join(', ')}`, context, { requiredFields, missingFields } ); } return null; } /** * Validate parameter types * @param {Object} params - Parameters object * @param {Object} schema - Schema of expected types {fieldName: 'string'|'number'|'boolean'|'array'|'object'} * @param {string} context - Context where validation occurs * @returns {Object|null} Error response or null if all valid */ export function validateTypes(params, schema, context = "unknown") { const typeErrors = []; for (const [field, expectedType] of Object.entries(schema)) { const value = params[field]; if (value === null || value === undefined) { continue; // Skip optional fields that are not provided } let actualType = typeof value; if (Array.isArray(value)) actualType = 'array'; if (value instanceof Date) actualType = 'date'; if (actualType !== expectedType) { typeErrors.push(`${field}: expected ${expectedType}, got ${actualType}`); } } if (typeErrors.length > 0) { return handleToolError( `Type validation failed: ${typeErrors.join('; ')}`, context, { typeErrors } ); } return null; } /** * Safely parse JSON with error handling * @param {string} jsonString - JSON string to parse * @param {string} context - Context where parsing occurs * @returns {Object} Parsed object or error response object */ export function safeJsonParse(jsonString, context = "unknown") { try { return { success: true, data: JSON.parse(jsonString) }; } catch (error) { return { success: false, error: handleToolError(error, `${context} - JSON parsing`, { input: jsonString.substring(0, 100) }) }; } } /** * Create a validation middleware for tools * @param {string[]} requiredFields - Required parameter names * @param {Object} typeSchema - Type validation schema * @returns {Function} Middleware function */ export function createValidator(requiredFields = [], typeSchema = {}) { return function validateInput(params, context = "unknown") { // Check required fields const requiredError = validateRequired(params, requiredFields, context); if (requiredError) return requiredError; // Check types const typeError = validateTypes(params, typeSchema, context); if (typeError) return typeError; return null; // No errors }; } /** * Wrap a tool handler with automatic error handling * @param {Function} handler - The tool handler function * @param {string} toolName - Name of the tool for logging * @returns {Function} Wrapped handler */ export function withErrorHandling(handler, toolName = "unknown") { return async (params, extra) => { try { return await handler(params, extra); } catch (error) { return handleToolError(error, toolName); } }; }