UNPKG

evolution-api-mcp

Version:

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

322 lines (321 loc) 11.2 kB
"use strict"; /** * MCP Tool Factory * Creates MCP tools from Evolution API endpoint definitions */ Object.defineProperty(exports, "__esModule", { value: true }); exports.mcpToolFactory = exports.McpToolFactory = void 0; const zod_1 = require("zod"); const endpoint_registry_1 = require("../registry/endpoint-registry"); /** * Implementation of the MCP tool factory */ class McpToolFactory { constructor(config = {}) { this.config = { errorHandler: this.defaultErrorHandler, validator: this.defaultValidator, ...config }; } /** * Create tools for all endpoints in a controller */ createToolsForController(controller, config) { const endpoints = endpoint_registry_1.evolutionEndpointRegistry.getEndpointsByController(controller); const toolConfig = { ...this.config, ...config }; return endpoints.map(endpoint => this.createToolForEndpoint(endpoint, toolConfig)); } /** * Create a single tool from an endpoint definition */ createToolForEndpoint(endpoint, config) { const toolConfig = { ...this.config, ...config }; return { name: this.generateToolName(endpoint), description: this.generateToolDescription(endpoint), controller: endpoint.controller, endpoint, schema: this.generateSchema(endpoint), handler: this.createHandler(endpoint, toolConfig), examples: this.generateExamples(endpoint) }; } /** * Generate Zod schema for tool parameters based on endpoint definition */ generateSchema(endpoint) { const schemaFields = {}; endpoint.parameters.forEach(param => { let fieldSchema = this.createZodFieldFromParameter(param); // Add description fieldSchema = fieldSchema.describe(param.description); schemaFields[param.name] = fieldSchema; }); return zod_1.z.object(schemaFields); } /** * Create a tool handler for an endpoint */ createHandler(endpoint, config) { const toolConfig = { ...this.config, ...config }; return async (params) => { try { // Validate parameters const validation = toolConfig.validator(params, this.generateSchema(endpoint)); if (!validation.valid) { return { success: false, error: { type: 'VALIDATION_ERROR', message: 'Invalid parameters', details: validation.errors } }; } // Transform parameters for the API request const transformedParams = this.transformParameters(params, endpoint); // Make the API request (placeholder - will be implemented when HTTP client is available) if (!toolConfig.httpClient) { return { success: false, error: { type: 'CONFIGURATION_ERROR', message: 'HTTP client not configured' } }; } // This will be implemented when the HTTP client is available const result = await this.makeApiRequest(endpoint, transformedParams, toolConfig.httpClient); return { success: true, data: result }; } catch (error) { return toolConfig.errorHandler(error); } }; } /** * Generate a tool name from endpoint information */ generateToolName(endpoint) { // Convert endpoint name to MCP tool name format // Example: "send-text-message" -> "evolution_send_text_message" const baseName = endpoint.name.replace(/[^a-zA-Z0-9]/g, '_').toLowerCase(); return `evolution_${baseName}`; } /** * Generate a tool description from endpoint information */ generateToolDescription(endpoint) { const controllerName = endpoint.controller.charAt(0).toUpperCase() + endpoint.controller.slice(1); return `${controllerName} Controller: ${endpoint.description}`; } /** * Generate examples for a tool */ generateExamples(endpoint) { const exampleParams = {}; endpoint.parameters.forEach(param => { if (param.example !== undefined) { exampleParams[param.name] = param.example; } else { // Generate example based on parameter type and name exampleParams[param.name] = this.generateExampleValue(param); } }); return { usage: `Use this tool to ${endpoint.description.toLowerCase()}`, parameters: exampleParams }; } /** * Generate example value for a parameter */ generateExampleValue(param) { // Generate examples based on parameter name and type if (param.name.includes('instance')) { return 'my_instance'; } if (param.name.includes('number') || param.name.includes('phone')) { return '5511999999999'; } if (param.name.includes('text') || param.name.includes('message')) { return 'Hello, this is a test message'; } if (param.name.includes('url')) { return 'https://example.com'; } if (param.name.includes('email')) { return 'user@example.com'; } if (param.name.includes('name')) { return 'Example Name'; } if (param.name.includes('id') || param.name.includes('jid')) { return 'example_id'; } if (param.name.includes('delay')) { return 1000; } if (param.name.includes('enabled') || param.name.includes('active')) { return true; } // Default examples based on type switch (param.type.toLowerCase()) { case 'string': return 'example_value'; case 'number': case 'integer': return 123; case 'boolean': return true; case 'array': return []; case 'object': return {}; default: return 'example_value'; } } /** * Create Zod field schema from parameter definition */ createZodFieldFromParameter(param) { let schema; // Create base schema based on type switch (param.type.toLowerCase()) { case 'string': schema = zod_1.z.string(); break; case 'number': case 'integer': schema = zod_1.z.number(); break; case 'boolean': schema = zod_1.z.boolean(); break; case 'array': schema = zod_1.z.array(zod_1.z.any()); break; case 'object': schema = zod_1.z.record(zod_1.z.any()); break; default: schema = zod_1.z.any(); } // Make optional if not required if (!param.required) { schema = schema.optional(); } return schema; } /** * Transform parameters for API request */ transformParameters(params, endpoint) { const pathParams = {}; const queryParams = {}; const bodyParams = {}; const headerParams = {}; endpoint.parameters.forEach(param => { const value = params[param.name]; if (value !== undefined) { switch (param.location) { case 'path': pathParams[param.name] = value; break; case 'query': queryParams[param.name] = value; break; case 'body': bodyParams[param.name] = value; break; case 'header': headerParams[param.name] = value; break; } } }); return { pathParams, queryParams, bodyParams, headerParams }; } /** * Make API request using the HTTP client */ async makeApiRequest(endpoint, params, httpClient) { const { pathParams, queryParams, bodyParams } = params; // Replace path parameters in the endpoint path let requestPath = endpoint.path; Object.entries(pathParams).forEach(([key, value]) => { requestPath = requestPath.replace(`{${key}}`, String(value)); }); // Make the HTTP request based on the endpoint method const method = endpoint.method.toUpperCase(); let response; switch (method) { case 'GET': response = await httpClient.get(requestPath, queryParams); break; case 'POST': response = await httpClient.post(requestPath, bodyParams, queryParams); break; case 'PUT': response = await httpClient.put(requestPath, bodyParams, queryParams); break; case 'DELETE': response = await httpClient.delete(requestPath, queryParams); break; case 'PATCH': response = await httpClient.patch(requestPath, bodyParams, queryParams); break; default: throw new Error(`Unsupported HTTP method: ${method}`); } if (!response.success) { throw response.error; } return response.data; } /** * Default error handler */ defaultErrorHandler(error) { const errorMessage = error instanceof Error ? error.message : 'An unknown error occurred'; return { success: false, error: { type: 'UNKNOWN_ERROR', message: errorMessage, details: error } }; } /** * Default parameter validator */ defaultValidator(params, schema) { try { schema.parse(params); return { valid: true }; } catch (error) { if (error instanceof zod_1.z.ZodError) { return { valid: false, errors: error.errors.map(err => `${err.path.join('.')}: ${err.message}`) }; } const errorMessage = error instanceof Error ? error.message : 'Validation failed'; return { valid: false, errors: [errorMessage] }; } } } exports.McpToolFactory = McpToolFactory; // Export a singleton instance exports.mcpToolFactory = new McpToolFactory();