Passo a Passo
### MCP N8N + API WHATSAPP ###
1- Instalar o Docker Desktop e Node
2- Instalar N8N
- RUN:
> definir pasta | /home/node/.n8n
> definir variável
- GENERIC_TIMEZONE=America/Sao_Paulo | America/Sao_Paulo
- Abrir o N8N
> Download community nodes | n8n-nodes-mcp
3- Instalar Evolution
- Download do Pacote
> docker compose up
4- Instalar o MCP Server
- Download do Pacote
> npm i
5- Criar o Node (usar o modelo ou fazer do zero)
- AI Agent
> configurar credenciais de API Key (OpenAi Model)
> configurar o prompt (AI Agent)
- Você é um agente que usa MCP (model context protocol). Use o 'BuscarTool' para informações de ferramentas. Use 'ExecutarTool' para executar ferramentas.
- MCP
> configurar credenciais
- listTools
> command: node
> arguments: /home/node/.n8n/index.js
> enviorment
EVOLUTION_INSTANCIA=
EVOLUTION_APIKEY=
EVOLUTION_API_BASE=
- executeTools
> Tool Name: {{ $fromAI("toolName", "nome da tool a serr chamada") }}
> Tool Parameter: Defined automatically by the model (AI)
Evolution
docker-compose.yml
services:
evo_api:
container_name: evolution_api_aula
image: atendai/evolution-api:v2.2.0
restart: always
ports:
- 8088:8080
volumes:
- evolution_api_aula:/evolution/instances
networks:
- evolution-net
env_file:
- .env
postgres:
image: postgres:13
restart: always
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: typebot
POSTGRES_DB: evolution_db
volumes:
- pg_data:/var/lib/postgresql/data
networks:
- evolution-net
- default
redis:
image: redis:alpine
restart: always
ports:
- "6388:6379"
networks:
- evolution-net
- default
volumes:
evolution_api_aula:
pg_data:
networks:
evolution-net:
driver: bridge
.env
SERVER_TYPE=http
SERVER_PORT=8080
SERVER_URL=http://localhost:8080
SENTRY_DSN=
CORS_ORIGIN=*
CORS_METHODS=GET,POST,PUT,DELETE
CORS_CREDENTIALS=true
LOG_LEVEL=ERROR,WARN,DEBUG,INFO,LOG,VERBOSE,DARK,WEBHOOKS
LOG_COLOR=true
LOG_BAILEYS=error
DEL_INSTANCE=false
DATABASE_PROVIDER=postgresql
DATABASE_CONNECTION_URI=postgresql://postgres:typebot@postgres:5432/evolution_db?schema=public
DATABASE_CONNECTION_CLIENT_NAME=evolution_exchange
DATABASE_SAVE_DATA_INSTANCE=true
DATABASE_SAVE_DATA_NEW_MESSAGE=true
DATABASE_SAVE_MESSAGE_UPDATE=true
DATABASE_SAVE_DATA_CONTACTS=true
DATABASE_SAVE_DATA_CHATS=true
DATABASE_SAVE_DATA_LABELS=true
DATABASE_SAVE_DATA_HISTORIC=true
RABBITMQ_ENABLED=false
RABBITMQ_URI=amqp://localhost
RABBITMQ_EXCHANGE_NAME=evolution
RABBITMQ_GLOBAL_ENABLED=false
RABBITMQ_EVENTS_APPLICATION_STARTUP=false
RABBITMQ_EVENTS_INSTANCE_CREATE=false
RABBITMQ_EVENTS_INSTANCE_DELETE=false
RABBITMQ_EVENTS_QRCODE_UPDATED=false
RABBITMQ_EVENTS_MESSAGES_SET=false
RABBITMQ_EVENTS_MESSAGES_UPSERT=false
RABBITMQ_EVENTS_MESSAGES_EDITED=false
RABBITMQ_EVENTS_MESSAGES_UPDATE=false
RABBITMQ_EVENTS_MESSAGES_DELETE=false
RABBITMQ_EVENTS_SEND_MESSAGE=false
RABBITMQ_EVENTS_CONTACTS_SET=false
RABBITMQ_EVENTS_CONTACTS_UPSERT=false
RABBITMQ_EVENTS_CONTACTS_UPDATE=false
RABBITMQ_EVENTS_PRESENCE_UPDATE=false
RABBITMQ_EVENTS_CHATS_SET=false
RABBITMQ_EVENTS_CHATS_UPSERT=false
RABBITMQ_EVENTS_CHATS_UPDATE=false
RABBITMQ_EVENTS_CHATS_DELETE=false
RABBITMQ_EVENTS_GROUPS_UPSERT=false
RABBITMQ_EVENTS_GROUP_UPDATE=false
RABBITMQ_EVENTS_GROUP_PARTICIPANTS_UPDATE=false
RABBITMQ_EVENTS_CONNECTION_UPDATE=false
RABBITMQ_EVENTS_CALL=false
RABBITMQ_EVENTS_TYPEBOT_START=false
RABBITMQ_EVENTS_TYPEBOT_CHANGE_STATUS=false
SQS_ENABLED=false
SQS_ACCESS_KEY_ID=
SQS_SECRET_ACCESS_KEY=
SQS_ACCOUNT_ID=
SQS_REGION=
WEBSOCKET_ENABLED=false
WEBSOCKET_GLOBAL_EVENTS=false
WA_BUSINESS_TOKEN_WEBHOOK=evolution
WA_BUSINESS_URL=https://graph.facebook.com
WA_BUSINESS_VERSION=v20.0
WA_BUSINESS_LANGUAGE=en_US
WEBHOOK_GLOBAL_ENABLED=false
WEBHOOK_GLOBAL_URL=''
WEBHOOK_GLOBAL_WEBHOOK_BY_EVENTS=false
WEBHOOK_EVENTS_APPLICATION_STARTUP=false
WEBHOOK_EVENTS_QRCODE_UPDATED=true
WEBHOOK_EVENTS_MESSAGES_SET=true
WEBHOOK_EVENTS_MESSAGES_UPSERT=true
WEBHOOK_EVENTS_MESSAGES_EDITED=true
WEBHOOK_EVENTS_MESSAGES_UPDATE=true
WEBHOOK_EVENTS_MESSAGES_DELETE=true
WEBHOOK_EVENTS_SEND_MESSAGE=true
WEBHOOK_EVENTS_CONTACTS_SET=true
WEBHOOK_EVENTS_CONTACTS_UPSERT=true
WEBHOOK_EVENTS_CONTACTS_UPDATE=true
WEBHOOK_EVENTS_PRESENCE_UPDATE=true
WEBHOOK_EVENTS_CHATS_SET=true
WEBHOOK_EVENTS_CHATS_UPSERT=true
WEBHOOK_EVENTS_CHATS_UPDATE=true
WEBHOOK_EVENTS_CHATS_DELETE=true
WEBHOOK_EVENTS_GROUPS_UPSERT=true
WEBHOOK_EVENTS_GROUPS_UPDATE=true
WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE=true
WEBHOOK_EVENTS_CONNECTION_UPDATE=true
WEBHOOK_EVENTS_LABELS_EDIT=true
WEBHOOK_EVENTS_LABELS_ASSOCIATION=true
WEBHOOK_EVENTS_CALL=true
WEBHOOK_EVENTS_TYPEBOT_START=false
WEBHOOK_EVENTS_TYPEBOT_CHANGE_STATUS=false
WEBHOOK_EVENTS_ERRORS=false
WEBHOOK_EVENTS_ERRORS_WEBHOOK=
CONFIG_SESSION_PHONE_CLIENT=Evolution API
CONFIG_SESSION_PHONE_NAME=Chrome
QRCODE_LIMIT=30
QRCODE_COLOR='#175197'
TYPEBOT_ENABLED=true
TYPEBOT_API_VERSION=latest
CHATWOOT_ENABLED=false
CHATWOOT_MESSAGE_READ=true
CHATWOOT_MESSAGE_DELETE=true
CHATWOOT_IMPORT_DATABASE_CONNECTION_URI=postgresql://user:password@host:5432/chatwoot?sslmode=disable
CHATWOOT_IMPORT_PLACEHOLDER_MEDIA_MESSAGE=true
OPENAI_ENABLED=false
DIFY_ENABLED=false
CACHE_REDIS_ENABLED=true
CACHE_REDIS_URI=redis://redis:6379/2
CACHE_REDIS_PREFIX_KEY=evolution
CACHE_REDIS_SAVE_INSTANCES=false
CACHE_LOCAL_ENABLED=false
S3_ENABLED=false
S3_ACCESS_KEY=
S3_SECRET_KEY=
S3_BUCKET=evolution
S3_PORT=443
S3_ENDPOINT=s3.domain.com
S3_REGION=eu-west-3
S3_USE_SSL=true
AUTHENTICATION_API_KEY=429683C4C977415CAAFCCE10F7D57E11
AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES=true
LANGUAGE=en
MCP
index.js
const { Server } = require("@modelcontextprotocol/sdk/server/index.js");
const { StdioServerTransport } = require("@modelcontextprotocol/sdk/server/stdio.js");
const { CallToolRequestSchema, ListToolsRequestSchema } = require("@modelcontextprotocol/sdk/types.js");
const { z } = require("zod");
const axios = require("axios");
const dotenv = require("dotenv");
dotenv.config();
// Esquemas de validação com Zod
const schemas = {
toolInputs: {
enviaMensagem: z.object({
number: z.string(),
mensagem: z.string(),
}),
criaGrupo: z.object({
subject: z.string(),
description: z.string().optional(),
participants: z.array(z.string()),
}),
buscaGrupos: z.object({
getParticipants: z.boolean().optional().default(false),
}),
buscaParticipantesGrupo: z.object({
groupJid: z.string(),
}),
},
};
// Definições das ferramentas (tools)
const TOOL_DEFINITIONS = [
{
name: "envia_mensagem",
description: "Envia mensagem de texto via API Evolution",
inputSchema: {
type: "object",
properties: {
number: { type: "string", description: "Número do destinatário com DDI e DDD" },
mensagem: { type: "string", description: "Texto da mensagem a ser enviada" },
},
required: ["number", "mensagem"],
},
},
{
name: "cria_grupo",
description: "Cria um grupo via API Evolution",
inputSchema: {
type: "object",
properties: {
subject: { type: "string", description: "Nome do grupo" },
description: { type: "string", description: "Descrição do grupo" },
participants: {
type: "array",
items: { type: "string" },
description: "Participantes do grupo (números com DDI/DDD)"
},
},
required: ["subject", "participants"],
},
},
{
name: "busca_grupos",
description: "Busca todos os grupos da instância com opção de listar participantes.",
inputSchema: {
type: "object",
properties: {
getParticipants: {
type: "boolean",
description: "Listar participantes dos grupos?",
default: false,
},
},
required: [],
},
},
{
name: "busca_participantes_grupo",
description: "Busca participantes específicos de um grupo pela instância.",
inputSchema: {
type: "object",
properties: {
groupJid: { type: "string", description: "Identificador do grupo" },
},
required: ["groupJid"],
},
},
];
// Handlers das ferramentas
const toolHandlers = {
envia_mensagem: async (args) => {
const parsed = schemas.toolInputs.enviaMensagem.parse(args);
const instancia = process.env.EVOLUTION_INSTANCIA;
const apikey = process.env.EVOLUTION_APIKEY;
const apiBase = process.env.EVOLUTION_API_BASE || "sua_url_evolution";
const url = `http://${apiBase}/message/sendText/${instancia}`;
const response = await axios.post(url, {
number: parsed.number,
text: parsed.mensagem,
}, {
headers: {
"Content-Type": "application/json",
"apikey": apikey,
},
});
return {
content: [{
type: "text",
text: `Mensagem enviada com sucesso para ${parsed.number}.\nResposta: ${JSON.stringify(response.data)}`,
}],
};
},
cria_grupo: async (args) => {
const parsed = schemas.toolInputs.criaGrupo.parse(args);
const instancia = process.env.EVOLUTION_INSTANCIA;
const apikey = process.env.EVOLUTION_APIKEY;
const apiBase = process.env.EVOLUTION_API_BASE || "url_evolution";
const url = `http://${apiBase}/group/create/${instancia}`;
const response = await axios.post(url, {
subject: parsed.subject,
description: parsed.description,
participants: parsed.participants,
}, {
headers: {
"Content-Type": "application/json",
"apikey": apikey,
},
});
return {
content: [{
type: "text",
text: `Grupo criado com sucesso!\nResposta: ${JSON.stringify(response.data)}`,
}],
};
},
busca_grupos: async (args) => {
const parsed = schemas.toolInputs.buscaGrupos.parse(args);
const instancia = process.env.EVOLUTION_INSTANCIA;
const apikey = process.env.EVOLUTION_APIKEY;
const apiBase = process.env.EVOLUTION_API_BASE || "url_evolution";
const url = `http://${apiBase}/group/fetchAllGroups/${instancia}?getParticipants=${parsed.getParticipants}`;
try {
const response = await axios.get(url, {
headers: {
"Content-Type": "application/json",
"apikey": apikey,
},
});
return {
content: [{
type: "text",
text: `Grupos obtidos com sucesso:\n${JSON.stringify(response.data, null, 2)}`,
}],
};
} catch (error) {
console.error("Erro na chamada API Evolution:", error);
return {
content: [{
type: "text",
text: `Erro ao obter grupos: ${error.message}`,
}],
};
}
},
busca_participantes_grupo: async (args) => {
const parsed = schemas.toolInputs.buscaParticipantesGrupo.parse(args);
const instancia = process.env.EVOLUTION_INSTANCIA;
const apikey = process.env.EVOLUTION_APIKEY;
const apiBase = process.env.EVOLUTION_API_BASE || "url_evolution";
const url = `http://${apiBase}/group/participants/${instancia}?groupJid=${parsed.groupJid}`;
try {
const response = await axios.get(url, {
headers: {
"Content-Type": "application/json",
"apikey": apikey,
},
});
return {
content: [{
type: "text",
text: `Participantes obtidos com sucesso:\n${JSON.stringify(response.data, null, 2)}`,
}],
};
} catch (error) {
console.error("Erro na chamada API Evolution:", error);
return {
content: [{
type: "text",
text: `Erro ao obter participantes: ${error.message}`,
}],
};
}
},
};
// Instância do servidor MPC
const server = new Server(
{ name: "evolution-tools-server", version: "1.0.0" },
{ capabilities: { tools: {} } }
);
// Handlers das requisições MPC
server.setRequestHandler(ListToolsRequestSchema, async () => {
console.error("Ferramenta requesitada pelo cliente");
return { tools: TOOL_DEFINITIONS };
});
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
const handler = toolHandlers[name];
if (!handler) throw new Error(`Tool Desconhecida: ${name}`);
return await handler(args);
} catch (error) {
console.error(`Erro executando a tool ${name}:`, error);
throw error;
}
});
// Execução principal
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Evolution API MPC Server rodando no stdio");
}
// Execução direta por argumentos CLI
const args = process.argv.slice(2);
if (args.length > 0) {
const funcao = args[0];
const input = args[1] ? JSON.parse(args[1]) : {};
console.log("🔐 Variáveis de ambiente utilizadas:");
console.log("EVOLUTION_INSTANCIA:", process.env.EVOLUTION_INSTANCIA);
console.log("EVOLUTION_APIKEY:", process.env.EVOLUTION_APIKEY);
console.log("EVOLUTION_API_BASE:", process.env.EVOLUTION_API_BASE);
if (toolHandlers[funcao]) {
toolHandlers[funcao](input)
.then((res) => {
console.log(JSON.stringify(res, null, 2));
process.exit(0);
})
.catch((err) => {
console.error(`Erro ao executar ${funcao}:`, err);
process.exit(1);
});
} else {
console.error(`❌ Função desconhecida: ${funcao}`);
process.exit(1);
}
} else {
main().catch((error) => {
console.error("Erro Fatal:", error);
process.exit(1);
});
}
package.json
{
"name": "mcp",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.8.0",
"axios": "^1.8.4",
"dotenv": "^16.4.7",
"zod": "^3.24.2"
}
}