UNPKG

mcp-grocy

Version:

Model Context Protocol (MCP) server for Grocy integration

139 lines (138 loc) 4.45 kB
/** * Simplified error handling system */ import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js'; import { logger } from './logger.js'; /** * Simplified application error */ export class AppError extends Error { category; statusCode; operation; details; timestamp = new Date(); constructor(message, category, statusCode, operation, details) { super(message); this.name = 'AppError'; this.category = category; this.statusCode = statusCode; this.operation = operation; this.details = details; Object.setPrototypeOf(this, AppError.prototype); } toMcpError() { const errorCode = this.getErrorCode(); return new McpError(errorCode, this.message); } getErrorCode() { switch (this.category) { case 'VALIDATION': return ErrorCode.InvalidParams; case 'AUTH': return ErrorCode.InvalidRequest; case 'API': if (this.statusCode === 404) return ErrorCode.InvalidRequest; if (this.statusCode === 401 || this.statusCode === 403) return ErrorCode.InvalidRequest; return ErrorCode.InternalError; default: return ErrorCode.InternalError; } } } /** * API-specific error */ export class ApiError extends AppError { constructor(message, statusCode, operation, details) { super(message, 'API', statusCode, operation, details); this.name = 'ApiError'; } static fromAxiosError(error, operation) { if (error.response) { return new ApiError(error.response.data?.message || `HTTP ${error.response.status}`, error.response.status, operation, { url: error.config?.url, method: error.config?.method }); } if (error.request) { return new ApiError('Network error - unable to reach server', undefined, operation, { url: error.config?.url }); } return new ApiError(error.message || 'Request error', undefined, operation); } } /** * Validation error */ export class ValidationError extends AppError { constructor(message, operation, details) { super(message, 'VALIDATION', undefined, operation, details); this.name = 'ValidationError'; } } /** * Configuration error */ export class ConfigError extends AppError { constructor(message, operation, details) { super(message, 'CONFIG', undefined, operation, details); this.name = 'ConfigError'; } } /** * Centralized error handler */ export class ErrorHandler { /** * Handle async operations with error wrapping */ static async handleAsync(operation, context) { try { return await operation(); } catch (error) { this.logError(error, context); throw this.wrapError(error, context); } } /** * Log error with context */ static logError(error, context) { const errorMessage = error instanceof Error ? error.message : String(error); const fullContext = context ? `${context}: ${errorMessage}` : errorMessage; if (error instanceof AppError) { logger.warn(fullContext, 'ERROR', { category: error.category, statusCode: error.statusCode, operation: error.operation, details: error.details }); } else { logger.error(fullContext, 'ERROR', { error }); } } /** * Wrap unknown errors in AppError */ static wrapError(error, context) { if (error instanceof AppError) { return error; } if (error instanceof McpError) { return new AppError(error.message, 'INTERNAL', undefined, context); } const message = error instanceof Error ? error.message : String(error); return new AppError(message, 'INTERNAL', undefined, context); } /** * Convert error to MCP error */ static toMcpError(error, fallbackMessage = 'Internal error') { if (error instanceof McpError) { return error; } if (error instanceof AppError) { return error.toMcpError(); } const message = error instanceof Error ? error.message : fallbackMessage; return new McpError(ErrorCode.InternalError, message); } }