evolution-api-mcp
Version:
MCP Server for Evolution API v2 - Integrate WhatsApp functionality with Claude Desktop and other MCP clients
472 lines (471 loc) • 19.5 kB
JavaScript
"use strict";
/**
* Instance Controller MCP Tools Implementation
* Implements all tools for managing WhatsApp instances in Evolution API
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.InstanceTools = void 0;
const zod_1 = require("zod");
const evolution_http_client_1 = require("../../clients/evolution-http-client");
const instance_endpoints_1 = require("../../registry/endpoints/instance-endpoints");
/**
* Instance Controller tool implementations
*/
class InstanceTools {
constructor(httpClient) {
this.httpClient = httpClient;
}
/**
* Create a new WhatsApp instance
*/
createCreateInstanceTool() {
const endpoint = instance_endpoints_1.instanceEndpoints.find(e => e.name === 'create-instance');
return {
name: 'evolution_create_instance',
description: 'Create a new WhatsApp instance with optional webhook configuration',
controller: 'instance',
endpoint,
schema: zod_1.z.object({
instanceName: zod_1.z.string()
.min(1, 'Instance name is required')
.regex(/^[a-zA-Z0-9_-]+$/, 'Instance name can only contain letters, numbers, underscores and hyphens')
.describe('Unique name for the WhatsApp instance'),
token: zod_1.z.string().optional()
.describe('Optional authentication token for the instance'),
qrcode: zod_1.z.boolean().optional().default(true)
.describe('Whether to generate QR code for connection (default: true)'),
webhook: zod_1.z.string().url().optional()
.describe('Webhook URL to receive events from this instance'),
webhookByEvents: zod_1.z.boolean().optional().default(false)
.describe('Whether to send webhook events separately'),
webhookBase64: zod_1.z.boolean().optional().default(false)
.describe('Whether to encode webhook data in base64'),
events: zod_1.z.array(zod_1.z.string()).optional()
.describe('List of specific events to send to webhook')
}),
handler: this.createInstanceHandler.bind(this),
examples: {
usage: 'Create a new WhatsApp instance with QR code generation',
parameters: {
instanceName: 'my_whatsapp_bot',
qrcode: true,
webhook: 'https://myapp.com/webhook'
}
}
};
}
async createInstanceHandler(params) {
try {
const response = await this.httpClient.post('/instance/create', params);
if (!response.success) {
return this.handleApiError(response.error, 'create instance');
}
return {
success: true,
data: {
message: `Instance '${params.instanceName}' created successfully`,
instance: response.data,
qrCode: response.data?.qrcode ? 'QR code generated - check the response data' : 'No QR code requested'
}
};
}
catch (error) {
return this.handleUnexpectedError(error, 'create instance');
}
}
/**
* Fetch all instances
*/
createFetchInstancesTool() {
const endpoint = instance_endpoints_1.instanceEndpoints.find(e => e.name === 'fetch-instances');
return {
name: 'evolution_fetch_instances',
description: 'List all WhatsApp instances and their current status',
controller: 'instance',
endpoint,
schema: zod_1.z.object({}),
handler: this.fetchInstancesHandler.bind(this),
examples: {
usage: 'Get a list of all WhatsApp instances',
parameters: {}
}
};
}
async fetchInstancesHandler(params) {
try {
const response = await this.httpClient.get('/instance/fetchInstances');
if (!response.success) {
return this.handleApiError(response.error, 'fetch instances');
}
const instances = response.data || [];
return {
success: true,
data: {
message: `Found ${instances.length} instance(s)`,
instances: instances,
summary: instances.map((instance) => ({
name: instance.instanceName || instance.instance,
status: instance.status || 'unknown',
connected: instance.status === 'open'
}))
}
};
}
catch (error) {
return this.handleUnexpectedError(error, 'fetch instances');
}
}
/**
* Connect instance and get QR code
*/
createConnectInstanceTool() {
const endpoint = instance_endpoints_1.instanceEndpoints.find(e => e.name === 'connect-instance');
return {
name: 'evolution_connect_instance',
description: 'Connect a WhatsApp instance and generate QR code for authentication',
controller: 'instance',
endpoint,
schema: zod_1.z.object({
instance: zod_1.z.string()
.min(1, 'Instance name is required')
.describe('Name of the instance to connect')
}),
handler: this.connectInstanceHandler.bind(this),
examples: {
usage: 'Connect an instance and get QR code for WhatsApp authentication',
parameters: {
instance: 'my_whatsapp_bot'
}
}
};
}
async connectInstanceHandler(params) {
try {
const response = await this.httpClient.get(`/instance/connect/${params.instance}`);
if (!response.success) {
return this.handleApiError(response.error, 'connect instance');
}
return {
success: true,
data: {
message: `Instance '${params.instance}' connection initiated`,
qrCode: response.data?.qrcode || response.data?.qr,
status: response.data?.status || 'connecting',
instructions: 'Scan the QR code with WhatsApp on your phone to connect the instance'
}
};
}
catch (error) {
return this.handleUnexpectedError(error, 'connect instance');
}
}
/**
* Restart an instance
*/
createRestartInstanceTool() {
const endpoint = instance_endpoints_1.instanceEndpoints.find(e => e.name === 'restart-instance');
return {
name: 'evolution_restart_instance',
description: 'Restart a WhatsApp instance to refresh its connection',
controller: 'instance',
endpoint,
schema: zod_1.z.object({
instance: zod_1.z.string()
.min(1, 'Instance name is required')
.describe('Name of the instance to restart')
}),
handler: this.restartInstanceHandler.bind(this),
examples: {
usage: 'Restart an instance to fix connection issues',
parameters: {
instance: 'my_whatsapp_bot'
}
}
};
}
async restartInstanceHandler(params) {
try {
const response = await this.httpClient.put(`/instance/restart/${params.instance}`);
if (!response.success) {
return this.handleApiError(response.error, 'restart instance');
}
return {
success: true,
data: {
message: `Instance '${params.instance}' restarted successfully`,
status: response.data?.status || 'restarting',
note: 'The instance may take a few moments to fully restart and reconnect'
}
};
}
catch (error) {
return this.handleUnexpectedError(error, 'restart instance');
}
}
/**
* Delete an instance
*/
createDeleteInstanceTool() {
const endpoint = instance_endpoints_1.instanceEndpoints.find(e => e.name === 'delete-instance');
return {
name: 'evolution_delete_instance',
description: 'Permanently delete a WhatsApp instance and all its data',
controller: 'instance',
endpoint,
schema: zod_1.z.object({
instance: zod_1.z.string()
.min(1, 'Instance name is required')
.describe('Name of the instance to delete')
}),
handler: this.deleteInstanceHandler.bind(this),
examples: {
usage: 'Permanently delete an instance (WARNING: This cannot be undone)',
parameters: {
instance: 'my_whatsapp_bot'
}
}
};
}
async deleteInstanceHandler(params) {
try {
const response = await this.httpClient.delete(`/instance/delete/${params.instance}`);
if (!response.success) {
return this.handleApiError(response.error, 'delete instance');
}
return {
success: true,
data: {
message: `Instance '${params.instance}' deleted successfully`,
warning: 'This action cannot be undone. All data for this instance has been permanently removed.',
result: response.data
}
};
}
catch (error) {
return this.handleUnexpectedError(error, 'delete instance');
}
}
/**
* Set presence status for an instance
*/
createSetPresenceTool() {
const endpoint = instance_endpoints_1.instanceEndpoints.find(e => e.name === 'set-presence');
return {
name: 'evolution_set_presence',
description: 'Set the online presence status for a WhatsApp instance',
controller: 'instance',
endpoint,
schema: zod_1.z.object({
instance: zod_1.z.string()
.min(1, 'Instance name is required')
.describe('Name of the instance'),
presence: zod_1.z.enum(['available', 'unavailable', 'composing', 'recording', 'paused'])
.describe('Presence status to set (available=online, unavailable=offline, composing=typing, recording=recording audio, paused=stopped typing)')
}),
handler: this.setPresenceHandler.bind(this),
examples: {
usage: 'Set the instance presence status to show as online or typing',
parameters: {
instance: 'my_whatsapp_bot',
presence: 'available'
}
}
};
}
async setPresenceHandler(params) {
try {
const requestData = {
presence: params.presence
};
const response = await this.httpClient.post(`/chat/presence/${params.instance}`, requestData);
if (!response.success) {
return this.handleApiError(response.error, 'set presence');
}
const presenceLabels = {
available: 'Online',
unavailable: 'Offline',
composing: 'Typing...',
recording: 'Recording audio...',
paused: 'Stopped typing'
};
return {
success: true,
data: {
message: `Presence for instance '${params.instance}' set to: ${presenceLabels[params.presence]}`,
instance: params.instance,
presence: params.presence,
result: response.data
}
};
}
catch (error) {
return this.handleUnexpectedError(error, 'set presence');
}
}
/**
* Get all instance controller tools
*/
getAllTools() {
return [
this.createCreateInstanceTool(),
this.createFetchInstancesTool(),
this.createConnectInstanceTool(),
this.createRestartInstanceTool(),
this.createDeleteInstanceTool(),
this.createSetPresenceTool()
];
}
/**
* Handle API errors with user-friendly messages
*/
handleApiError(error, operation) {
const baseMessage = `Failed to ${operation}`;
switch (error.type) {
case evolution_http_client_1.ErrorType.AUTHENTICATION_ERROR:
return {
success: false,
error: {
type: 'AUTHENTICATION_ERROR',
message: `${baseMessage}: Authentication failed. Please check your Evolution API key.`,
code: error.code,
details: {
suggestion: 'Verify that your EVOLUTION_API_KEY environment variable is set correctly',
originalError: error.message
}
}
};
case evolution_http_client_1.ErrorType.NETWORK_ERROR:
return {
success: false,
error: {
type: 'NETWORK_ERROR',
message: `${baseMessage}: Network error occurred. Please check your connection to the Evolution API.`,
code: error.code,
details: {
suggestion: 'Verify that your EVOLUTION_URL is correct and the API is accessible',
originalError: error.message
}
}
};
case evolution_http_client_1.ErrorType.TIMEOUT_ERROR:
return {
success: false,
error: {
type: 'TIMEOUT_ERROR',
message: `${baseMessage}: Request timed out. The Evolution API did not respond in time.`,
code: error.code,
details: {
suggestion: 'Try again in a few moments. If the problem persists, check the API server status',
originalError: error.message
}
}
};
case evolution_http_client_1.ErrorType.API_ERROR:
// Handle specific API error codes
if (error.statusCode === 404) {
return {
success: false,
error: {
type: 'API_ERROR',
message: `${baseMessage}: Instance not found or endpoint not available.`,
code: error.code,
details: {
suggestion: 'Check that the instance name is correct and that the instance exists',
statusCode: error.statusCode,
originalError: error.message
}
}
};
}
if (error.statusCode === 400) {
return {
success: false,
error: {
type: 'VALIDATION_ERROR',
message: `${baseMessage}: Invalid parameters provided.`,
code: error.code,
details: {
suggestion: 'Check the parameter values and format',
statusCode: error.statusCode,
originalError: error.message,
apiResponse: error.details
}
}
};
}
if (error.statusCode === 409) {
return {
success: false,
error: {
type: 'API_ERROR',
message: `${baseMessage}: Conflict - instance may already exist or be in use.`,
code: error.code,
details: {
suggestion: 'Try using a different instance name or check if the instance already exists',
statusCode: error.statusCode,
originalError: error.message
}
}
};
}
return {
success: false,
error: {
type: 'API_ERROR',
message: `${baseMessage}: ${error.message}`,
code: error.code,
details: {
statusCode: error.statusCode,
originalError: error.message,
apiResponse: error.details
}
}
};
case evolution_http_client_1.ErrorType.RATE_LIMIT_ERROR:
return {
success: false,
error: {
type: 'RATE_LIMIT_ERROR',
message: `${baseMessage}: Rate limit exceeded. Too many requests.`,
code: error.code,
details: {
suggestion: 'Wait a moment before trying again',
retryAfter: error.details?.retryAfter,
originalError: error.message
}
}
};
default:
return {
success: false,
error: {
type: 'UNKNOWN_ERROR',
message: `${baseMessage}: ${error.message || 'Unknown error occurred'}`,
code: error.code,
details: {
suggestion: 'Please try again or contact support if the problem persists',
originalError: error
}
}
};
}
}
/**
* Handle unexpected errors
*/
handleUnexpectedError(error, operation) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
return {
success: false,
error: {
type: 'UNKNOWN_ERROR',
message: `Failed to ${operation}: ${errorMessage}`,
details: {
suggestion: 'This is an unexpected error. Please try again or contact support.',
originalError: error
}
}
};
}
}
exports.InstanceTools = InstanceTools;