UNPKG

evolution-api-mcp

Version:

MCP Server for Evolution API v2 - Integrate WhatsApp functionality with Claude Desktop and other MCP clients

242 lines (241 loc) 9.11 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.EvolutionHttpClient = exports.HttpClientConfigSchema = exports.ApiError = exports.ErrorType = void 0; const axios_1 = __importDefault(require("axios")); const zod_1 = require("zod"); const error_handler_1 = require("../utils/error-handler"); // Re-export error types for backward compatibility var error_handler_2 = require("../utils/error-handler"); Object.defineProperty(exports, "ErrorType", { enumerable: true, get: function () { return error_handler_2.ErrorType; } }); Object.defineProperty(exports, "ApiError", { enumerable: true, get: function () { return error_handler_2.McpError; } }); // Configuration schema exports.HttpClientConfigSchema = zod_1.z.object({ baseURL: zod_1.z.string().url('Base URL must be a valid URL'), apiKey: zod_1.z.string().min(1, 'API key is required'), timeout: zod_1.z.number().positive().optional().default(30000), retryAttempts: zod_1.z.number().min(0).max(10).optional().default(3), retryDelay: zod_1.z.number().positive().optional().default(1000), maxRetryDelay: zod_1.z.number().positive().optional().default(30000), enableLogging: zod_1.z.boolean().optional().default(false) }); class EvolutionHttpClient { constructor(config) { this.requestCount = 0; // Validate configuration this.config = exports.HttpClientConfigSchema.parse(config); // Initialize error handler this.errorHandler = new error_handler_1.ErrorHandler({ enableLogging: this.config.enableLogging, logLevel: this.config.enableLogging ? 'error' : 'error' }); // Create axios instance with base configuration this.axiosInstance = axios_1.default.create({ baseURL: this.config.baseURL, timeout: this.config.timeout, headers: { 'Content-Type': 'application/json', 'apikey': this.config.apiKey, 'User-Agent': 'evolution-api-mcp/1.0.0' }, // Enable connection pooling maxRedirects: 5, validateStatus: (status) => status < 500 // Don't throw on 4xx errors }); this.setupInterceptors(); } setupInterceptors() { // Request interceptor for logging and authentication this.axiosInstance.interceptors.request.use((config) => { if (this.config.enableLogging) { console.log(`[HTTP Client] Request: ${config.method?.toUpperCase()} ${config.url}`); if (config.data) { console.log('[HTTP Client] Request data:', JSON.stringify(config.data, null, 2)); } } // Ensure API key is always present if (!config.headers['apikey']) { config.headers['apikey'] = this.config.apiKey; } return config; }, (error) => { if (this.config.enableLogging) { console.error('[HTTP Client] Request error:', error.message); } return Promise.reject(this.createApiError(error, { operation: 'request_interceptor' })); }); // Response interceptor for logging and error handling this.axiosInstance.interceptors.response.use((response) => { if (this.config.enableLogging) { console.log(`[HTTP Client] Response: ${response.status} ${response.statusText}`); console.log('[HTTP Client] Response data:', JSON.stringify(response.data, null, 2)); } return response; }, (error) => { if (this.config.enableLogging) { console.error('[HTTP Client] Response error:', error.message); if (error.response) { console.error('[HTTP Client] Error response:', { status: error.response.status, data: error.response.data }); } } return Promise.reject(this.createApiError(error, { operation: 'response_interceptor' })); }); } createApiError(error, context) { return this.errorHandler.handleHttpError(error, context); } async executeWithRetry(requestFn, retries = this.config.retryAttempts, context) { let lastError; let currentDelay = this.config.retryDelay; for (let attempt = 0; attempt <= retries; attempt++) { try { this.requestCount++; const response = await requestFn(); return { success: true, data: response.data, statusCode: response.status, headers: response.headers }; } catch (error) { // Convert axios error to our McpError format with context const errorContext = { ...context, operation: 'http_request', requestId: `req_${this.requestCount}_${Date.now()}` }; lastError = this.createApiError(error, errorContext); // Don't retry on non-retryable errors if (!lastError.retryable) { break; } // Don't retry on the last attempt if (attempt === retries) { break; } if (this.config.enableLogging) { console.log(`[HTTP Client] Attempt ${attempt + 1} failed, retrying in ${currentDelay}ms...`); } // Wait before retrying with exponential backoff await this.delay(currentDelay); currentDelay = Math.min(currentDelay * 2, this.config.maxRetryDelay); } } // Log the final error this.errorHandler.logError(lastError); return { success: false, error: lastError, statusCode: lastError.statusCode || 0 }; } delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } // Generic request method async request(options) { const { method, path, data, params, headers, timeout, retries } = options; const requestConfig = { method, url: path, data, params, headers: { ...headers }, timeout: timeout || this.config.timeout }; const context = { operation: 'http_request', endpoint: path, parameters: { method, path, params } }; return this.executeWithRetry(() => this.axiosInstance.request(requestConfig), retries !== undefined ? retries : this.config.retryAttempts, context); } // Convenience methods async get(path, params, options) { return this.request({ method: 'GET', path, params, ...options }); } async post(path, data, params, options) { return this.request({ method: 'POST', path, data, params, ...options }); } async put(path, data, params, options) { return this.request({ method: 'PUT', path, data, params, ...options }); } async delete(path, params, options) { return this.request({ method: 'DELETE', path, params, ...options }); } async patch(path, data, params, options) { return this.request({ method: 'PATCH', path, data, params, ...options }); } // Configuration methods updateConfig(updates) { this.config = { ...this.config, ...updates }; // Update axios instance if needed if (updates.baseURL) { this.axiosInstance.defaults.baseURL = updates.baseURL; } if (updates.apiKey) { this.axiosInstance.defaults.headers['apikey'] = updates.apiKey; } if (updates.timeout) { this.axiosInstance.defaults.timeout = updates.timeout; } } getConfig() { return { ...this.config }; } // Health check method async healthCheck() { try { return await this.get('/'); } catch (error) { const mcpError = this.createApiError(error, { operation: 'health_check' }); return { success: false, error: mcpError, statusCode: mcpError.statusCode || 0 }; } } // Get request statistics getStats() { return { requestCount: this.requestCount }; } } exports.EvolutionHttpClient = EvolutionHttpClient;