evolution-api-mcp
Version:
MCP Server for Evolution API v2 - Integrate WhatsApp functionality with Claude Desktop and other MCP clients
650 lines (648 loc) • 26 kB
JavaScript
"use strict";
/**
* Group Controller MCP Tools Implementation
* Implements all tools for group management through Evolution API
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.GroupTools = void 0;
const zod_1 = require("zod");
const group_endpoints_1 = require("../../registry/endpoints/group-endpoints");
/**
* Group Controller tool implementations
*/
class GroupTools {
constructor(httpClient) {
this.httpClient = httpClient;
}
/**
* Create a new group
*/
createCreateGroupTool() {
const endpoint = group_endpoints_1.groupEndpoints.find(e => e.name === 'create-group');
return {
name: 'evolution_create_group',
description: 'Create a new WhatsApp group with participants',
controller: 'group',
endpoint,
schema: zod_1.z.object({
instance: zod_1.z.string()
.min(1, 'Instance name is required')
.describe('Name of the WhatsApp instance'),
subject: zod_1.z.string()
.min(1, 'Group name is required')
.max(100, 'Group name cannot exceed 100 characters')
.describe('Name of the group to create'),
description: zod_1.z.string()
.max(512, 'Group description cannot exceed 512 characters')
.optional()
.describe('Optional description for the group'),
participants: zod_1.z.array(zod_1.z.string()
.min(10, 'Valid phone number is required')
.regex(/^\d+$/, 'Phone number must contain only digits')).min(1, 'At least one participant is required')
.max(256, 'Cannot add more than 256 participants at once')
.describe('Array of participant phone numbers (format: 5511999999999)')
}),
handler: this.createGroupHandler.bind(this),
examples: {
usage: 'Create a new WhatsApp group with multiple participants',
parameters: {
instance: 'my_whatsapp_bot',
subject: 'My New Group',
description: 'This is a test group created via Evolution API',
participants: ['5511999999999', '5511888888888']
}
}
};
}
async createGroupHandler(params) {
try {
const requestData = {
subject: params.subject,
participants: params.participants,
...(params.description && { description: params.description })
};
const response = await this.httpClient.post(`/group/create/${params.instance}`, requestData);
if (!response.success) {
return this.handleApiError(response.error, 'create group');
}
return {
success: true,
data: {
message: `Group "${params.subject}" created successfully with ${params.participants.length} participants`,
instance: params.instance,
groupName: params.subject,
groupJid: response.data?.groupJid || response.data?.id,
participantCount: params.participants.length,
participants: params.participants,
description: params.description,
result: response.data
}
};
}
catch (error) {
return this.handleUnexpectedError(error, 'create group');
}
}
/**
* Update group picture
*/
createUpdateGroupPictureTool() {
const endpoint = group_endpoints_1.groupEndpoints.find(e => e.name === 'update-group-picture');
return {
name: 'evolution_update_group_picture',
description: 'Update the profile picture of a WhatsApp group',
controller: 'group',
endpoint,
schema: zod_1.z.object({
instance: zod_1.z.string()
.min(1, 'Instance name is required')
.describe('Name of the WhatsApp instance'),
groupJid: zod_1.z.string()
.min(1, 'Group JID is required')
.regex(/ \.us$/, 'Group JID must end with @g.us')
.describe('JID of the group (format: 120363123456789012@g.us)'),
image: zod_1.z.string()
.min(1, 'Image URL or base64 is required')
.describe('Image URL (https://...) or base64 encoded image data')
}),
handler: this.updateGroupPictureHandler.bind(this),
examples: {
usage: 'Update the profile picture of a WhatsApp group',
parameters: {
instance: 'my_whatsapp_bot',
groupJid: '120363123456789012@g.us',
image: 'https://example.com/group-picture.jpg'
}
}
};
}
async updateGroupPictureHandler(params) {
try {
const requestData = {
groupJid: params.groupJid,
image: params.image
};
const response = await this.httpClient.put(`/group/updateGroupPicture/${params.instance}`, requestData);
if (!response.success) {
return this.handleApiError(response.error, 'update group picture');
}
return {
success: true,
data: {
message: `Group picture updated successfully`,
instance: params.instance,
groupJid: params.groupJid,
imageSource: this.detectImageType(params.image),
result: response.data
}
};
}
catch (error) {
return this.handleUnexpectedError(error, 'update group picture');
}
}
/**
* Update group subject (name)
*/
createUpdateGroupSubjectTool() {
const endpoint = group_endpoints_1.groupEndpoints.find(e => e.name === 'update-group-subject');
return {
name: 'evolution_update_group_subject',
description: 'Update the name/subject of a WhatsApp group',
controller: 'group',
endpoint,
schema: zod_1.z.object({
instance: zod_1.z.string()
.min(1, 'Instance name is required')
.describe('Name of the WhatsApp instance'),
groupJid: zod_1.z.string()
.min(1, 'Group JID is required')
.regex(/ \.us$/, 'Group JID must end with @g.us')
.describe('JID of the group (format: 120363123456789012@g.us)'),
subject: zod_1.z.string()
.min(1, 'Group name is required')
.max(100, 'Group name cannot exceed 100 characters')
.describe('New name for the group')
}),
handler: this.updateGroupSubjectHandler.bind(this),
examples: {
usage: 'Change the name of a WhatsApp group',
parameters: {
instance: 'my_whatsapp_bot',
groupJid: '120363123456789012@g.us',
subject: 'Updated Group Name'
}
}
};
}
async updateGroupSubjectHandler(params) {
try {
const requestData = {
groupJid: params.groupJid,
subject: params.subject
};
const response = await this.httpClient.put(`/group/updateGroupSubject/${params.instance}`, requestData);
if (!response.success) {
return this.handleApiError(response.error, 'update group subject');
}
return {
success: true,
data: {
message: `Group name updated successfully to "${params.subject}"`,
instance: params.instance,
groupJid: params.groupJid,
newSubject: params.subject,
result: response.data
}
};
}
catch (error) {
return this.handleUnexpectedError(error, 'update group subject');
}
}
/**
* Update group description
*/
createUpdateGroupDescriptionTool() {
const endpoint = group_endpoints_1.groupEndpoints.find(e => e.name === 'update-group-description');
return {
name: 'evolution_update_group_description',
description: 'Update the description of a WhatsApp group',
controller: 'group',
endpoint,
schema: zod_1.z.object({
instance: zod_1.z.string()
.min(1, 'Instance name is required')
.describe('Name of the WhatsApp instance'),
groupJid: zod_1.z.string()
.min(1, 'Group JID is required')
.regex(/ \.us$/, 'Group JID must end with @g.us')
.describe('JID of the group (format: 120363123456789012@g.us)'),
description: zod_1.z.string()
.max(512, 'Group description cannot exceed 512 characters')
.describe('New description for the group (empty string to remove description)')
}),
handler: this.updateGroupDescriptionHandler.bind(this),
examples: {
usage: 'Update the description of a WhatsApp group',
parameters: {
instance: 'my_whatsapp_bot',
groupJid: '120363123456789012@g.us',
description: 'This is the updated group description'
}
}
};
}
async updateGroupDescriptionHandler(params) {
try {
const requestData = {
groupJid: params.groupJid,
description: params.description
};
const response = await this.httpClient.put(`/group/updateGroupDescription/${params.instance}`, requestData);
if (!response.success) {
return this.handleApiError(response.error, 'update group description');
}
const action = params.description ? 'updated' : 'removed';
return {
success: true,
data: {
message: `Group description ${action} successfully`,
instance: params.instance,
groupJid: params.groupJid,
newDescription: params.description,
result: response.data
}
};
}
catch (error) {
return this.handleUnexpectedError(error, 'update group description');
}
} /**
* Fetch group invite code
*/
createFetchInviteCodeTool() {
const endpoint = group_endpoints_1.groupEndpoints.find(e => e.name === 'fetch-invite-code');
return {
name: 'evolution_fetch_group_invite_code',
description: 'Get the invite code/link for a WhatsApp group',
controller: 'group',
endpoint,
schema: zod_1.z.object({
instance: zod_1.z.string()
.min(1, 'Instance name is required')
.describe('Name of the WhatsApp instance'),
groupJid: zod_1.z.string()
.min(1, 'Group JID is required')
.regex(/ \.us$/, 'Group JID must end with @g.us')
.describe('JID of the group (format: 120363123456789012@g.us)')
}),
handler: this.fetchInviteCodeHandler.bind(this),
examples: {
usage: 'Get the invite link for a WhatsApp group',
parameters: {
instance: 'my_whatsapp_bot',
groupJid: '120363123456789012@g.us'
}
}
};
}
async fetchInviteCodeHandler(params) {
try {
const requestData = {
groupJid: params.groupJid
};
const response = await this.httpClient.post(`/group/fetchInviteCode/${params.instance}`, requestData);
if (!response.success) {
return this.handleApiError(response.error, 'fetch group invite code');
}
const inviteCode = response.data?.inviteCode || response.data?.code;
const inviteUrl = inviteCode ? `https://chat.whatsapp.com/${inviteCode}` : null;
return {
success: true,
data: {
message: `Group invite code retrieved successfully`,
instance: params.instance,
groupJid: params.groupJid,
inviteCode: inviteCode,
inviteUrl: inviteUrl,
result: response.data
}
};
}
catch (error) {
return this.handleUnexpectedError(error, 'fetch group invite code');
}
}
/**
* Revoke group invite code
*/
createRevokeInviteCodeTool() {
const endpoint = group_endpoints_1.groupEndpoints.find(e => e.name === 'revoke-invite-code');
return {
name: 'evolution_revoke_group_invite_code',
description: 'Revoke the current invite code for a WhatsApp group (generates a new one)',
controller: 'group',
endpoint,
schema: zod_1.z.object({
instance: zod_1.z.string()
.min(1, 'Instance name is required')
.describe('Name of the WhatsApp instance'),
groupJid: zod_1.z.string()
.min(1, 'Group JID is required')
.regex(/ \.us$/, 'Group JID must end with @g.us')
.describe('JID of the group (format: 120363123456789012@g.us)')
}),
handler: this.revokeInviteCodeHandler.bind(this),
examples: {
usage: 'Revoke the current invite link and generate a new one for a WhatsApp group',
parameters: {
instance: 'my_whatsapp_bot',
groupJid: '120363123456789012@g.us'
}
}
};
}
async revokeInviteCodeHandler(params) {
try {
const requestData = {
groupJid: params.groupJid
};
const response = await this.httpClient.put(`/group/revokeInviteCode/${params.instance}`, requestData);
if (!response.success) {
return this.handleApiError(response.error, 'revoke group invite code');
}
const newInviteCode = response.data?.inviteCode || response.data?.code;
const newInviteUrl = newInviteCode ? `https://chat.whatsapp.com/${newInviteCode}` : null;
return {
success: true,
data: {
message: `Group invite code revoked and new code generated successfully`,
instance: params.instance,
groupJid: params.groupJid,
newInviteCode: newInviteCode,
newInviteUrl: newInviteUrl,
result: response.data
}
};
}
catch (error) {
return this.handleUnexpectedError(error, 'revoke group invite code');
}
}
/**
* Update group participants (add, remove, promote, demote)
*/
createUpdateParticipantTool() {
const endpoint = group_endpoints_1.groupEndpoints.find(e => e.name === 'update-participant');
return {
name: 'evolution_update_group_participant',
description: 'Manage group participants: add, remove, promote to admin, or demote from admin',
controller: 'group',
endpoint,
schema: zod_1.z.object({
instance: zod_1.z.string()
.min(1, 'Instance name is required')
.describe('Name of the WhatsApp instance'),
groupJid: zod_1.z.string()
.min(1, 'Group JID is required')
.regex(/ \.us$/, 'Group JID must end with @g.us')
.describe('JID of the group (format: 120363123456789012@g.us)'),
action: zod_1.z.enum(['add', 'remove', 'promote', 'demote'])
.describe('Action to perform: add (new members), remove (kick members), promote (make admin), demote (remove admin)'),
participants: zod_1.z.array(zod_1.z.string()
.min(10, 'Valid phone number is required')
.regex(/^\d+$/, 'Phone number must contain only digits')).min(1, 'At least one participant is required')
.max(50, 'Cannot process more than 50 participants at once')
.describe('Array of participant phone numbers (format: 5511999999999)')
}),
handler: this.updateParticipantHandler.bind(this),
examples: {
usage: 'Add, remove, promote, or demote participants in a WhatsApp group',
parameters: {
instance: 'my_whatsapp_bot',
groupJid: '120363123456789012@g.us',
action: 'add',
participants: ['5511999999999', '5511888888888']
}
}
};
}
async updateParticipantHandler(params) {
try {
const requestData = {
groupJid: params.groupJid,
action: params.action,
participants: params.participants
};
const response = await this.httpClient.put(`/group/updateParticipant/${params.instance}`, requestData);
if (!response.success) {
return this.handleApiError(response.error, 'update group participants');
}
const actionLabels = {
add: 'added to',
remove: 'removed from',
promote: 'promoted to admin in',
demote: 'demoted from admin in'
};
return {
success: true,
data: {
message: `${params.participants.length} participant(s) ${actionLabels[params.action]} the group successfully`,
instance: params.instance,
groupJid: params.groupJid,
action: params.action,
participantCount: params.participants.length,
participants: params.participants,
result: response.data
}
};
}
catch (error) {
return this.handleUnexpectedError(error, 'update group participants');
}
}
/**
* Leave group
*/
createLeaveGroupTool() {
const endpoint = group_endpoints_1.groupEndpoints.find(e => e.name === 'leave-group');
return {
name: 'evolution_leave_group',
description: 'Leave a WhatsApp group (the bot instance will exit the group)',
controller: 'group',
endpoint,
schema: zod_1.z.object({
instance: zod_1.z.string()
.min(1, 'Instance name is required')
.describe('Name of the WhatsApp instance'),
groupJid: zod_1.z.string()
.min(1, 'Group JID is required')
.regex(/ \.us$/, 'Group JID must end with @g.us')
.describe('JID of the group to leave (format: 120363123456789012@g.us)')
}),
handler: this.leaveGroupHandler.bind(this),
examples: {
usage: 'Leave a WhatsApp group',
parameters: {
instance: 'my_whatsapp_bot',
groupJid: '120363123456789012@g.us'
}
}
};
}
async leaveGroupHandler(params) {
try {
const requestData = {
groupJid: params.groupJid
};
const response = await this.httpClient.delete(`/group/leaveGroup/${params.instance}`, requestData);
if (!response.success) {
return this.handleApiError(response.error, 'leave group');
}
return {
success: true,
data: {
message: `Successfully left the group`,
instance: params.instance,
groupJid: params.groupJid,
result: response.data
}
};
}
catch (error) {
return this.handleUnexpectedError(error, 'leave group');
}
}
/**
* Fetch group information
*/
createFetchGroupInfoTool() {
const endpoint = group_endpoints_1.groupEndpoints.find(e => e.name === 'fetch-group-info');
return {
name: 'evolution_fetch_group_info',
description: 'Fetch information about all groups the instance is part of',
controller: 'group',
endpoint,
schema: zod_1.z.object({
instance: zod_1.z.string()
.min(1, 'Instance name is required')
.describe('Name of the WhatsApp instance')
}),
handler: this.fetchGroupInfoHandler.bind(this),
examples: {
usage: 'Get information about all groups the bot is part of',
parameters: {
instance: 'my_whatsapp_bot'
}
}
};
}
async fetchGroupInfoHandler(params) {
try {
const response = await this.httpClient.get(`/group/fetchAllGroups/${params.instance}`);
if (!response.success) {
return this.handleApiError(response.error, 'fetch group information');
}
const groups = response.data || [];
const groupCount = Array.isArray(groups) ? groups.length : 0;
return {
success: true,
data: {
message: `Found ${groupCount} group(s)`,
instance: params.instance,
groupCount,
groups: groups
}
};
}
catch (error) {
return this.handleUnexpectedError(error, 'fetch group information');
}
}
/**
* Get all group tools
*/
getAllTools() {
return [
this.createCreateGroupTool(),
this.createUpdateGroupPictureTool(),
this.createUpdateGroupSubjectTool(),
this.createUpdateGroupDescriptionTool(),
this.createFetchInviteCodeTool(),
this.createRevokeInviteCodeTool(),
this.createUpdateParticipantTool(),
this.createLeaveGroupTool(),
this.createFetchGroupInfoTool()
];
}
/**
* Detect image type from URL or base64
*/
detectImageType(image) {
if (image.startsWith('data:')) {
return 'base64';
}
else if (image.startsWith('http')) {
return 'url';
}
return 'unknown';
}
/**
* Handle API errors with user-friendly messages
*/
handleApiError(error, operation) {
const statusCode = error.statusCode || error.status || 0;
let message = `Failed to ${operation}`;
switch (statusCode) {
case 400:
message = `Invalid request parameters for ${operation}`;
break;
case 401:
message = `Authentication failed for ${operation}`;
break;
case 403:
message = `Permission denied for ${operation}`;
break;
case 404:
message = `Resource not found for ${operation}`;
break;
case 422:
message = `Invalid data provided for ${operation}`;
break;
case 429:
message = `Rate limit exceeded for ${operation}`;
break;
case 500:
message = `Server error occurred during ${operation}`;
break;
}
return {
success: false,
error: {
type: this.mapStatusToErrorType(statusCode),
message,
code: statusCode.toString(),
details: error
}
};
}
/**
* Handle unexpected errors
*/
handleUnexpectedError(error, operation) {
const errorMessage = error instanceof Error ? error.message : 'An unexpected error occurred';
return {
success: false,
error: {
type: 'UNKNOWN_ERROR',
message: `Unexpected error during ${operation}: ${errorMessage}`,
details: error
}
};
}
/**
* Map HTTP status codes to error types
*/
mapStatusToErrorType(statusCode) {
switch (statusCode) {
case 400:
case 422:
return 'VALIDATION_ERROR';
case 401:
case 403:
return 'AUTHENTICATION_ERROR';
case 404:
return 'NOT_FOUND_ERROR';
case 429:
return 'RATE_LIMIT_ERROR';
case 500:
case 502:
case 503:
return 'API_ERROR';
default:
return 'NETWORK_ERROR';
}
}
}
exports.GroupTools = GroupTools;