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
JavaScript
"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();