6 tasks: 4 sub-workflows de envio (enviar_texto, enviar_template, enviar_template_com_parametros, enviar_template_marketing), servidor MCP com as 4 tools, e task final dedicada a resolver a pendência real de autenticação (ApiID de mensageria não confirmado) com gate de confirmação explícita do usuário antes de qualquer disparo de teste real -- diferente de agendamento fictício, mensagem enviada não pode ser desfeita. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
30 KiB
MCP Server Nativo no n8n para Mensageria (Z-PRO) Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task (não subagent-driven-development — as tasks são chamadas de API do n8n, não commits de código; não há diff de git pra revisar por task). Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Construir um MCP Server nativo do n8n, standalone, com 4 tools de envio de mensagem WhatsApp via API do Z-PRO — desacoplado de qualquer provider de agendamento, reutilizável por qualquer workflow futuro.
Architecture: 5 workflows n8n — o servidor (MCP Server Trigger + 4 tools) e quatro sub-workflows (enviar_texto, enviar_template, enviar_template_com_parametros, enviar_template_marketing), mesmo padrão já usado nos MCP Servers de agendamento deste projeto.
Tech Stack: n8n (devn8n.gtigrupo.com.br), nodes oficiais @n8n/n8n-nodes-langchain (mcpTrigger, toolWorkflow), n8n-nodes-base (executeWorkflowTrigger, httpRequest, code, set).
Global Constraints
- Nenhuma tool inventa campo fora do que foi documentado pelo usuário (ver spec) — parâmetros de template cobrem só os exemplos reais mostrados.
- Classificação de erro genérica por status HTTP (mesma taxonomia dos outros MCP Servers) — a API de mensageria não teve nenhum código de erro específico documentado.
- Pendência real de autenticação: a credencial
ZPRO API Token - Canal QR - MC AGENDAMENTO(8XHGuA4cg9z1jUq8) é uma hipótese razoável pra esta integração (não confirmada). OapiIdde mensageria também não é conhecido ainda — fica como placeholder até confirmação. - Nenhum teste real de envio acontece sem confirmação explícita do usuário antes de cada disparo — diferente de criar um agendamento fictício (que pode ser apagado), uma mensagem de WhatsApp enviada não pode ser desfeita. Testar só com um número de celular de teste controlado, nunca um número de paciente real.
Spec de referência: docs/superpowers/specs/2026-08-11-mcp-server-mensageria-zpro-design.md
Task 1: Sub-workflow "MCP Server - Mensageria ZPRO - enviar_texto"
Interfaces:
-
Produces: workflow
MCP Server - Mensageria ZPRO - enviar_texto, recebenumber,body,externalKey(obrigatórios),isClosed(opcional), retorna o corpo cru dePOST /v2/api/external/{apiId}/ou erro classificado. -
Step 1: Criar o workflow
{
"name": "MCP Server - Mensageria ZPRO - enviar_texto",
"nodes": [
{
"id": "trigger",
"name": "Execute Workflow Trigger",
"type": "n8n-nodes-base.executeWorkflowTrigger",
"typeVersion": 1.2,
"position": [0, 0],
"parameters": {
"inputSource": "workflowInputs",
"workflowInputs": {
"values": [
{ "name": "number", "type": "string" },
{ "name": "body", "type": "string" },
{ "name": "externalKey", "type": "string" },
{ "name": "isClosed", "type": "boolean" }
]
}
}
},
{
"id": "config",
"name": "Config Mensageria",
"type": "n8n-nodes-base.set",
"typeVersion": 3.5,
"position": [260, 0],
"parameters": {
"mode": "manual",
"includeOtherFields": true,
"assignments": {
"assignments": [
{ "id": "cfg-baseurl", "name": "baseUrl", "type": "string", "value": "https://apigchat.gtigrupo.com.br" },
{ "id": "cfg-apiid", "name": "apiId", "type": "string", "value": "SUBSTITUIR_PELO_APIID_DE_MENSAGERIA" }
]
}
}
},
{
"id": "montar-corpo",
"name": "Montar corpo",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [520, 0],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const input = $json;\nconst requestBody = {\n body: input.body,\n number: input.number,\n externalKey: input.externalKey,\n};\nif (input.isClosed !== undefined && input.isClosed !== null && input.isClosed !== \"\") {\n requestBody.isClosed = input.isClosed;\n}\n\nreturn {\n json: {\n baseUrl: input.baseUrl,\n apiId: input.apiId,\n requestBody,\n },\n};"
}
},
{
"id": "post-enviar-texto",
"name": "POST enviar texto",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [780, 0],
"parameters": {
"method": "POST",
"url": "={{ $json.baseUrl }}/v2/api/external/{{ $json.apiId }}/",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"contentType": "json",
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify($json.requestBody) }}",
"options": {
"response": { "response": { "fullResponse": true, "neverError": true, "responseFormat": "autodetect" } },
"timeout": 15000
}
},
"credentials": {
"httpHeaderAuth": { "id": "8XHGuA4cg9z1jUq8", "name": "ZPRO API Token - Canal QR - MC AGENDAMENTO" }
}
},
{
"id": "classificar",
"name": "Classificar resultado",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [1040, 0],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const status = $json.statusCode;\nconst rawBody = $json.body;\n\nfunction classifyError(status, context) {\n if (status === 401 || status === 403) {\n return { code: \"AUTHENTICATION_ERROR\", message: `Falha de autenticação na API de mensageria do Z-PRO ao ${context} (status ${status}).` };\n }\n if (status === 404) {\n return { code: \"NOT_FOUND\", message: `Recurso não encontrado na API de mensageria do Z-PRO ao ${context} (status ${status}). Verifique o ApiID.` };\n }\n if (status === 400 || status === 422) {\n return { code: \"VALIDATION_ERROR\", message: `A API de mensageria do Z-PRO rejeitou os dados enviados ao ${context} (status ${status}).` };\n }\n return { code: \"ZPRO_MENSAGERIA_API_ERROR\", message: `A API de mensageria do Z-PRO retornou um erro inesperado ao ${context} (status ${status}).` };\n}\n\nif (status < 200 || status >= 300) {\n return { json: { success: false, error: classifyError(status, \"enviar mensagem de texto\") } };\n}\n\nreturn { json: { success: true, data: rawBody } };"
}
}
],
"connections": {
"Execute Workflow Trigger": { "main": [[{ "node": "Config Mensageria", "type": "main", "index": 0 }]] },
"Config Mensageria": { "main": [[{ "node": "Montar corpo", "type": "main", "index": 0 }]] },
"Montar corpo": { "main": [[{ "node": "POST enviar texto", "type": "main", "index": 0 }]] },
"POST enviar texto": { "main": [[{ "node": "Classificar resultado", "type": "main", "index": 0 }]] }
}
}
Guarde o id retornado como <wf-enviar-texto>.
- Step 2: Validar
n8n_validate_workflow com {"id": "<wf-enviar-texto>"}. Esperado: sem erros.
Task 2: Sub-workflow "MCP Server - Mensageria ZPRO - enviar_template"
Interfaces:
-
Produces: workflow
MCP Server - Mensageria ZPRO - enviar_template, recebenumber,templateName,templateLanguageCode(obrigatórios),isClosed(opcional), retorna o corpo cru dePOST /v2/api/external/{apiId}/templateou erro classificado. -
Step 1: Criar o workflow
{
"name": "MCP Server - Mensageria ZPRO - enviar_template",
"nodes": [
{
"id": "trigger",
"name": "Execute Workflow Trigger",
"type": "n8n-nodes-base.executeWorkflowTrigger",
"typeVersion": 1.2,
"position": [0, 0],
"parameters": {
"inputSource": "workflowInputs",
"workflowInputs": {
"values": [
{ "name": "number", "type": "string" },
{ "name": "templateName", "type": "string" },
{ "name": "templateLanguageCode", "type": "string" },
{ "name": "isClosed", "type": "boolean" }
]
}
}
},
{
"id": "config",
"name": "Config Mensageria",
"type": "n8n-nodes-base.set",
"typeVersion": 3.5,
"position": [260, 0],
"parameters": {
"mode": "manual",
"includeOtherFields": true,
"assignments": {
"assignments": [
{ "id": "cfg-baseurl", "name": "baseUrl", "type": "string", "value": "https://apigchat.gtigrupo.com.br" },
{ "id": "cfg-apiid", "name": "apiId", "type": "string", "value": "SUBSTITUIR_PELO_APIID_DE_MENSAGERIA" }
]
}
}
},
{
"id": "montar-corpo",
"name": "Montar corpo",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [520, 0],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const input = $json;\nconst requestBody = {\n number: input.number,\n templateData: {\n messaging_product: \"whatsapp\",\n to: input.number,\n type: \"template\",\n template: {\n name: input.templateName,\n language: { code: input.templateLanguageCode },\n },\n },\n};\nif (input.isClosed !== undefined && input.isClosed !== null && input.isClosed !== \"\") {\n requestBody.isClosed = input.isClosed;\n}\n\nreturn {\n json: {\n baseUrl: input.baseUrl,\n apiId: input.apiId,\n requestBody,\n },\n};"
}
},
{
"id": "post-enviar-template",
"name": "POST enviar template",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [780, 0],
"parameters": {
"method": "POST",
"url": "={{ $json.baseUrl }}/v2/api/external/{{ $json.apiId }}/template",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"contentType": "json",
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify($json.requestBody) }}",
"options": {
"response": { "response": { "fullResponse": true, "neverError": true, "responseFormat": "autodetect" } },
"timeout": 15000
}
},
"credentials": {
"httpHeaderAuth": { "id": "8XHGuA4cg9z1jUq8", "name": "ZPRO API Token - Canal QR - MC AGENDAMENTO" }
}
},
{
"id": "classificar",
"name": "Classificar resultado",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [1040, 0],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const status = $json.statusCode;\nconst rawBody = $json.body;\n\nfunction classifyError(status, context) {\n if (status === 401 || status === 403) {\n return { code: \"AUTHENTICATION_ERROR\", message: `Falha de autenticação na API de mensageria do Z-PRO ao ${context} (status ${status}).` };\n }\n if (status === 404) {\n return { code: \"NOT_FOUND\", message: `Recurso não encontrado na API de mensageria do Z-PRO ao ${context} (status ${status}). Verifique o ApiID.` };\n }\n if (status === 400 || status === 422) {\n return { code: \"VALIDATION_ERROR\", message: `A API de mensageria do Z-PRO rejeitou os dados enviados ao ${context} (status ${status}).` };\n }\n return { code: \"ZPRO_MENSAGERIA_API_ERROR\", message: `A API de mensageria do Z-PRO retornou um erro inesperado ao ${context} (status ${status}).` };\n}\n\nif (status < 200 || status >= 300) {\n return { json: { success: false, error: classifyError(status, \"enviar template\") } };\n}\n\nreturn { json: { success: true, data: rawBody } };"
}
}
],
"connections": {
"Execute Workflow Trigger": { "main": [[{ "node": "Config Mensageria", "type": "main", "index": 0 }]] },
"Config Mensageria": { "main": [[{ "node": "Montar corpo", "type": "main", "index": 0 }]] },
"Montar corpo": { "main": [[{ "node": "POST enviar template", "type": "main", "index": 0 }]] },
"POST enviar template": { "main": [[{ "node": "Classificar resultado", "type": "main", "index": 0 }]] }
}
}
Guarde o id retornado como <wf-enviar-template>.
- Step 2: Validar
n8n_validate_workflow com {"id": "<wf-enviar-template>"}. Esperado: sem erros.
Task 3: Sub-workflow "MCP Server - Mensageria ZPRO - enviar_template_com_parametros"
Interfaces:
-
Produces: workflow
MCP Server - Mensageria ZPRO - enviar_template_com_parametros, recebenumber,templateName,templateLanguageCode,parametrosBody(array de strings, obrigatórios),isClosed(opcional), retorna o corpo cru dePOST /v2/api/external/{apiId}/templateBodyou erro classificado. -
Step 1: Criar o workflow
{
"name": "MCP Server - Mensageria ZPRO - enviar_template_com_parametros",
"nodes": [
{
"id": "trigger",
"name": "Execute Workflow Trigger",
"type": "n8n-nodes-base.executeWorkflowTrigger",
"typeVersion": 1.2,
"position": [0, 0],
"parameters": {
"inputSource": "workflowInputs",
"workflowInputs": {
"values": [
{ "name": "number", "type": "string" },
{ "name": "templateName", "type": "string" },
{ "name": "templateLanguageCode", "type": "string" },
{ "name": "parametrosBody", "type": "array" },
{ "name": "isClosed", "type": "boolean" }
]
}
}
},
{
"id": "config",
"name": "Config Mensageria",
"type": "n8n-nodes-base.set",
"typeVersion": 3.5,
"position": [260, 0],
"parameters": {
"mode": "manual",
"includeOtherFields": true,
"assignments": {
"assignments": [
{ "id": "cfg-baseurl", "name": "baseUrl", "type": "string", "value": "https://apigchat.gtigrupo.com.br" },
{ "id": "cfg-apiid", "name": "apiId", "type": "string", "value": "SUBSTITUIR_PELO_APIID_DE_MENSAGERIA" }
]
}
}
},
{
"id": "montar-corpo",
"name": "Montar corpo",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [520, 0],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const input = $json;\nconst params = Array.isArray(input.parametrosBody) ? input.parametrosBody : [];\nconst requestBody = {\n number: input.number,\n templateData: {\n messaging_product: \"whatsapp\",\n to: input.number,\n type: \"template\",\n template: {\n name: input.templateName,\n language: { code: input.templateLanguageCode },\n components: [\n {\n type: \"body\",\n parameters: params.map((valor) => ({ type: \"text\", text: String(valor) })),\n },\n ],\n },\n },\n};\nif (input.isClosed !== undefined && input.isClosed !== null && input.isClosed !== \"\") {\n requestBody.isClosed = input.isClosed;\n}\n\nreturn {\n json: {\n baseUrl: input.baseUrl,\n apiId: input.apiId,\n requestBody,\n },\n};"
}
},
{
"id": "post-enviar-template-body",
"name": "POST enviar template com parâmetros",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [780, 0],
"parameters": {
"method": "POST",
"url": "={{ $json.baseUrl }}/v2/api/external/{{ $json.apiId }}/templateBody",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"contentType": "json",
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify($json.requestBody) }}",
"options": {
"response": { "response": { "fullResponse": true, "neverError": true, "responseFormat": "autodetect" } },
"timeout": 15000
}
},
"credentials": {
"httpHeaderAuth": { "id": "8XHGuA4cg9z1jUq8", "name": "ZPRO API Token - Canal QR - MC AGENDAMENTO" }
}
},
{
"id": "classificar",
"name": "Classificar resultado",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [1040, 0],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const status = $json.statusCode;\nconst rawBody = $json.body;\n\nfunction classifyError(status, context) {\n if (status === 401 || status === 403) {\n return { code: \"AUTHENTICATION_ERROR\", message: `Falha de autenticação na API de mensageria do Z-PRO ao ${context} (status ${status}).` };\n }\n if (status === 404) {\n return { code: \"NOT_FOUND\", message: `Recurso não encontrado na API de mensageria do Z-PRO ao ${context} (status ${status}). Verifique o ApiID.` };\n }\n if (status === 400 || status === 422) {\n return { code: \"VALIDATION_ERROR\", message: `A API de mensageria do Z-PRO rejeitou os dados enviados ao ${context} (status ${status}).` };\n }\n return { code: \"ZPRO_MENSAGERIA_API_ERROR\", message: `A API de mensageria do Z-PRO retornou um erro inesperado ao ${context} (status ${status}).` };\n}\n\nif (status < 200 || status >= 300) {\n return { json: { success: false, error: classifyError(status, \"enviar template com parâmetros\") } };\n}\n\nreturn { json: { success: true, data: rawBody } };"
}
}
],
"connections": {
"Execute Workflow Trigger": { "main": [[{ "node": "Config Mensageria", "type": "main", "index": 0 }]] },
"Config Mensageria": { "main": [[{ "node": "Montar corpo", "type": "main", "index": 0 }]] },
"Montar corpo": { "main": [[{ "node": "POST enviar template com parâmetros", "type": "main", "index": 0 }]] },
"POST enviar template com parâmetros": { "main": [[{ "node": "Classificar resultado", "type": "main", "index": 0 }]] }
}
}
Guarde o id retornado como <wf-enviar-template-parametros>.
- Step 2: Validar
n8n_validate_workflow com {"id": "<wf-enviar-template-parametros>"}. Esperado: sem erros.
Task 4: Sub-workflow "MCP Server - Mensageria ZPRO - enviar_template_marketing"
Interfaces:
-
Produces: workflow
MCP Server - Mensageria ZPRO - enviar_template_marketing, recebenumber,templateName,templateLanguageCode,parametrosBody(obrigatórios),headerImageUrl(opcional),isClosed(opcional), retorna o corpo cru dePOST /v2/api/external/{apiId}/templateMarketingBodyou erro classificado. -
Step 1: Criar o workflow
{
"name": "MCP Server - Mensageria ZPRO - enviar_template_marketing",
"nodes": [
{
"id": "trigger",
"name": "Execute Workflow Trigger",
"type": "n8n-nodes-base.executeWorkflowTrigger",
"typeVersion": 1.2,
"position": [0, 0],
"parameters": {
"inputSource": "workflowInputs",
"workflowInputs": {
"values": [
{ "name": "number", "type": "string" },
{ "name": "templateName", "type": "string" },
{ "name": "templateLanguageCode", "type": "string" },
{ "name": "parametrosBody", "type": "array" },
{ "name": "headerImageUrl", "type": "string" },
{ "name": "isClosed", "type": "boolean" }
]
}
}
},
{
"id": "config",
"name": "Config Mensageria",
"type": "n8n-nodes-base.set",
"typeVersion": 3.5,
"position": [260, 0],
"parameters": {
"mode": "manual",
"includeOtherFields": true,
"assignments": {
"assignments": [
{ "id": "cfg-baseurl", "name": "baseUrl", "type": "string", "value": "https://apigchat.gtigrupo.com.br" },
{ "id": "cfg-apiid", "name": "apiId", "type": "string", "value": "SUBSTITUIR_PELO_APIID_DE_MENSAGERIA" }
]
}
}
},
{
"id": "montar-corpo",
"name": "Montar corpo",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [520, 0],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const input = $json;\nconst params = Array.isArray(input.parametrosBody) ? input.parametrosBody : [];\nconst components = [];\nif (input.headerImageUrl) {\n components.push({ type: \"header\", parameters: [{ type: \"image\", image: { link: input.headerImageUrl } }] });\n}\ncomponents.push({ type: \"body\", parameters: params.map((valor) => ({ type: \"text\", text: String(valor) })) });\n\nconst requestBody = {\n number: input.number,\n templateData: {\n messaging_product: \"whatsapp\",\n to: input.number,\n type: \"template\",\n template: {\n name: input.templateName,\n language: { code: input.templateLanguageCode },\n components,\n },\n },\n};\nif (input.isClosed !== undefined && input.isClosed !== null && input.isClosed !== \"\") {\n requestBody.isClosed = input.isClosed;\n}\n\nreturn {\n json: {\n baseUrl: input.baseUrl,\n apiId: input.apiId,\n requestBody,\n },\n};"
}
},
{
"id": "post-enviar-template-marketing",
"name": "POST enviar template marketing",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [780, 0],
"parameters": {
"method": "POST",
"url": "={{ $json.baseUrl }}/v2/api/external/{{ $json.apiId }}/templateMarketingBody",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"contentType": "json",
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify($json.requestBody) }}",
"options": {
"response": { "response": { "fullResponse": true, "neverError": true, "responseFormat": "autodetect" } },
"timeout": 15000
}
},
"credentials": {
"httpHeaderAuth": { "id": "8XHGuA4cg9z1jUq8", "name": "ZPRO API Token - Canal QR - MC AGENDAMENTO" }
}
},
{
"id": "classificar",
"name": "Classificar resultado",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [1040, 0],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const status = $json.statusCode;\nconst rawBody = $json.body;\n\nfunction classifyError(status, context) {\n if (status === 401 || status === 403) {\n return { code: \"AUTHENTICATION_ERROR\", message: `Falha de autenticação na API de mensageria do Z-PRO ao ${context} (status ${status}).` };\n }\n if (status === 404) {\n return { code: \"NOT_FOUND\", message: `Recurso não encontrado na API de mensageria do Z-PRO ao ${context} (status ${status}). Verifique o ApiID.` };\n }\n if (status === 400 || status === 422) {\n return { code: \"VALIDATION_ERROR\", message: `A API de mensageria do Z-PRO rejeitou os dados enviados ao ${context} (status ${status}).` };\n }\n return { code: \"ZPRO_MENSAGERIA_API_ERROR\", message: `A API de mensageria do Z-PRO retornou um erro inesperado ao ${context} (status ${status}).` };\n}\n\nif (status < 200 || status >= 300) {\n return { json: { success: false, error: classifyError(status, \"enviar template de marketing\") } };\n}\n\nreturn { json: { success: true, data: rawBody } };"
}
}
],
"connections": {
"Execute Workflow Trigger": { "main": [[{ "node": "Config Mensageria", "type": "main", "index": 0 }]] },
"Config Mensageria": { "main": [[{ "node": "Montar corpo", "type": "main", "index": 0 }]] },
"Montar corpo": { "main": [[{ "node": "POST enviar template marketing", "type": "main", "index": 0 }]] },
"POST enviar template marketing": { "main": [[{ "node": "Classificar resultado", "type": "main", "index": 0 }]] }
}
}
Guarde o id retornado como <wf-enviar-template-marketing>.
- Step 2: Validar
n8n_validate_workflow com {"id": "<wf-enviar-template-marketing>"}. Esperado: sem erros.
Task 5: Workflow "MCP Server - Mensageria ZPRO" (o servidor)
Interfaces:
-
Consumes:
<wf-enviar-texto>(Task 1),<wf-enviar-template>(Task 2),<wf-enviar-template-parametros>(Task 3),<wf-enviar-template-marketing>(Task 4). -
Produces: endpoint MCP em
https://devn8n.gtigrupo.com.br/mcp/mcp-server-mensageria-zpro. -
Step 1: Criar o workflow com o trigger e os quatro nodes de tool
{
"name": "MCP Server - Mensageria ZPRO",
"nodes": [
{
"id": "mcp-trigger",
"name": "MCP Server Trigger",
"type": "@n8n/n8n-nodes-langchain.mcpTrigger",
"typeVersion": 2,
"position": [0, 0],
"parameters": { "path": "mcp-server-mensageria-zpro", "authentication": "none" }
},
{
"id": "tool-enviar-texto",
"name": "enviar_texto",
"type": "@n8n/n8n-nodes-langchain.toolWorkflow",
"typeVersion": 2.2,
"position": [260, -160],
"parameters": {
"description": "Envia uma mensagem de texto simples via WhatsApp pro número informado. Funciona tanto em canal não oficial quanto dentro da janela de 24h de conversa ativa no WABA oficial.",
"source": "database",
"workflowId": { "value": "<wf-enviar-texto>", "mode": "id" }
}
},
{
"id": "tool-enviar-template",
"name": "enviar_template",
"type": "@n8n/n8n-nodes-langchain.toolWorkflow",
"typeVersion": 2.2,
"position": [260, -80],
"parameters": {
"description": "Envia um template WABA aprovado, sem parâmetros dinâmicos (ex.: template genérico de saudação). Use fora da janela de 24h de conversa, quando o canal for WABA oficial.",
"source": "database",
"workflowId": { "value": "<wf-enviar-template>", "mode": "id" }
}
},
{
"id": "tool-enviar-template-parametros",
"name": "enviar_template_com_parametros",
"type": "@n8n/n8n-nodes-langchain.toolWorkflow",
"typeVersion": 2.2,
"position": [260, 0],
"parameters": {
"description": "Envia um template WABA aprovado com parâmetros dinâmicos de texto no corpo (ex.: nome do paciente, data da consulta). Use quando o template exigir preencher variáveis.",
"source": "database",
"workflowId": { "value": "<wf-enviar-template-parametros>", "mode": "id" }
}
},
{
"id": "tool-enviar-template-marketing",
"name": "enviar_template_marketing",
"type": "@n8n/n8n-nodes-langchain.toolWorkflow",
"typeVersion": 2.2,
"position": [260, 80],
"parameters": {
"description": "Envia um template WABA de marketing, com componente de header (ex.: imagem) além do corpo com parâmetros.",
"source": "database",
"workflowId": { "value": "<wf-enviar-template-marketing>", "mode": "id" }
}
}
],
"connections": {
"enviar_texto": { "ai_tool": [[{ "node": "MCP Server Trigger", "type": "ai_tool", "index": 0 }]] },
"enviar_template": { "ai_tool": [[{ "node": "MCP Server Trigger", "type": "ai_tool", "index": 0 }]] },
"enviar_template_com_parametros": { "ai_tool": [[{ "node": "MCP Server Trigger", "type": "ai_tool", "index": 0 }]] },
"enviar_template_marketing": { "ai_tool": [[{ "node": "MCP Server Trigger", "type": "ai_tool", "index": 0 }]] }
}
}
Substitua os 4 <wf-...> pelos IDs reais das Tasks 1-4.
Guarde o id deste workflow como <wf-mcp-server-mensageria-zpro>.
- Step 2: Mapear os parâmetros de cada tool na UI (passo manual — resourceMapper não é confiável via API)
Abrir cada um dos 4 nodes de tool na UI do n8n. Em "Workflow Inputs",
mapear cada campo pra uma expressão $fromAI(...) com descrição clara.
Exemplo pra enviar_texto:
number: ={{ $fromAI('number', 'Número de WhatsApp do destinatário, com DDI e DDD, sem símbolos (ex.: 5511999999999)', 'string') }}
body: ={{ $fromAI('body', 'Texto da mensagem a enviar', 'string') }}
externalKey: ={{ $fromAI('externalKey', 'Chave única de controle para rastrear este envio (ex.: identificador do contexto que originou o envio)', 'string') }}
isClosed: ={{ $fromAI('isClosed', 'Se true, fecha o ticket de atendimento após o envio (opcional)', 'boolean') }}
Mesmo padrão pros demais 3 nodes, usando os nomes de campo já definidos
nas Tasks 2-4 (templateName, templateLanguageCode, parametrosBody,
headerImageUrl).
- Step 3: Validar (sem ativar)
n8n_validate_workflow com {"id": "<wf-mcp-server-mensageria-zpro>"}.
Esperado: sem erros. Não ativar — falta confirmar a pendência de
autenticação (Task 6) antes de qualquer uso real.
Task 6: Resolver a pendência de autenticação (ApiID/token de mensageria)
Interfaces:
-
Consumes: workflows das Tasks 1-5.
-
Step 1: Confirmar com o usuário o ApiID correto da integração de mensageria
Perguntar ao usuário (ou verificar na tela de integrações do Z-PRO) qual
é o ApiID associado à credencial ZPRO API Token - Canal QR - MC AGENDAMENTO — mesma disciplina já usada no agendamento (nunca assumir
que ApiID e token são da mesma integração sem confirmar). Se for uma
integração diferente, criar uma credencial nova e obter o ApiID correto,
em vez de reaproveitar por suposição.
- Step 2: Preencher o
apiIdreal nos 4 sub-workflows
n8n_update_partial_workflow em cada um dos 4 sub-workflows (Tasks 1-4),
patchNodeField no node "Config Mensageria", substituindo
"SUBSTITUIR_PELO_APIID_DE_MENSAGERIA" pelo valor real confirmado no
Step 1.
- Step 3: Teste real, só com número de teste — PEDIR CONFIRMAÇÃO EXPLÍCITA ANTES DE DISPARAR
Antes de rodar qualquer teste que envie uma mensagem de verdade, pedir
confirmação explícita ao usuário (número de destino, qual tool testar)
— mensagem de WhatsApp enviada não pode ser desfeita, diferente de um
agendamento fictício. Só depois de confirmado, testar via tools/call
raw MCP (mesmo processo usado nos outros MCP Servers — handshake
initialize + tools/call com curl) contra enviar_texto primeiro
(o mais simples), com um número de celular de teste controlado pela
equipe. Confirmar visualmente no celular de teste que a mensagem chegou.
Observação sobre execução
Mesmo padrão dos planos anteriores: tasks são chamadas de API ao n8n, não commits de código. Diferente da maioria dos planos anteriores, este tem uma ação com efeito colateral real e irreversível (enviar mensagem) — por isso a Task 6 exige confirmação explícita do usuário antes de qualquer disparo de teste, mesmo depois de toda a estrutura validada. Não pular essa confirmação mesmo que pareça óbvio que o teste vai funcionar.