Initial commit
This commit is contained in:
215
mcp-server/tools/helpers/errorHandler.js
Normal file
215
mcp-server/tools/helpers/errorHandler.js
Normal file
@@ -0,0 +1,215 @@
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user