UNPKG

@rhofkens/mcp-quotes-server-claude-code

Version:

Model Context Protocol (MCP) server for managing and serving quotes

308 lines 10.6 kB
/** * Error Handling Utilities * * Custom error classes and error handling utilities for the MCP Quotes Server */ import { isDevelopment } from './config.js'; /** * Error codes for categorizing different error types */ export var ErrorCode; (function (ErrorCode) { // General errors ErrorCode["UNKNOWN_ERROR"] = "UNKNOWN_ERROR"; ErrorCode["INTERNAL_ERROR"] = "INTERNAL_ERROR"; // Validation errors ErrorCode["VALIDATION_ERROR"] = "VALIDATION_ERROR"; ErrorCode["INVALID_INPUT"] = "INVALID_INPUT"; ErrorCode["MISSING_PARAMETER"] = "MISSING_PARAMETER"; ErrorCode["TYPE_MISMATCH"] = "TYPE_MISMATCH"; // API errors ErrorCode["API_ERROR"] = "API_ERROR"; ErrorCode["API_TIMEOUT"] = "API_TIMEOUT"; ErrorCode["API_RATE_LIMIT"] = "API_RATE_LIMIT"; ErrorCode["API_UNAUTHORIZED"] = "API_UNAUTHORIZED"; ErrorCode["API_NOT_FOUND"] = "API_NOT_FOUND"; // Configuration errors ErrorCode["CONFIG_ERROR"] = "CONFIG_ERROR"; ErrorCode["MISSING_ENV_VAR"] = "MISSING_ENV_VAR"; ErrorCode["INVALID_CONFIG"] = "INVALID_CONFIG"; // MCP specific errors ErrorCode["MCP_ERROR"] = "MCP_ERROR"; ErrorCode["MCP_METHOD_NOT_FOUND"] = "MCP_METHOD_NOT_FOUND"; ErrorCode["MCP_INVALID_PARAMS"] = "MCP_INVALID_PARAMS"; ErrorCode["MCP_INTERNAL_ERROR"] = "MCP_INTERNAL_ERROR"; // Resource errors ErrorCode["RESOURCE_NOT_FOUND"] = "RESOURCE_NOT_FOUND"; ErrorCode["RESOURCE_UNAVAILABLE"] = "RESOURCE_UNAVAILABLE"; })(ErrorCode || (ErrorCode = {})); /** * Base error class for all custom errors */ export class BaseError extends Error { code; statusCode; details; constructor(message, code, statusCode = 500, details) { super(message); this.code = code; this.statusCode = statusCode; this.details = details; this.name = this.constructor.name; // Capture stack trace Error.captureStackTrace(this, this.constructor); } /** * Convert error to JSON representation */ toJSON() { const json = { name: this.name, message: this.message, code: this.code, statusCode: this.statusCode, details: this.details, }; if (isDevelopment() && this.stack) { json['stack'] = this.stack; } return json; } /** * Get user-friendly error message */ getUserMessage() { // Override in subclasses for custom user messages return this.message; } } /** * MCP protocol specific errors */ export class MCPError extends BaseError { constructor(message, code = ErrorCode.MCP_ERROR, details) { super(message, code, 500, details); } getUserMessage() { switch (this.code) { case ErrorCode.MCP_METHOD_NOT_FOUND: return 'The requested method is not available'; case ErrorCode.MCP_INVALID_PARAMS: return 'Invalid parameters provided'; case ErrorCode.MCP_INTERNAL_ERROR: return 'An internal server error occurred'; default: return 'An error occurred while processing your request'; } } } /** * Validation errors for input validation failures */ export class ValidationError extends BaseError { field; constructor(message, field, details) { super(message, ErrorCode.VALIDATION_ERROR, 400, { ...details, field }); this.field = field; } getUserMessage() { if (this.field) { if (this.field === 'email') { return `Invalid value for field '${this.field}': ${this.message}. The email field is not a standard field for the quote tool. Please check the available fields.`; } return `Invalid value for field '${this.field}': ${this.message}. Please refer to the prompt template resource for the correct format.`; } return `Validation error: ${this.message}. Please check that all required parameters are provided correctly.`; } } /** * API errors for external service failures */ export class APIError extends BaseError { service; constructor(message, code = ErrorCode.API_ERROR, service, details) { const statusCode = APIError.getStatusCodeForErrorCode(code); super(message, code, statusCode, { ...details, service }); this.service = service; } static getStatusCodeForErrorCode(code) { switch (code) { case ErrorCode.API_UNAUTHORIZED: return 401; case ErrorCode.API_NOT_FOUND: return 404; case ErrorCode.API_RATE_LIMIT: return 429; case ErrorCode.API_TIMEOUT: return 504; default: return 502; // Bad Gateway for generic API errors } } getUserMessage() { switch (this.code) { case ErrorCode.API_TIMEOUT: return 'The request timed out. Please try again.'; case ErrorCode.API_RATE_LIMIT: return 'Rate limit exceeded. Please wait a few minutes before making more requests. Consider reducing the number of quotes requested.'; case ErrorCode.API_UNAUTHORIZED: return 'Authentication failed. Please check your API key.'; case ErrorCode.API_NOT_FOUND: return 'The requested resource was not found.'; default: return 'An error occurred while communicating with external services.'; } } } /** * Configuration errors */ export class ConfigError extends BaseError { variable; constructor(message, variable, details) { super(message, ErrorCode.CONFIG_ERROR, 500, { ...details, variable }); this.variable = variable; } getUserMessage() { if (this.variable) { return `Configuration error: Missing or invalid ${this.variable}`; } return 'Configuration error: Please check your environment settings'; } } /** * Network errors for connectivity issues */ export class NetworkError extends APIError { constructor(message, service, details) { super(message, ErrorCode.API_TIMEOUT, service, details); } getUserMessage() { const serviceInfo = this.service ? ` to ${this.service}` : ''; return `Network error: Unable to connect${serviceInfo}. Please check your internet connection and ensure the service is accessible. If using a proxy or firewall, ensure it allows HTTPS connections to external APIs.`; } } /** * Authentication errors for API key issues */ export class AuthenticationError extends APIError { constructor(message, service, details) { super(message, ErrorCode.API_UNAUTHORIZED, service, details); } getUserMessage() { const serviceInfo = this.service ? ` for ${this.service}` : ''; return `Authentication failed${serviceInfo}. Please verify:\n1. Your SERPER_API_KEY is correctly set\n2. The API key is valid and active\n3. You have not exceeded your plan limits\nGet a new API key at https://serper.dev if needed.`; } } /** * Rate limit errors */ export class RateLimitError extends APIError { constructor(message, service, details) { super(message, ErrorCode.API_RATE_LIMIT, service, details); } getUserMessage() { const serviceInfo = this.service ? ` for ${this.service}` : ''; return `Rate limit exceeded${serviceInfo}. To avoid this:\n1. Wait a few minutes before retrying\n2. Reduce the number of quotes requested\n3. Space out your requests over time\n4. Consider upgrading your API plan if this happens frequently`; } } /** * Error formatter for consistent error messages */ export class ErrorFormatter { /** * Format error for logging */ static formatForLog(error) { if (error instanceof BaseError) { return JSON.stringify(error.toJSON(), null, 2); } return JSON.stringify({ name: error.name, message: error.message, stack: error.stack, }, null, 2); } /** * Format error for user display */ static formatForUser(error) { if (error instanceof BaseError) { return error.getUserMessage(); } // Generic message for unknown errors return 'An unexpected error occurred. Please try again.'; } /** * Extract error details for debugging */ static extractDetails(error) { if (error instanceof BaseError) { return error.details || {}; } if (error instanceof Error) { return { name: error.name, message: error.message, ...(isDevelopment() && { stack: error.stack }), }; } return { error: String(error), }; } } /** * Wrap an error with additional context */ export function wrapError(error, message, code) { if (error instanceof BaseError) { // Preserve original error but add context return new BaseError(`${message}: ${error.message}`, code || error.code, error.statusCode, { ...error.details, originalError: error.toJSON(), }); } if (error instanceof Error) { return new BaseError(`${message}: ${error.message}`, code || ErrorCode.INTERNAL_ERROR, 500, { originalError: error.message, ...(isDevelopment() && { stack: error.stack }), }); } return new BaseError(message, code || ErrorCode.UNKNOWN_ERROR, 500, { originalError: String(error), }); } /** * Type guard to check if error is a BaseError */ export function isBaseError(error) { return error instanceof BaseError; } /** * Type guard to check if error has a code property */ export function hasErrorCode(error) { return (typeof error === 'object' && error !== null && 'code' in error && typeof error.code === 'string'); } /** * Convert unknown error to BaseError */ export function toBaseError(error) { if (error instanceof BaseError) { return error; } if (error instanceof Error) { return new BaseError(error.message, ErrorCode.INTERNAL_ERROR, 500, { originalError: error.name, ...(isDevelopment() && { stack: error.stack }), }); } return new BaseError('An unknown error occurred', ErrorCode.UNKNOWN_ERROR, 500, { originalError: String(error), }); } //# sourceMappingURL=errors.js.map