UNPKG

cakemail-mcp-server

Version:

Enterprise MCP server for Cakemail API integration with Claude AI - includes comprehensive template management, list management, sub-account management, BEEeditor visual email design, and advanced analytics

253 lines 10.3 kB
// Custom error classes for Cakemail API export class CakemailError extends Error { statusCode; response; constructor(message, statusCode, response) { super(message); this.name = 'CakemailError'; this.statusCode = statusCode; this.response = response; // Maintains proper stack trace for where our error was thrown (only available on V8) if (Error.captureStackTrace) { Error.captureStackTrace(this, CakemailError); } } } export class CakemailAuthenticationError extends CakemailError { constructor(message, response) { super(message, 401, response); this.name = 'CakemailAuthenticationError'; } } export class CakemailBadRequestError extends CakemailError { detail; constructor(errorResponse) { let detail; if (typeof errorResponse === 'string') { detail = errorResponse; } else if (errorResponse && typeof errorResponse === 'object') { if (errorResponse.detail && typeof errorResponse.detail === 'string') { detail = errorResponse.detail; } else if (Array.isArray(errorResponse.detail)) { // Handle validation errors format detail = errorResponse.detail.map((err) => { const field = Array.isArray(err.loc) ? err.loc.join('.') : 'unknown'; return `${field}: ${err.msg || 'validation error'}`; }).join(', '); } else { // Fallback: try to stringify the object try { detail = JSON.stringify(errorResponse, null, 2); } catch { detail = String(errorResponse); } } } else { detail = String(errorResponse); } super(`Bad Request: ${detail}`, 400, errorResponse); this.name = 'CakemailBadRequestError'; this.detail = detail; } } export class CakemailValidationError extends CakemailError { validationErrors; constructor(errorResponse) { const message = `Validation Error: ${CakemailValidationError.formatValidationErrors(errorResponse.detail)}`; super(message, 422, errorResponse); this.name = 'CakemailValidationError'; this.validationErrors = errorResponse.detail; } static formatValidationErrors(errors) { return errors.map(error => { const location = error.loc.join('.'); return `${location}: ${error.msg}`; }).join('; '); } // Helper to get errors for a specific field getFieldErrors(fieldName) { return this.validationErrors.filter(error => error.loc.includes(fieldName)); } } export class CakemailForbiddenError extends CakemailError { constructor(message = 'Forbidden: Insufficient permissions', response) { super(message, 403, response); this.name = 'CakemailForbiddenError'; } } export class CakemailNotFoundError extends CakemailError { constructor(message = 'Resource not found', response) { super(message, 404, response); this.name = 'CakemailNotFoundError'; } } export class CakemailConflictError extends CakemailError { constructor(message = 'Conflict: Resource already exists or operation conflicts with current state', response) { super(message, 409, response); this.name = 'CakemailConflictError'; } } export class CakemailRateLimitError extends CakemailError { retryAfter; constructor(message = 'Rate limit exceeded', retryAfter, response) { super(message, 429, response); this.name = 'CakemailRateLimitError'; this.retryAfter = retryAfter; } } export class CakemailServerError extends CakemailError { constructor(message = 'Internal server error', statusCode = 500, response) { super(message, statusCode, response); this.name = 'CakemailServerError'; } } export class CakemailNetworkError extends CakemailError { constructor(message = 'Network error occurred', originalError) { super(message, 0, originalError); this.name = 'CakemailNetworkError'; } } /** * Email API specific error class * Extends the base CakemailError with email-specific context */ export class EmailAPIError extends CakemailError { emailId; errorCode; errorDetails; constructor(message, statusCode = 500, emailId, errorCode, errorDetails) { super(message, statusCode); this.name = 'EmailAPIError'; this.emailId = emailId; this.errorCode = errorCode; this.errorDetails = errorDetails; } /** * Create an EmailAPIError from HTTP validation errors */ static fromValidationError(validationError, statusCode = 422) { const details = []; let mainMessage = 'Validation failed'; if (validationError.detail && Array.isArray(validationError.detail)) { validationError.detail.forEach((err) => { const field = Array.isArray(err.loc) ? err.loc.join('.') : 'unknown'; details.push({ code: err.type || 'validation_error', message: err.msg || 'Validation failed', field, value: err.input, suggestion: generateFieldSuggestion(field, err.type) }); }); mainMessage = `Validation failed for fields: ${details.map(d => d.field).join(', ')}`; } return new EmailAPIError(mainMessage, statusCode, undefined, 'VALIDATION_ERROR', details); } /** * Create an EmailAPIError for invalid email format */ static forInvalidEmail(email) { return new EmailAPIError(`Invalid email format: ${email}`, 400, undefined, 'INVALID_EMAIL', [{ code: 'INVALID_EMAIL', message: 'Email address format is invalid', field: 'to_email', value: email, suggestion: 'Ensure email follows RFC 5322 format (e.g., user@domain.com)' }]); } /** * Create an EmailAPIError for missing content */ static forMissingContent() { return new EmailAPIError('Email content is required', 400, undefined, 'MISSING_CONTENT', [{ code: 'MISSING_CONTENT', message: 'Either html_content, text_content, or template_id must be provided', field: 'content', suggestion: 'Provide html_content, text_content, or specify a template_id' }]); } /** * Get a user-friendly error message */ getUserFriendlyMessage() { if (this.errorDetails && this.errorDetails.length > 0) { const primaryError = this.errorDetails[0]; return `${primaryError.message}${primaryError.suggestion ? `. ${primaryError.suggestion}` : ''}`; } return this.message; } /** * Get all error details formatted for display */ getFormattedDetails() { if (!this.errorDetails || this.errorDetails.length === 0) { return this.message; } return this.errorDetails.map((detail, index) => { const parts = [`${index + 1}. ${detail.message}`]; if (detail.field) parts.push(`Field: ${detail.field}`); if (detail.value !== undefined) parts.push(`Value: ${detail.value}`); if (detail.suggestion) parts.push(`Suggestion: ${detail.suggestion}`); return parts.join('\n '); }).join('\n\n'); } } // Helper functions function generateFieldSuggestion(field, errorType) { const suggestions = { 'to': 'Provide a valid email address in format: user@domain.com', 'to_email': 'Provide a valid email address in format: user@domain.com', 'sender_id': 'Use a verified sender ID from your account', 'subject': 'Subject cannot be empty and should be descriptive', 'html_content': 'Provide valid HTML content for the email body', 'text_content': 'Provide plain text content for the email body', 'template_id': 'Use a valid template ID from your account', 'tags': 'Tags should be an array of strings', 'metadata': 'Metadata should be a valid JSON object' }; const typeSuggestions = { 'string_type': 'Value must be a string', 'missing': 'This field is required', 'value_error': 'Value format is invalid', 'type_error': 'Incorrect data type provided' }; return suggestions[field] || typeSuggestions[errorType || ''] || 'Check the field value and format'; } // Helper function to create appropriate error from response export function createCakemailError(response, errorBody) { const status = response.status; const statusText = response.statusText; switch (status) { case 400: return new CakemailBadRequestError(errorBody); case 401: return new CakemailAuthenticationError(`Authentication failed: ${errorBody?.detail || errorBody?.error_description || statusText}`, errorBody); case 403: return new CakemailForbiddenError(`Forbidden: ${errorBody?.detail || statusText}`, errorBody); case 404: return new CakemailNotFoundError(`Not found: ${errorBody?.detail || statusText}`, errorBody); case 409: return new CakemailConflictError(`Conflict: ${errorBody?.detail || statusText}`, errorBody); case 422: return new CakemailValidationError(errorBody); case 429: const retryAfter = response.headers.get('Retry-After'); return new CakemailRateLimitError(`Rate limit exceeded: ${errorBody?.detail || statusText}`, retryAfter ? parseInt(retryAfter) : undefined, errorBody); case 500: case 502: case 503: case 504: return new CakemailServerError(`Server error (${status}): ${errorBody?.detail || statusText}`, status, errorBody); default: return new CakemailError(`HTTP ${status}: ${errorBody?.detail || statusText}`, status, errorBody); } } //# sourceMappingURL=errors.js.map