# Workflow n8n de Agendamento (GChat/Z-PRO → gti-scheduling-mcp) Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Construir, no n8n (`https://devn8n.gtigrupo.com.br`), o primeiro workflow que recebe mensagens de clientes via webhook do Z-PRO/GChat, conduz a conversa com um AI Agent para capturar os dados do agendamento, e chama a tool `agendar` do `gti-scheduling-mcp` (via `ZProAdapter`) para realizar o agendamento de fato. **Architecture:** Webhook (Z-PRO) → node `Code` de normalização/guarda de `step` → `AI Agent` (OpenAI + memória de sessão por `protocol` + tool MCP `agendar`) → `Respond to Webhook` (texto puro). Construído incrementalmente: cada task adiciona uma camada e é validada isoladamente antes da próxima. **Tech Stack:** n8n (self-hosted, `devn8n.gtigrupo.com.br`), node `n8n-nodes-mcp` (community, transporte stdio), `gti-scheduling-mcp` (já implementado, sem alterações de código nesta etapa), OpenAI Chat Model. ## Global Constraints - Nenhuma alteração de código/arquitetura no `gti-scheduling-mcp` é necessária ou permitida nesta etapa — ele já expõe a tool `agendar` exatamente como o workflow precisa consumir. - O webhook precisa **sempre responder de imediato**, uma execução por mensagem — nunca segurar a resposta esperando mais mensagens (Z-PRO descarta mensagens se isso acontecer). - A resposta ao Z-PRO é **sempre texto puro**, nunca JSON. - Nenhuma credencial (token do Z-PRO, API key da OpenAI) é inserida por quem executa este plano de forma automática — todas vêm de Credentials do n8n, criadas manualmente pelo usuário na UI. - `sessionId` da memória do agente = `protocol` do payload do Z-PRO (nunca o telefone — o mesmo telefone pode ter vários atendimentos/protocolos). - Escopo: só `agendar()` via `ZProAdapter`. Nada de `consultar_horarios`, cancelamento, remarcação ou múltiplas clínicas nesta etapa (ver spec, seção "Fora de escopo"). Spec de referência: `docs/superpowers/specs/2026-08-10-n8n-agendamento-workflow-design.md` --- ## Pré-requisitos (ação manual do usuário — fora do escopo de execução automática) Estes itens **não podem ser feitos pelas tasks abaixo** (exigem acesso à infraestrutura do n8n/credenciais reais) e precisam estar prontos antes da Task 5: 1. **Instalar o node comunitário `n8n-nodes-mcp`** no n8n de vocês, seguindo `https://docs.n8n.io/integrations/community-nodes/installation/`. 2. **Definir a variável de ambiente `N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE=true`** no processo do n8n (ex.: no `docker-compose.yml` do serviço n8n) e reiniciar o n8n — sem isso, o node comunitário não pode ser usado como tool dentro do AI Agent. 3. **Criar uma Credential do tipo OpenAI** no n8n (Settings → Credentials → OpenAI API), com uma API key válida. 4. **Criar uma Credential do tipo "MCP Client (Command Line) API"** (fornecida pelo node `n8n-nodes-mcp` após instalado), com: - **Command**: `node` - **Arguments**: caminho absoluto para `dist/index.js` do `gti-scheduling-mcp` no servidor onde o n8n roda (ex.: `/opt/gti-scheduling-mcp/dist/index.js`) — depende de onde o projeto for implantado, que é uma decisão de infraestrutura fora do escopo deste plano. - **Environment Variables** (uma por linha, formato `NOME=valor`): ``` SCHEDULING_PROVIDER=zpro ZPRO_BASE_URL= ZPRO_API_TOKEN= ZPRO_API_ID= ``` Sem os 4 itens acima, a Task 5 (conectar a tool MCP ao Agent) não pode ser validada de ponta a ponta — as Tasks 1 a 4 não dependem deles. --- ### Task 1: Build do `gti-scheduling-mcp` e localização do `dist/index.js` **Files:** - Nenhum arquivo novo — apenas build do projeto existente. **Interfaces:** - Produces: caminho absoluto de `dist/index.js`, usado nas Tasks 5 e no pré-requisito 4 acima. - [ ] **Step 1: Rodar o build** ```bash cd "D:\PROJETOS\KLINGO-CLINICAL-SYSLIFE-GCHAT-AGENDAMENTO\gti-scheduling-mcp" npm run build ``` Esperado: `tsc -p tsconfig.json` sem erros, e a pasta `dist/` criada/atualizada. - [ ] **Step 2: Confirmar que `dist/index.js` existe e roda** ```bash node "D:\PROJETOS\KLINGO-CLINICAL-SYSLIFE-GCHAT-AGENDAMENTO\gti-scheduling-mcp\dist\index.js" --help 2>&1 | head -5 ``` Esperado: o processo inicia (mesmo que não reconheça `--help`, não deve dar erro de módulo não encontrado). Se o servidor esperar variáveis de ambiente obrigatórias e falhar por causa disso, isso confirma que o build está correto (o processo rodou, só não tinha config — comportamento esperado sem env vars). Anote o caminho absoluto de `dist/index.js` — será necessário nos pré-requisitos e na Task 5. --- ### Task 2: Esqueleto base do webhook (Webhook → Respond to Webhook) **Files:** Nenhum arquivo local — cria o workflow no n8n via `n8n_create_workflow`. **Interfaces:** - Produces: `workflowId` (retornado pela chamada) — usado em todas as tasks seguintes. - [ ] **Step 1: Criar o workflow com dois nodes ligados diretamente** Chame a tool `n8n_create_workflow` com: ```json { "name": "Agendamento - GChat - Z-PRO (clínica exemplo)", "nodes": [ { "id": "webhook-zpro", "name": "Webhook Z-PRO", "type": "n8n-nodes-base.webhook", "typeVersion": 2.1, "position": [0, 0], "parameters": { "httpMethod": "POST", "path": "agendamento-zpro-clinica-exemplo", "responseMode": "responseNode" } }, { "id": "respond-stub", "name": "Respond to Webhook", "type": "n8n-nodes-base.respondToWebhook", "typeVersion": 1.5, "position": [300, 0], "parameters": { "respondWith": "text", "responseBody": "teste ok" } } ], "connections": { "Webhook Z-PRO": { "main": [[{ "node": "Respond to Webhook", "type": "main", "index": 0 }]] } } } ``` Guarde o `id` do workflow retornado — chamado de `` no restante deste plano. - [ ] **Step 2: Validar o workflow** Chame `n8n_validate_workflow` com `{"id": ""}`. Esperado: sem erros (pode haver avisos sobre o workflow estar inativo — normal, só ativamos na Task 6). - [ ] **Step 3: Ativar temporariamente e testar a resposta em texto puro** Ative o workflow (`n8n_update_partial_workflow` com operação `{"type": "activateWorkflow"}`), pegue a URL de produção do webhook (visível no node Webhook Z-PRO / na resposta da criação), e chame: ```bash curl -i -X POST "https://devn8n.gtigrupo.com.br/webhook/agendamento-zpro-clinica-exemplo" \ -H "Content-Type: application/json" \ -d '{"step":"process","entrada":"oi","number":"5511999999999","contactName":"Teste","protocol":"protocolo-teste-1"}' ``` Esperado: HTTP 200, corpo **exatamente** `teste ok` (texto puro, sem aspas de JSON ao redor). - [ ] **Step 4: Desativar o workflow de novo** `n8n_update_partial_workflow` com `{"type": "deactivateWorkflow"}` — mantemos desativado até a Task 6, para não responder de verdade a mensagens reais enquanto ainda está incompleto. --- ### Task 3: Node de normalização + guarda de `step` **Files:** Nenhum arquivo local — edita o workflow `` via `n8n_update_partial_workflow`. **Interfaces:** - Consumes: `` da Task 2. - Produces: campos `sessionId`, `mensagem`, `nomeContato`, `telefone`, `provider`, `clinicId`, `earlyReply` — consumidos pela Task 4 (AI Agent) e pelo `If` desta task. - [ ] **Step 1: Adicionar o node `Code` de normalização** ```json { "type": "addNode", "node": { "id": "normalizar-entrada", "name": "Normalizar entrada", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [150, -120], "parameters": { "mode": "runOnceForEachItem", "language": "javaScript", "jsCode": "const body = $input.item.json.body || {};\n\n// Configuração fixa desta cópia do workflow. Ajustar por clínica ao\n// duplicar este workflow para uma nova instância.\nconst clinicConfig = {\n provider: \"zpro\",\n clinicId: \"clinica-exemplo\",\n};\n\nif (body.step !== \"process\") {\n return {\n json: {\n earlyReply: true,\n sessionId: body.protocol || \"\",\n mensagem: \"\",\n nomeContato: \"\",\n telefone: \"\",\n provider: clinicConfig.provider,\n clinicId: clinicConfig.clinicId,\n },\n };\n}\n\nreturn {\n json: {\n earlyReply: false,\n sessionId: body.protocol || \"\",\n mensagem: String(body.entrada || \"\").trim(),\n nomeContato: body.contactName || \"\",\n telefone: body.number || \"\",\n provider: clinicConfig.provider,\n clinicId: clinicConfig.clinicId,\n },\n};" } } } ``` - [ ] **Step 2: Adicionar o node `If` que decide entre resposta antecipada e fluxo normal** ```json { "type": "addNode", "node": { "id": "if-early-reply", "name": "É step=process?", "type": "n8n-nodes-base.if", "typeVersion": 2.3, "position": [400, -120], "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict" }, "combinator": "and", "conditions": [ { "id": "cond-early-reply", "leftValue": "={{ $json.earlyReply }}", "rightValue": true, "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ] } } } } ``` - [ ] **Step 3: Rewire — Webhook → Normalizar entrada → If** ```json [ { "type": "removeConnection", "source": "Webhook Z-PRO", "target": "Respond to Webhook" }, { "type": "addConnection", "source": "Webhook Z-PRO", "target": "Normalizar entrada" }, { "type": "addConnection", "source": "Normalizar entrada", "target": "É step=process?" } ] ``` - [ ] **Step 4: Ligar a saída TRUE do If (índice 0) direto ao Respond to Webhook existente, com texto vazio** ```json [ { "type": "patchNodeField", "nodeName": "Respond to Webhook", "fieldPath": "parameters.responseBody", "patches": [{ "find": "teste ok", "replace": "={{ $json.mensagem || '' }}" }] }, { "type": "addConnection", "source": "É step=process?", "sourceOutput": 0, "target": "Respond to Webhook" } ] ``` A saída FALSE (índice 1) do `If` fica sem conexão por enquanto — será ligada ao AI Agent na Task 4. - [ ] **Step 5: Validar** `n8n_validate_workflow` com `{"id": ""}`. Esperado: sem erros. Pode haver aviso de node com saída não conectada (`É step=process?` saída FALSE) — esperado nesta task, resolvido na Task 4. - [ ] **Step 6: Testar os dois caminhos** Ative o workflow, chame o webhook duas vezes: ```bash curl -s -X POST "https://devn8n.gtigrupo.com.br/webhook/agendamento-zpro-clinica-exemplo" \ -H "Content-Type: application/json" \ -d '{"step":"outro","entrada":"x","number":"1","contactName":"y","protocol":"p1"}' ``` Esperado: corpo vazio (early reply, `step` diferente de `process`). ```bash curl -s -X POST "https://devn8n.gtigrupo.com.br/webhook/agendamento-zpro-clinica-exemplo" \ -H "Content-Type: application/json" \ -d '{"step":"process","entrada":"Olá, quero marcar uma consulta","number":"5511999999999","contactName":"Fulano","protocol":"p2"}' ``` Esperado: corpo = `Olá, quero marcar uma consulta` (o `Respond to Webhook` está ecoando `mensagem` só para confirmar que os campos normalizados chegaram certos — será substituído pela resposta do Agent na Task 4). Desative o workflow ao final (`deactivateWorkflow`), como na Task 2. --- ### Task 4: AI Agent com memória de sessão (sem tool ainda) **Files:** Nenhum arquivo local — edita ``. **Interfaces:** - Consumes: `earlyReply` (falso), `sessionId`, `mensagem`, `nomeContato`, `telefone` da Task 3. - Produces: texto de resposta do Agent, consumido pelo `Respond to Webhook`. - [ ] **Step 1: Adicionar o node OpenAI Chat Model** ```json { "type": "addNode", "node": { "id": "openai-chat-model", "name": "OpenAI Chat Model", "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi", "typeVersion": 1, "position": [650, 100], "parameters": { "model": { "value": "gpt-4o-mini" }, "options": {} }, "credentials": { "openAiApi": { "id": "", "name": "OpenAI account" } } } } ``` Substitua `` pelo ID real da credencial (visível na UI do n8n, em Settings → Credentials, ou via `n8n_manage_credentials` se disponível). Se preferir, este campo pode ser deixado para o usuário selecionar manualmente na UI do n8n após a criação do node — não bloqueia os steps seguintes. - [ ] **Step 2: Adicionar o node Simple Memory** ```json { "type": "addNode", "node": { "id": "memoria-sessao", "name": "Memória da Sessão", "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow", "typeVersion": 1.4, "position": [650, 250], "parameters": { "sessionIdType": "customKey", "sessionKey": "={{ $('Normalizar entrada').item.json.sessionId }}", "contextWindowLength": 20 } } } ``` - [ ] **Step 3: Adicionar o node AI Agent** ```json { "type": "addNode", "node": { "id": "agente-agendamento", "name": "AI Agent", "type": "@n8n/n8n-nodes-langchain.agent", "typeVersion": 3.1, "position": [650, -120], "parameters": { "promptType": "define", "text": "={{ $json.mensagem }}", "options": { "systemMessage": "Você é o assistente de agendamento da clínica, conversando por WhatsApp. Regras:\n1. O nome de contato do WhatsApp é \"{{ $('Normalizar entrada').item.json.nomeContato }}\" e o telefone é \"{{ $('Normalizar entrada').item.json.telefone }}\" — mas SEMPRE confirme o nome completo do paciente com o cliente antes de agendar, mesmo que já tenha o nome de contato.\n2. Pergunte a data e o horário desejados para a consulta, e opcionalmente o motivo.\n3. Nunca invente informação que o cliente não deu.\n4. Responda sempre em português, em tom cordial e objetivo, adequado para WhatsApp (mensagens curtas).\n5. Quando tiver o nome completo confirmado e a data/hora desejada, você pode agendar de verdade.\n6. NUNCA agende mais de uma vez na mesma conversa — se o cliente já confirmou um agendamento nesta conversa, não agende de novo; informe que o agendamento já foi feito.\n7. Se o agendamento falhar, explique o problema ao cliente em linguagem simples e pergunte se ele quer tentar de novo com outros dados, ou avise que vai encaminhar para um atendente." } } } } ``` - [ ] **Step 4: Conectar Chat Model e Memory ao Agent, e a saída FALSE do If ao Agent** ```json [ { "type": "addConnection", "source": "OpenAI Chat Model", "sourceOutput": "ai_languageModel", "target": "AI Agent", "targetInput": "ai_languageModel" }, { "type": "addConnection", "source": "Memória da Sessão", "sourceOutput": "ai_memory", "target": "AI Agent", "targetInput": "ai_memory" }, { "type": "addConnection", "source": "É step=process?", "sourceOutput": 1, "target": "AI Agent" }, { "type": "addConnection", "source": "AI Agent", "target": "Respond to Webhook" } ] ``` - [ ] **Step 5: Ajustar o Respond to Webhook para usar a saída do Agent** ```json { "type": "patchNodeField", "nodeName": "Respond to Webhook", "fieldPath": "parameters.responseBody", "patches": [{ "find": "={{ $json.mensagem || '' }}", "replace": "={{ $json.output }}" }] } ``` (`$json.output` é o campo de texto padrão que o node AI Agent produz.) - [ ] **Step 6: Validar** `n8n_validate_workflow` com `{"id": ""}`. Esperado: sem erros. Se aparecer erro sobre a credencial OpenAI ausente/inválida, ajuste o `credentials.openAiApi.id` (Step 1) ou peça para o usuário selecionar a credencial manualmente na UI antes de prosseguir. - [ ] **Step 7: Testar a memória de sessão** Ative o workflow. Envie duas mensagens com o **mesmo** `protocol`: ```bash curl -s -X POST "https://devn8n.gtigrupo.com.br/webhook/agendamento-zpro-clinica-exemplo" \ -H "Content-Type: application/json" \ -d '{"step":"process","entrada":"Meu nome completo é João da Silva Teste","number":"5511999999999","contactName":"João","protocol":"p-memoria-1"}' ``` ```bash curl -s -X POST "https://devn8n.gtigrupo.com.br/webhook/agendamento-zpro-clinica-exemplo" \ -H "Content-Type: application/json" \ -d '{"step":"process","entrada":"Qual é o meu nome completo que eu te falei?","number":"5511999999999","contactName":"João","protocol":"p-memoria-1"}' ``` Esperado: a segunda resposta menciona "João da Silva Teste" — confirma que a memória por `sessionId=protocol` está funcionando entre execuções separadas do webhook. Desative o workflow ao final. --- ### Task 5: Conectar a tool MCP `agendar` ao Agent **Pré-requisito:** os 4 itens da seção "Pré-requisitos" no topo deste plano precisam estar prontos (node comunitário instalado, env var setada e n8n reiniciado, credenciais OpenAI e MCP criadas). **Files:** Nenhum arquivo local — edita ``. **Interfaces:** - Consumes: credencial MCP stdio criada no pré-requisito 4. - Produces: capacidade do Agent de chamar `agendar` de verdade. - [ ] **Step 1: Adicionar o node MCP Client Tool** ```json { "type": "addNode", "node": { "id": "mcp-tool-agendar", "name": "MCP - gti-scheduling-mcp", "type": "n8n-nodes-mcp.mcpClientTool", "typeVersion": 1, "position": [900, 250], "parameters": { "connectionType": "cmd", "include": "selected", "includeTools": ["agendar"] }, "credentials": { "mcpClientCmdApi": { "id": "", "name": "gti-scheduling-mcp (stdio)" } } } } ``` Os nomes exatos do tipo de credencial (`mcpClientCmdApi`) e da propriedade `connectionType` podem variar conforme a versão instalada do pacote `n8n-nodes-mcp` — se a validação do Step 3 apontar um nome de campo diferente, ajuste conforme o que a própria UI do n8n mostrar ao adicionar o node manualmente (Settings do node → aba Credentials). - [ ] **Step 2: Conectar o node como tool do Agent** ```json { "type": "addConnection", "source": "MCP - gti-scheduling-mcp", "sourceOutput": "ai_tool", "target": "AI Agent", "targetInput": "ai_tool" } ``` - [ ] **Step 3: Validar** `n8n_validate_workflow` com `{"id": "", "options": {"profile": "ai-friendly"}}`. Corrija qualquer erro de nome de campo/credencial apontado (ver nota no Step 1). - [ ] **Step 4: Teste manual controlado, com dados fictícios, seguindo a mesma disciplina de segurança contra duplicidade do teste do `ZProAdapter`** Ative o workflow. Envie, com um `protocol` novo e único: ```bash curl -s -X POST "https://devn8n.gtigrupo.com.br/webhook/agendamento-zpro-clinica-exemplo" \ -H "Content-Type: application/json" \ -d '{"step":"process","entrada":"Oi, quero marcar uma consulta pra amanhã às 14h, meu nome completo é TESTE-N8N Paciente","number":"5511900000001","contactName":"Teste N8N","protocol":"p-e2e-1"}' ``` Continue a conversa (repetindo a chamada com o mesmo `protocol`, mudando só `entrada`) até o Agent confirmar o agendamento. **Antes de cada nova tentativa, se algo parecer ter falhado, confira visualmente no Z-PRO se o agendamento já foi criado** (mesma regra usada no teste manual do `ZProAdapter`) — não repita mensagens de confirmação "às cegas". Confirme visualmente no Z-PRO que o agendamento fictício foi criado. - [ ] **Step 5: Desativar o workflow ao final do teste** `n8n_update_partial_workflow` com `{"type": "deactivateWorkflow"}` — só reativar definitivamente depois que o usuário revisar o resultado (Task 6). --- ### Task 6: Revisão final e ativação **Files:** Nenhum arquivo local. - [ ] **Step 1: Validação final completa** `n8n_validate_workflow` com `{"id": "", "options": {"profile": "strict"}}`. Resolver qualquer erro/aviso restante. - [ ] **Step 2: Apresentar o resumo ao usuário** Antes de ativar definitivamente, mostrar ao usuário: URL do webhook de produção, nome do workflow, e pedir confirmação de que o Z-PRO já está configurado para apontar o fluxo de agendamento desta clínica para essa URL. - [ ] **Step 3: Ativar** Só ativar (`activateWorkflow`) depois da confirmação explícita do usuário no Step 2 — ativar aqui significa que mensagens reais de clientes passam a ser processadas. --- ## Observação sobre "commit" Nem `D:\PROJETOS\KLINGO-CLINICAL-SYSLIFE-GCHAT-AGENDAMENTO` nem `gti-scheduling-mcp` são repositórios git — não há passo de `git commit` neste plano. O estado do workflow vive inteiramente no n8n; cada task é validada via `n8n_validate_workflow` e teste funcional (chamada real ao webhook), no lugar de testes automatizados de código.