@rhofkens/mcp-quotes-server-claude-code
Version:
Model Context Protocol (MCP) server for managing and serving quotes
308 lines • 10.6 kB
JavaScript
/**
* 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