UNPKG

mcp-grocy

Version:

Model Context Protocol (MCP) server for Grocy integration

142 lines (141 loc) 4.24 kB
/** * Simplified base tool handler */ import apiClient from '../api/client.js'; import { ErrorHandler, ValidationError } from '../utils/errors.js'; import { logger } from '../utils/logger.js'; export class BaseToolHandler { /** * Create a standardized success result */ createSuccess(data, message) { return { content: [ { type: 'text', text: message || 'Operation completed successfully' }, { type: 'text', text: JSON.stringify(data, null, 2) } ] }; } /** * Create a standardized error result */ createError(message, details) { return { content: [ { type: 'text', text: `Error: ${message}` }, ...(details ? [{ type: 'text', text: JSON.stringify(details, null, 2) }] : []) ], isError: true }; } /** * Validate required parameters */ validateRequired(params, required) { const missing = required.filter(field => { const value = params[field]; return value === undefined || value === null || value === ''; }); if (missing.length > 0) { throw new ValidationError(`Missing required parameters: ${missing.join(', ')}`, 'parameter validation'); } } /** * Safely make API calls with error handling */ async apiCall(endpoint, method = 'GET', data, options) { return ErrorHandler.handleAsync(async () => { const response = await apiClient.request(endpoint, { method, body: data, queryParams: options?.queryParams || {} }); return response.data; }, `API ${method} ${endpoint}`); } /** * Handle tool execution with standardized error handling */ async executeToolHandler(handler) { try { return await handler(); } catch (error) { ErrorHandler.logError(error, 'tool execution'); if (error instanceof ValidationError) { return this.createError(error.message); } const message = error instanceof Error ? error.message : 'Internal error'; return this.createError(`Tool execution failed: ${message}`); } } /** * Safe JSON formatting */ safeStringify(data) { try { return JSON.stringify(data, null, 2); } catch (error) { logger.warn('Failed to stringify data', 'TOOLS', { error }); return '[Unable to format data]'; } } /** * Filter object fields based on allowlist */ filterFields(objects, fields) { return objects.map(obj => { const filtered = {}; fields.forEach(field => { if (field in obj) { filtered[field] = obj[field]; } }); return filtered; }); } /** * Parse and validate array parameter */ parseArrayParam(value, paramName) { if (!value) { throw new ValidationError(`${paramName} is required`); } if (!Array.isArray(value)) { throw new ValidationError(`${paramName} must be an array`); } if (value.length === 0) { throw new ValidationError(`${paramName} cannot be empty`); } return value.map(v => String(v)); } /** * Parse and validate numeric parameter */ parseNumberParam(value, paramName, required = true) { if (value === undefined || value === null) { if (required) { throw new ValidationError(`${paramName} is required`); } return undefined; } const num = Number(value); if (isNaN(num)) { throw new ValidationError(`${paramName} must be a valid number`); } return num; } }