Files
KLINGO-CLINICAL-SYSLIFE-GCHAT/docs/n8n-agendamento-runbook.md
T

803 lines
47 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Runbook — Workflow de Agendamento no n8n (Z-PRO, Klingo, SysLife, ClinicAll)
> Registra problemas reais encontrados ao montar os workflows de
> agendamento no n8n (servidor `srvdevn8ngti` / `devn8n.gtigrupo.com.br`)
> e como foram resolvidos — para não repetir o mesmo processo de
> descoberta ao duplicar para uma nova clínica ou provider. As seções 1-13
> cobrem o histórico completo do **Z-PRO** (o único provider em produção
> real até agora, incluindo a migração do node comunitário pro MCP Server
> nativo do n8n). A seção 14 cobre os outros três providers — **Klingo**,
> **SysLife** e **ClinicAll** — todos construídos com o mesmo padrão de
> MCP Server nativo, mas ainda sem clínica real pra testar (ver cada
> subseção pras specs/planos completos e pendências específicas).
## 1. Node MCP: qual usar e como instalar
- O node **oficial** do n8n (`MCP Client Tool`, `@n8n/n8n-nodes-langchain`) só
conecta em MCP remoto via HTTP Streamable/SSE — **não** roda comando local
(stdio). Para rodar o `gti-scheduling-mcp` como subprocesso local, use o
pacote **comunitário** `n8n-nodes-mcp` (autor `nerding-io`).
- Instalação: **Settings → Community Nodes → Install a community node** →
`n8n-nodes-mcp`.
- Ao conectar como tool de um AI Agent, o **tipo do node no JSON do workflow**
precisa ser `n8n-nodes-mcp.mcpClientTool` (não `n8n-nodes-mcp.mcpClient`) —
é uma variante "Tool" que o n8n gera automaticamente para nodes marcados
como usáveis em AI Agents. Usar `mcpClient` na conexão `ai_tool` falha a
validação com "cannot output ai_tool connections".
- É necessário setar a variável de ambiente do **processo do n8n**
(não é configurável pela UI):
```
N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE=true
```
Nesse servidor, o n8n roda via **systemd** (`n8n.service`), com
`EnvironmentFile=/opt/n8n.env`. A variável foi adicionada nesse arquivo,
seguido de `systemctl restart n8n`. (Se o n8n rodasse em Docker, seria em
`environment:` no `docker-compose.yml`.)
## 2. Credencial MCP (STDIO) — dois bugs reais
Tipo de credencial: `mcpClientApi` ("MCP Client (STDIO) API"), campos:
`command`, `args`, `environments`.
- **Bug 1 — `command: "node"` sozinho falhou** com "Connection closed". O
serviço systemd tem um `PATH` mais restrito que um shell interativo. Fix:
usar o **caminho absoluto** (`which node` no servidor → `/usr/bin/node`).
- **Bug 2 — o campo "Environments" é um `<input>` de uma linha só (mascarado
como senha) na UI do n8n.** O código real do node
(`nodes/McpClient/McpClient.node.ts` no pacote `n8n-nodes-mcp`) espera o
formato `NOME=VALOR` **separado por quebra de linha real** (`split('\n')`),
mas navegadores removem quebras de linha ao digitar OU colar em campos de
uma linha — então múltiplas variáveis nunca conseguem ser inseridas
corretamente pela UI (tentamos: uma por linha, vírgula, espaço — todas
colapsam a mesma forma, e a primeira variável acaba recebendo o resto do
texto como valor, quebrando a validação).
**Fix**: criar/recriar a credencial via **API REST do n8n**, que aceita o
JSON com `\n` de verdade (não passa pelo campo quebrado da UI):
```bash
# 1. Gerar uma API key: Settings → n8n API → Create an API key
# 2. DELETE na credencial antiga (se existir), depois POST na nova:
curl -X POST "https://devn8n.gtigrupo.com.br/api/v1/credentials" \
-H "X-N8N-API-KEY: <chave>" \
-H "Content-Type: application/json" \
-d '{
"name": "Client (STDIO) account",
"type": "mcpClientApi",
"data": {
"command": "/usr/bin/node",
"args": "/opt/gti-scheduling-mcp/dist/index.js",
"environments": "SCHEDULING_PROVIDER=zpro\nZPRO_BASE_URL=https://...\nZPRO_API_TOKEN=...\nZPRO_API_ID=..."
}
}'
```
Sintoma quando esse bug está presente: o log do n8n
(`journalctl -u n8n -f`) mostra exatamente a mensagem de erro do
`gti-scheduling-mcp` (`Falha ao carregar a configuração: SCHEDULING_PROVIDER
deve ser "zpro" ou "klingo".`), seguida de `MCP error -32000: Connection
closed` — o processo sobe e cai na validação de config antes de completar o
handshake stdio.
## 3. Deploy do `gti-scheduling-mcp` no servidor do n8n
O `node dist/index.js` executado pela credencial precisa estar no **mesmo
host** onde o n8n roda (o node comunitário spawna o processo localmente).
Processo usado (sem repositório git configurado ainda neste projeto):
`tar` local (excluindo `node_modules`/`dist`) → `scp` pro servidor → `npm
install && npm run build` lá. Publicado em `/opt/gti-scheduling-mcp`.
## 4. Modelo de IA (OpenAI Chat Model node) — gotchas de configuração
Mesmo usando um provider "compatível com OpenAI" (ex.: Z.ai/GLM), o node
`OpenAI Chat Model` (`@n8n/n8n-nodes-langchain.lmChatOpenAi`, v1.3) tem
comportamento por padrão que quebra com endpoints não-OpenAI:
- **`responsesApiEnabled` é `true` por padrão** nessa versão — usa a
"Responses API" da OpenAI (`/responses`), um endpoint proprietário que
providers OpenAI-compatíveis geralmente não implementam (eles só têm
`/chat/completions`). Sintoma: `404 Not Found` /
`MODEL_NOT_FOUND`. **Fix**: setar `responsesApiEnabled: false`
explicitamente.
- **Campo `model`** é um `resourceLocator` — precisa de `{mode: "id", value:
"nome-do-modelo"}` explícito. Omitir `mode` pode fazer o n8n tentar listar
modelos via uma chamada de API (`GET /models`) que o provider alternativo
pode não suportar do mesmo jeito, gerando erro parecido com o anterior.
- **Base URL da credencial**: o campo correto na credencial `openAiApi` é
`url` (aparece como "Base URL" na UI, geralmente atrás de "Add Option").
## 5. Expressões em sub-nodes de IA (Memory/Tools) dentro do AI Agent
Dentro de nodes conectados via `ai_memory`/`ai_tool`/`ai_languageModel`
(sub-nodes do AI Agent), referenciar um node upstream explicitamente
(`={{ $('Normalizar entrada').item.json.sessionId }}`) falhou com "Key
parameter is empty" — mesmo com o dado presente no item que chegou no AI
Agent. **Fix**: usar `={{ $json.sessionId }}` (referência ao item atual, sem
nome de node) — é o padrão que o próprio n8n usa por padrão nesse campo, e
funciona porque sub-nodes de IA herdam o item do node pai (AI Agent) na
execução.
## 6. Contrato do webhook do Z-PRO/GChat (verificado, não documentado
oficialmente em lugar nenhum)
- Uma execução do workflow **por mensagem** do cliente — o Z-PRO não
enfileira mensagens seguintes se o workflow não responder imediatamente.
- Corpo recebido: `{step, entrada, number, contactName, protocol}` — `step`
deve ser `"process"`; `protocol` é o identificador do **atendimento**
(não do telefone).
- Resposta esperada: **texto puro**, nunca JSON — node `Respond to Webhook`
em modo `text`.
## 7. Z.ai/GLM como modelo temporário — limitação real de tool-calling
Usado como substituto temporário enquanto a chave da OpenAI não chegava.
Dois endpoints testados:
- **`https://api.z.ai/api/coding/paas/v4`** ("GLM Coding Plan"): funciona
bem para chat simples, mas é **restrito a ferramentas oficialmente
suportadas** (Claude Code, Goose, etc. — ver
`https://docs.z.ai/devpack/faq`). Ao anexar uma tool MCP genérica ao AI
Agent, o modelo passou a devolver respostas **vazias silenciosamente**
(sem erro) em qualquer mensagem, mesmo sem chamar a tool. Confirmado
desabilitando a tool: a conversa voltou a funcionar normalmente.
- **`https://api.z.ai/api/paas/v4/`** (endpoint geral, mesma conta): suporta
tool-calling arbitrário segundo a documentação, mas apresentou timeout
consistente nesta conta/plano (possivelmente saldo separado do "Coding
Plan").
- **Conclusão**: Z.ai/GLM não é uma substituição direta e confiável da
OpenAI para um AI Agent com tools neste projeto. Recomendado usar a chave
real da OpenAI (ou outro provider com suporte robusto e testado de
function-calling) assim que disponível — só trocar a credencial e o
`model`/`responsesApiEnabled` de volta, sem tocar no resto do workflow.
## 8. Tratamento de erro adicionado
`Normalizar entrada` e `AI Agent` têm `onError: "continueErrorOutput"`,
ambos roteados para um node `Set` ("Erro - Resposta padrão") com uma
mensagem fixa amigável, que alimenta o `Respond to Webhook`. Isso garante
que o Z-PRO **sempre** recebe alguma resposta, mesmo se a normalização
lançar uma exceção ou o AI Agent falhar (timeout, erro de modelo, etc.) —
testado com um timeout real do endpoint geral do Z.ai, confirmado
funcionando.
## 9. Bug crítico — `toolParameters` do node MCP nunca era preenchido pelo modelo
**Sintoma**: toda chamada de `agendar` falhava com
`MCP error -32602: Input validation error: Invalid arguments for tool
agendar: Required at clinicId, Required at startAt, Required at endAt,
Required at patient.` — mesmo com o prompt do sistema explicando o formato
esperado em detalhe.
**Causa raiz**: o node `n8n-nodes-mcp` (operação `executeTool`) tem um
campo próprio **`toolParameters`** (JSON, obrigatório, valor padrão
`'{}'`) que carrega os argumentos reais enviados à tool. Ele **não é
preenchido automaticamente** só por a tool estar conectada a um AI Agent —
é preciso configurá-lo explicitamente com o helper `$fromAI()` do n8n, que
é o mecanismo real usado para "deixar o modelo decidir este valor em cada
chamada". Sem isso, o campo fica travado no `{}` padrão, e nenhum valor
que o modelo "pensa" que está enviando chega de fato ao MCP.
**Fix**: configurar `toolParameters` assim (mistura valores fixos vindos
do próprio workflow com `$fromAI()` só para o que o modelo precisa mesmo
decidir):
```
={{ JSON.stringify({
clinicId: $('Normalizar entrada').item.json.clinicId,
startAt: $fromAI('startAt', 'Data e hora de início, ISO 8601 completo', 'string'),
endAt: $fromAI('endAt', 'Data e hora de término, ISO 8601 completo', 'string'),
patient: {
name: $fromAI('patientName', 'Nome completo confirmado do paciente', 'string'),
phone: $('Normalizar entrada').item.json.telefone
}
}) }}
```
## 10. Bug crítico — `systemMessage` do AI Agent nunca era avaliado como expressão
**Sintoma**: um agendamento de teste foi criado com o campo de telefone
contendo literalmente o texto `{{ $('Normalizar entrada').item.json.telefone
}}` em vez de um número de telefone real.
**Causa raiz**: o campo `parameters.options.systemMessage` do node AI Agent
só é avaliado como expressão do n8n se o **valor inteiro do campo** começar
com `=`. Sem esse prefixo, tudo — inclusive trechos `{{ ... }}` escritos
"como exemplo" dentro do texto — é enviado ao modelo como **texto literal**,
e o modelo, sem saber que aquilo deveria virar um valor real, às vezes
copia o placeholder ao pé da letra.
**Fix**: sempre prefixar o campo inteiro com `=` quando ele precisar
referenciar dados reais do workflow (`={{ $('Normalizar entrada')... }}`).
**Consequência que isso causou (transparência total)**: dois agendamentos
de teste fictícios ficaram duplicados no Z-PRO (`Consulta - TESTE-N8N
Paciente Final`, mesma data/hora) antes desse bug ser identificado — uma
chamada que o agente reportou como "erro" na verdade teve sucesso
silenciosamente no lado do Z-PRO. Os testes fictícios foram apagados
manualmente depois. Isso reforça por que a disciplina de "uma execução por
vez, conferir visualmente antes de tentar de novo" existe em todos os
testes reais deste projeto — mesmo seguindo ela, um bug de configuração
ainda pode gerar um resultado inesperado, e só a conferência visual pega
isso.
## 11. Bug — modelo não sabe a data de hoje
**Sintoma**: pedido de agendamento para "amanhã" resultou em uma data no
passado (2023) sendo enviada ao Z-PRO.
**Causa raiz**: modelos de linguagem não têm acesso à data/hora real do
momento da conversa — sem essa informação no prompt, o modelo "chuta" uma
data (geralmente relacionada à data de corte do treinamento).
**Fix**: incluir a data/hora atual no `systemMessage` via expressão do
próprio n8n:
```
A data de hoje é {{ $now.setZone('America/Sao_Paulo').toFormat('yyyy-MM-dd (cccc)') }}
```
## 12. Bug — horário criado 3h adiantado/atrasado (fuso horário)
**Sintoma**: cliente pediu "sexta às 10h", o agendamento foi criado em produção
às 7h — exatamente 3h a menos (o offset de America/Sao_Paulo, UTC-3).
**Causa raiz**: o exemplo de formato dado ao modelo usava sufixo `Z` (UTC)
— ex. `2026-04-20T18:00:00.000Z`. O modelo tratou o horário local que o
cliente pediu (10h) como se já fosse UTC, sem converter, e o Z-PRO (que
aparentemente armazena/exibe em UTC) mostrou 10h UTC como 7h local.
**Fix**: instruir o modelo a **nunca usar `Z`/UTC** e sempre escrever o
mesmo número de hora que o cliente pediu, com offset `-03:00` explícito
(ex.: cliente pediu 10h → `2026-08-14T10:00:00-03:00`). Isso evita
qualquer aritmética de fuso horário pelo modelo — ele só copia o número
que o cliente já disse, sem calcular nada. Ajustado tanto na descrição do
`$fromAI('startAt', ...)`/`$fromAI('endAt', ...)` quanto numa regra
explícita no `systemMessage`.
**Se isso acontecer nas próximas clínicas**: o offset `-03:00` só está
certo para fuso de Brasília. Se uma clínica futura estiver em outro fuso,
ajustar o offset (e o `$now.setZone(...)` do prompt) de acordo.
## 13. Migração — MCP Server nativo no n8n (substitui o node comunitário, só Z-PRO)
**Data:** 11/08/2026. Ver design completo em
`docs/superpowers/specs/2026-08-10-mcp-server-nativo-n8n-zpro-design.md` e
plano em `docs/superpowers/plans/2026-08-10-mcp-server-nativo-n8n-zpro.md`.
**Motivação**: o node comunitário (`n8n-nodes-mcp`, seções 1–2 acima) exigiu
depuração pesada (bug de credencial STDIO, variável de ambiente do processo,
`toolParameters`/`$fromAI` não documentado) e não é editável visualmente —
qualquer manutenção de memória/prompt/tool exige mexer em processo externo.
A alternativa: reconstruir o mesmo "MCP Server" **inteiramente dentro do
n8n**, com nodes **oficiais** (`MCP Server Trigger` + `Call n8n Sub-Workflow
Tool` do lado servidor, `MCP Client Tool` oficial via HTTP Streamable do
lado cliente) — sem processo Node.js externo, sem pacote comunitário.
**Arquitetura nova** (3 workflows adicionais, o workflow de agendamento
existente só trocou o node MCP):
- `MCP Server - ZPRO` (`qjSaKllvneBIZ7mC`) — `MCP Server Trigger`
(`path: mcp-server-zpro-clinica-exemplo`) + 2 `Call n8n Sub-Workflow Tool`
(`agendar`, `consultar_horarios`).
- `MCP Server - ZPRO - agendar` (`5ZEOqTOk4Xex6jUQ`) — porta fiel da lógica
já testada em `gti-scheduling-mcp` (`zpro.mapper.ts`/`zpro.adapter.ts`/
`zpro.errors.ts`) para nodes `Set`/`Code`/`HTTP Request`: monta o corpo,
chama `POST /v2/api/external/{apiId}/appointment/create` com
`neverError: true` + `fullResponse: true` (captura manual de status/body,
nunca deixa o n8n abortar em 4xx/5xx), classifica o resultado (qualquer
2xx é sucesso operacional — mesma proteção contra duplicidade de sempre).
- `MCP Server - ZPRO - consultar_horarios` (`yfKkhSTDZaIYCPqP`) — sempre
retorna `NOT_SUPPORTED` (mesma decisão de produto de sempre, Z-PRO não
expõe esse endpoint).
- No workflow de agendamento (`yG2mi6ECBpPv4Hmn`): saiu o node comunitário
`MCP - gti-scheduling-mcp` (`n8n-nodes-mcp.mcpClientTool`, stdio), entrou
`MCP - ZPRO (nativo n8n)` (`@n8n/n8n-nodes-langchain.mcpClientTool`,
`serverTransport: httpStreamable`, apontando pra URL do `MCP Server
Trigger` acima). Nada mais no workflow mudou (prompt, memória, fallback de
erro).
**Gotcha 1 — todos os workflows envolvidos precisam estar `active: true`**,
inclusive os dois sub-workflows (`agendar`/`consultar_horarios`) chamados
via `Call n8n Sub-Workflow Tool` — não só o servidor MCP e o workflow de
agendamento. O erro sintoma é `"Workflow is not active and cannot be
executed."` no node `MCP Client Tool` do lado do AI Agent.
**Gotcha 2 — diagnóstico via chamada MCP crua (`curl`), sem depender do AI
Agent**: pra isolar se o problema está na cadeia de sub-workflows ou no
transporte/roteamento do AI Agent, dá pra chamar o endpoint MCP diretamente:
```bash
# 1. Handshake — captura o mcp-session-id no header da resposta
curl -s -i "https://devn8n.gtigrupo.com.br/mcp/mcp-server-zpro-clinica-exemplo" -X POST \
-H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"curl-test","version":"1.0"}}}'
# 2. tools/call — usa o mcp-session-id do passo 1
curl -s "https://devn8n.gtigrupo.com.br/mcp/mcp-server-zpro-clinica-exemplo" -X POST \
-H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \
-H "mcp-session-id: <session-id>" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"agendar","arguments":{"startAt":"...","endAt":"...","patientName":"...","patientPhone":"..."}}}'
```
Isso foi o que revelou que a cadeia de sub-workflows já funcionava (chegava
até o Z-PRO de verdade) enquanto o caminho via AI Agent ainda reportava
"not active" — depois de reativar os 3 workflows envolvidos e aguardar um
pouco, o caminho via AI Agent voltou a funcionar igual ao teste direto
(não ficou claro se foi só o tempo/cache do n8n resolvendo, ou a
reativação — mas o sintoma não voltou a aparecer depois disso).
**Gotcha 3 — causa raiz real do primeiro `403` em produção**: não foi
formato de header nem token expirado — foi **ApiID e token de integrações
diferentes**. A clínica tem mais de uma integração externa cadastrada no
Z-PRO (ex.: uma pro canal de WhatsApp "Canal QR", outra pro agendamento),
cada uma com seu próprio `ApiID` (segmento da URL,
`/v2/api/external/{ApiID}/...`) **e** seu próprio Bearer token — não são
intercambiáveis. O node `Config ZPRO` precisa ter o `apiId` da **mesma**
integração de onde veio o token salvo na credencial `httpHeaderAuth`. Os
sintomas por tipo de descasamento, na ordem em que apareceram:
| Corpo de erro do Z-PRO (403) | Causa |
| --- | --- |
| `{"error": "ERR_SESSION_NOT_AUTH_TOKEN"}` | token nunca usado/nunca válido pra nenhuma integração |
| `{"error": "Invalid token."}` | token de uma integração, `apiId` de outra (ou vice-versa) |
| (sucesso, `200`) | `apiId` e token da **mesma** integração |
Ao duplicar este padrão pra uma nova clínica: sempre confirmar, na tela de
integrações do Z-PRO daquela clínica, que o `ApiID` copiado pra URL e o
Bearer token copiado pra credencial vêm da **mesma linha/integração**.
**Teste real de validação (11/08/2026)**: um agendamento fictício foi
criado de ponta a ponta pelo caminho de produção completo (webhook →
Normalizar entrada → AI Agent → `MCP - ZPRO (nativo n8n)` → `MCP Server -
ZPRO` → sub-workflow `agendar` → Z-PRO), com `startAt` local correto
(`2026-08-12T11:00:00-03:00`, sem bug de timezone), retorno `200` do
Z-PRO, `id` de agendamento real mapeado, e resposta final coerente pro
cliente. Conferido visualmente no Z-PRO e apagado logo em seguida.
**Resultado**: o node comunitário `n8n-nodes-mcp` não é mais necessário
pro Z-PRO (segue instalado no servidor, mas sem uso nesse workflow). As
seções 1–2 deste runbook (instalação do node comunitário, bugs da
credencial STDIO) ficam como **referência histórica** — não se aplicam
mais a este workflow, mas continuam relevantes se algum dia o padrão nativo
não for suficiente e for necessário voltar ao processo externo
(`gti-scheduling-mcp`, monorepo TypeScript pausado).
## Status final — Z-PRO (11/08/2026)
Testado de ponta a ponta com sucesso, tanto com a chave real da OpenAI
(`gpt-4o-mini`) quanto com o MCP Server nativo do n8n (seção 13): telefone
correto, data correta, um único agendamento por teste, confirmado
visualmente no Z-PRO. Workflow **ativo em produção** (`yG2mi6ECBpPv4Hmn`),
junto com os 3 workflows do MCP Server nativo (`qjSaKllvneBIZ7mC`,
`5ZEOqTOk4Xex6jUQ`, `yfKkhSTDZaIYCPqP`) — todos precisam permanecer
`active: true`. Desde a seção 15, o `systemMessage` também depende de um
Google Doc externo (leitura pública, `docId` fixo em "Config Clínica") —
se o Doc for despublicado ou o `docId` mudar, o atendimento continua
funcionando (fallback seguro), só perde a personalização da clínica.
## 14. Providers adicionais via MCP Server nativo (Klingo, SysLife, ClinicAll)
Mesmo padrão arquitetural validado no Z-PRO (seção 13) — `MCP Server
Trigger` + `Call n8n Sub-Workflow Tool`, sem processo Node.js externo —
aplicado a mais três providers nesta sessão (11/08/2026). Nenhum dos três
tem clínica real disponível ainda: todos os workflows foram criados e
validados estruturalmente (`n8n_validate_workflow`, 0 erros), mas ficam
**inativos**, sem nenhuma chamada HTTP real feita, e sem workflow de
agente cliente (AI Agent + prompt) — isso fica para quando houver uma
clínica real e um canal de mensagens definido para cada um.
### 14.1 - Klingo
- Spec: `docs/superpowers/specs/2026-08-11-mcp-server-nativo-n8n-klingo-design.md`
- Plano: `docs/superpowers/plans/2026-08-11-mcp-server-nativo-n8n-klingo.md`
- Tools de apoio (spec/plano adicional): `docs/superpowers/specs/2026-08-11-klingo-tools-de-apoio-design.md` / `docs/superpowers/plans/2026-08-11-klingo-tools-de-apoio.md`
- Fonte: `gti-scheduling-mcp/docs/klingo-openapi.json` (Swagger público) — único provider dos 4 com adapter TypeScript prévio testado (`KlingoAdapter`), portado fielmente.
- **7 tools** no servidor `MCP Server - Klingo` (`3eiFEnfOTLnutP78`): `agendar` (`0PXwQZ3EXJUtuRgN`), `consultar_horarios` (`7ikOIk6mdoBwClC3`), `especialidade` (`VhDRpgVxC7Bq9ZMg`), `profissional` (`27HOTfmlTH0r5UiX`), `convenio` (`v8VGt7Du0zfbc2FT`), `unidade` (`Sms5egwOtmZkeIkw`), `preco` (`tSq0p8fRxS39CSWE`).
- **Diferença de autenticação em relação ao Z-PRO**: dois estágios — `X-APP-TOKEN` estático (credencial `Klingo APP Token - clínica pendente`, `lwmRgDYe7W1zrKP5`) para identificar o paciente e consultar catálogo, e um **Bearer de sessão dinâmico** (obtido na resposta de `/paciente/identificar`, nunca armazenado como credential) só para `/agenda/reservar`.
- **Limitação conhecida, sem solução**: não existe endpoint público de listagem de procedimentos no Klingo — `specialtyId`/`procedimento` (exigido por `agendar` e opcionalmente por `preco`) não tem nenhuma tool que resolva. `consultar_horarios` sempre retorna `SLOT_MAPPING_UNAVAILABLE` pelo mesmo motivo já registrado no `KlingoAdapter` original (formato de horário individual não documentado no Swagger público).
### 14.2 - SysLife
- Spec: `docs/superpowers/specs/2026-08-11-mcp-server-nativo-n8n-syslife-design.md`
- Plano: `docs/superpowers/plans/2026-08-11-mcp-server-nativo-n8n-syslife.md`
- Fonte: `Manual WS SysLife V3.4.pdf` (fornecido pelo usuário) — sem adapter TypeScript prévio, único provider cuja fonte de verdade é só o manual.
- **7 tools** no servidor `MCP Server - SysLife` (`TYu78UqIUr2yCBHK`): `especialidade`, `medico`, `convenio`, `paciente`, `procedimento`, `consultar_horarios` (GET `/Horario`), `agendar` (PUT `/Agenda`).
- **Autenticação**: Basic Auth fixo do WebService (usuário/senha documentados literalmente no manual, não por clínica) — credencial `SysLife Basic Auth - clínica pendente` (`u8XXJ6wyrKuGJp4Y`), reaproveitada nas 7 tools. Só o `baseUrl` (IP:porta) muda por clínica.
- **Ponto forte deste provider**: o `Horario` GET retorna `nuConsulta`, que corresponde ao `NumeroConsulta` esperado no `Agenda` PUT — ciclo consultar→agendar fecha de verdade, diferente do Klingo.
### 14.3 - ClinicAll
- Spec: `docs/superpowers/specs/2026-08-11-mcp-server-nativo-n8n-clinicall-design.md`
- Plano: `docs/superpowers/plans/2026-08-11-mcp-server-nativo-n8n-clinicall.md`
- Fonte: `docs/API Clinicall.pdf` ("Documentação da API de Integração com ClinicAll para Módulo de Agendamento", v1.1.16, Focus Tecnologia em Sistemas) — a documentação mais completa e explícita dos 4 providers (campos obrigatórios, exemplos reais de request/response).
- **8 tools** no servidor `MCP Server - ClinicAll` (`igaKjoHcPNYRAxPS`): `unidade`, `especialidade`, `convenio`, `profissional`, `procedimento`, `paciente`, `consultar_horarios` (GET `/disponibilidade/{idUnidade}/{idProfissional}`), `agendar` (PUT `/agendamento`).
- **Duas pendências reais, sem credencial nem verbo assumido por invenção**:
1. **Autenticação desconhecida** — o manual não documenta nenhum header/token nas chamadas de `TEntityController`/`TPatientController`/`TCalendarController`. Nenhuma credencial foi criada; todos os nodes HTTP usam `authentication: none`. Existe um `TAuthenticateController/login` separado, mas não está claro se é pré-requisito nem que token ele gera.
2. **Verbo HTTP do agendamento não confirmado** — `/agendamento` aceita GET/POST/PUT/DELETE sem o manual mapear qual verbo concilia um `idAgenda` livre já existente (o fluxo real é atualizar um registro pré-existente, não criar do zero). Assumido `PUT`; se não funcionar contra uma clínica real, tentar `POST` antes de qualquer outra hipótese.
- **Ponto forte deste provider**: `/disponibilidade` retorna `idAgenda` de cada horário livre, e esse mesmo `idAgenda` é usado para "conciliar" o agendamento via `/agendamento` — mesmo padrão de ciclo fechado do SysLife, com a vantagem de já vir com nome do profissional/especialidade/unidade no mesmo payload.
### 14.4 - Padrão comum aos três (e ao Z-PRO)
Reforçando o que já vale desde a seção 13, agora confirmado em mais três implementações:
- Cada provider = 1 workflow servidor (`MCP Server Trigger` + N `Call n8n Sub-Workflow Tool`) + N sub-workflows (1 por tool).
- Sub-workflows chamados via `Call n8n Sub-Workflow Tool` **precisam estar `active: true`** para funcionar no caminho real do AI Agent (gotcha 1 da seção 13) — nenhum dos três providers desta seção foi ativado ainda, então isso só será re-confirmado quando houver teste real.
- HTTP Request nodes sempre com `neverError: true` + `fullResponse: true` — captura manual de status/corpo, nunca deixa o n8n abortar em 4xx/5xx.
- Tools de escrita (`agendar`) sempre com a regra "qualquer HTTP 2xx é sucesso operacional" — proteção contra duplicidade, nunca reportar erro numa operação que a API já aceitou.
- Tools de leitura nunca reformatam o corpo de sucesso — devolvem cru, sem inventar um envelope que não foi confirmado por um exemplo real de resposta.
- Mapeamento de parâmetros `$fromAI(...)` nas tools do servidor é sempre um passo manual na UI (resourceMapper não é confiável via API do n8n) — feito pelo usuário, não pela automação.
- Nenhuma credencial é criada automaticamente pela automação — sempre pré-requisito manual do usuário na UI do n8n, mesmo quando o valor é só um placeholder de rascunho.
## 15. Configuração da IA via Google Docs — autonomia do gestor (só Z-PRO)
**Data:** 11/08/2026. Ver design completo em
`docs/superpowers/specs/2026-08-11-config-ia-google-docs-design.md` e
plano em `docs/superpowers/plans/2026-08-11-config-ia-google-docs.md`.
**Motivação**: o `systemMessage` do AI Agent era texto fixo hardcoded no
workflow. Com o número de clientes crescendo (1 workflow de agente por
clínica), editar manualmente o prompt de cada um toda vez que um gestor
quisesse ajustar tom de voz, saudação, horário de funcionamento ou uma
regra específica não escala. Solução: um Google Doc por clínica,
público só para leitura (sem exigir credencial no n8n), editável
diretamente pela conta do gestor — o `systemMessage` passa a concatenar
as regras fixas de segurança (inalteradas) com o conteúdo do Doc.
**Arquitetura implementada** (4 nodes novos no workflow
`yG2mi6ECBpPv4Hmn`):
```
"É step=process?" → Config Clínica → Buscar config da clínica ─┬→ Doc OK ──────┐
(docId fixo) (GET export?format=txt, └→ Doc falhou ─┴→ AI Agent
sem credencial)
```
- **Config Clínica** (`Set`): `docId` do Google Doc desta clínica,
fixo por cópia do workflow (mesmo padrão de `clinicId`/`baseUrl` dos
outros providers) — setado uma vez na instalação, nunca mais mexido
depois disso.
- **Buscar config da clínica** (`HTTP Request`): `GET
https://docs.google.com/document/d/{docId}/export?format=txt`, sem
autenticação (Doc público só para leitura), `onError:
continueErrorOutput`.
- **Doc OK** / **Doc falhou** (`Set`): reconstroem o item explicitamente
a partir de `$('Config Clínica').item.json` + `docContent` (do corpo
da resposta ou vazio, respectivamente).
- **`systemMessage` do AI Agent**: ganhou uma seção final
`"Instruções específicas desta clínica:\n{{ $json.docContent }}"`,
depois de todas as 7 regras fixas originais (que continuam idênticas,
sem alteração de texto).
**Bug real encontrado e corrigido durante a implementação — perda de
contexto depois de um `HTTP Request`**: o node `HTTP Request` **substitui
o `$json` inteiro** pelo corpo da resposta, descartando qualquer campo
que viesse do item anterior (`sessionId`, `mensagem`, `telefone`,
`nomeContato`, `clinicId`). Isso quebrou a "Memória da Sessão"
(`Key parameter is empty`, porque `sessionKey` depende de
`$json.sessionId`) logo na primeira tentativa. Mesma classe de bug já
registrada na seção 5 deste runbook (expressões em sub-nodes de IA), só
que desta vez a causa foi upstream do AI Agent, não dentro dele.
**Fix**: os nodes `Doc OK`/`Doc falhou` **não podem confiar em
`includeOtherFields`** quando há um `HTTP Request` no meio do caminho —
precisam reconstruir cada campo explicitamente referenciando o node
anterior ao HTTP Request por nome
(`={{ $('Config Clínica').item.json.sessionId }}`, etc.), exatamente
como já era feito em `Classificar resultado` nos sub-workflows de
`agendar` dos MCP Servers (seção 13/14) — a lição "depois de um HTTP
Request, resgate os campos anteriores pelo nome do node, nunca confie
em passthrough automático" vale pra qualquer node no meio do fluxo
principal, não só dentro de sub-workflows de tool.
**Teste real de validação (11/08/2026)**, com um Google Doc de teste
contendo a regra fictícia `"não atendemos às segundas-feiras"`:
1. Conteúdo do Doc apareceu naturalmente na resposta do agente
(pergunta genérica sobre dias de atendimento).
2. Regras fixas continuaram valendo (agente pediu confirmação do nome
completo antes de agendar).
3. Caminho de falha testado de propósito (`docId` inválido) — Google
Docs respondeu `404`, capturado pelo `onError: continueErrorOutput`,
roteado pro branch `Doc falhou`, atendimento **não quebrou**
(resposta normal, só sem a personalização da clínica).
4. `docId` revertido pro valor real de teste depois da validação.
**Resultado**: workflow ativo em produção com a configuração de negócio
já externalizada para o Google Doc de teste
(`1NmYRIoF7yD_B6N160_6Bpfv4NyJiryTTO2LFr9xNlYY`). Falta trocar pelo Doc
real de produção da clínica exemplo quando o gestor tiver o conteúdo
definitivo. Padrão pronto pra replicar em Klingo/SysLife/ClinicAll
quando esses tiverem workflow de agente.
## 16. Memória da sessão via Postgres (substitui o buffer em RAM, só Z-PRO)
**Data:** 11/08/2026. Ver design completo em
`docs/superpowers/specs/2026-08-11-memoria-sessao-postgres-design.md` e
plano em `docs/superpowers/plans/2026-08-11-memoria-sessao-postgres.md`.
**Motivação**: o node `Memória da Sessão` (`memoryBufferWindow`/"Simple
Memory") guardava o histórico da conversa só em RAM do processo do
n8n — não sobrevive a um reinício (deploy, crash, atualização) e não
funcionaria em setups com múltiplos workers (queue mode). Trocado pelo
node oficial `Postgres Chat Memory`, que persiste o histórico num banco.
**Infraestrutura**: o usuário provisionou um **container Postgres**, com
**um banco por cliente/clínica** dentro dele (mesma filosofia de
isolamento já usada pra `docId`, `baseUrl`/`apiId` dos MCP Servers).
Credencial no n8n: `Postgres Agendamento - (clínica exemplo)`
(`xq3QAN3MZSOCVgdY`), apontando pro banco do Z-PRO. Cada cliente futuro
terá sua própria credencial, apontando pro banco dele no mesmo
container.
**Troca implementada** (operação atômica — adicionar + conectar +
remover, mesma lição da seção 15 sobre nós desconectados): saiu
`Memória da Sessão` (`memoryBufferWindow`), entrou `Memória da Sessão
(Postgres)` (`@n8n/n8n-nodes-langchain.memoryPostgresChat`, v1.4), com
os mesmos parâmetros de sessão de sempre (`sessionKey: ={{
$json.sessionId }}`, `contextWindowLength: 20`). A tabela
`n8n_chat_histories` foi criada **automaticamente pelo node** na
primeira execução — sem setup manual de schema.
**Sem migração de histórico**: o histórico anterior só existia em RAM,
não havia nada a migrar — mesmo efeito que já acontecia a cada reinício
do n8n, só que agora deixa de acontecer daqui pra frente.
**Teste real de validação (11/08/2026)**: duas mensagens enviadas em
sequência ao webhook de produção, mesmo `protocol`
(`teste-memoria-postgres-01`) — primeira mensagem informando um nome
fictício, segunda perguntando "qual nome você anotou". O agente
respondeu corretamente com o nome da primeira mensagem, confirmando que
o contexto persistiu **entre duas execuções separadas do workflow**
(prova de que não é mais RAM do processo, já que cada chamada webhook é
uma execução isolada). Confirmação direta via `SELECT` na tabela
`n8n_chat_histories` não foi feita (sem necessidade de dar acesso ao
banco pra automação — a prova conversacional já foi considerada
suficiente).
**Resultado**: workflow ativo em produção com memória de sessão
persistida em Postgres. Padrão pronto pra replicar em
Klingo/SysLife/ClinicAll quando esses tiverem workflow de agente — cada
um com seu próprio banco dentro do mesmo container e sua própria
credencial.
## 17. MCP Server standalone de Mensageria (Z-PRO) — reusável por qualquer provider
**Data:** 11/08/2026. Ver design completo em
`docs/superpowers/specs/2026-08-11-mcp-server-mensageria-zpro-design.md`
e plano em
`docs/superpowers/plans/2026-08-11-mcp-server-mensageria-zpro.md`.
**Motivação**: a feature de "confirmação de agenda" (Klingo) precisa
disparar mensagens de WhatsApp de forma proativa — capacidade que ainda
não existia no projeto (tudo até então era resposta reativa a webhook).
Decisão explícita do usuário: em vez de acoplar o envio ao MCP de
agendamento de um provider específico, construir um **MCP Server
standalone só de mensageria**, já que diferentes clientes usam canais
diferentes (WABA oficial, não oficial, híbrido) — qualquer workflow
futuro (confirmação, campanha, o que for, de qualquer provider) pode
chamar essas tools sem depender de qual sistema de agendamento o
cliente usa.
**Escopo**: 4 tools, reaproveitando a API de mensageria do Z-PRO (mesmo
host `apigchat.gtigrupo.com.br`, endpoints diferentes dos de
agendamento):
| Tool | Endpoint | Uso |
| --- | --- | --- |
| `enviar_texto` | `POST /v2/api/external/{ApiID}/` | Texto simples — canal não oficial ou dentro da janela de 24h do WABA oficial |
| `enviar_template` | `POST .../template` | Template WABA aprovado, sem parâmetros dinâmicos |
| `enviar_template_com_parametros` | `POST .../templateBody` | Template WABA aprovado, com parâmetros dinâmicos no corpo |
| `enviar_template_marketing` | `POST .../templateMarketingBody` | Template WABA de marketing, com componente de header (ex.: imagem) |
**Arquitetura**: mesmo padrão de sempre — `MCP Server - Mensageria ZPRO`
(`WsL4RlSD0hZ34gpB`) + 4 sub-workflows (`enviar_texto`
`Y4ndG7GuIUrmVRxV`, `enviar_template` `E9lsmu8mlgzKFemk`,
`enviar_template_com_parametros` `8zj1V7yXOrdCAWVq`,
`enviar_template_marketing` `WYakJ2GmV6fNW2iq`). ApiID confirmado pelo
usuário como o mesmo já validado pro agendamento
(`ab2d51db-54e3-48c2-8b97-114a20340809`, credencial `ZPRO API Token -
Canal QR - MC AGENDAMENTO`).
**Bug real — `$fromAI` com `==` duplicado no início da expressão**: ao
mapear manualmente os campos de "Workflow Inputs" nos 4 nodes
`Call n8n Sub-Workflow Tool` do servidor, os valores foram salvos como
`"=={{ $fromAI(...) }}"` (dois `=` em vez de um) — a expressão nunca era
avaliada, então todo argumento chegava `null` no sub-workflow e a API do
Z-PRO rejeitava com 400 (`body must be a string type... null`).
**Fix**: reescrever os campos com um único `=` no início
(`"={{ $fromAI(...) }}"`), via `patchNodeField`/`updateNode`. Depois de
corrigir, apareceu um segundo erro (`Required → at isClosed`): o
`$fromAI` do campo opcional `isClosed` (boolean) não tinha valor
padrão, então o schema da tool o tratava como obrigatório. **Fix**:
adicionar o 4º argumento de default ao `$fromAI` (ex.:
`$fromAI('isClosed', '...', 'boolean', false)`), mesma correção
aplicada ao `headerImageUrl` opcional do template de marketing (default
`''`). Lição nova: **sempre conferir visualmente o texto final salvo no
campo depois de colar uma expressão `$fromAI` na UI** — um `=` a mais
não dá erro de validação estrutural do workflow, só falha silenciosamente
em runtime com campos `null`.
**Teste real de validação (11/08/2026)**: chamada `tools/call` direta
via MCP (raw, sem AI Agent) pro tool `enviar_texto`, mandando uma
mensagem de teste pro número pessoal do usuário
(`5575999091780`, autorização explícita dada em chat). Resultado:
`{"success": true, "data": {"message": "Message sent successfully",
"ticketId": 461590}}` — mensagem confirmada recebida no celular de
teste. Tools de template (`enviar_template`,
`enviar_template_com_parametros`, `enviar_template_marketing`) ainda
**não testadas de verdade** — precisam de um nome de template WABA
real aprovado, não disponível no momento.
**Fora de escopo desta rodada**: envio de mídia (multipart/URL/Base64),
busca de mensagem por `wamid`, e a própria feature de "confirmação de
agenda" do Klingo (que motivou a criação deste MCP) — fica pro usuário
montar depois, atribuindo estas tools no workflow que ele decidir.
**Resultado**: MCP Server de mensageria standalone, validado e testado
com envio real, pronto para ser consumido por qualquer workflow futuro
(confirmação de agenda ou outro), de qualquer provider.
## 18. Confirmação de Agenda via WhatsApp (Klingo Telefonia)
**Data:** 11/08/2026. Ver design completo em
`docs/superpowers/specs/2026-08-11-confirmacao-agenda-klingo-design.md`
e plano em
`docs/superpowers/plans/2026-08-11-confirmacao-agenda-klingo.md`.
**Motivação**: permitir que o paciente confirme presença numa consulta
agendada via WhatsApp, usando uma API **separada** da de agendamento do
Klingo — a **Klingo Telefonia**
(`https://api-externa.klingo.app/api/telefonia`, auth `X-APP-TOKEN`).
Dois fluxos: um lembrete proativo (cron diário) e uma confirmação
reativa (o paciente responde "sim"/"não" no WhatsApp). `POST /entrada`
(registro de chamada telefônica) ficou fora do escopo por instrução
explícita do usuário — "não envolve telefonia, apenas a confirmação no
nosso sistema de WhatsApp".
**MCP Server "Klingo Telefonia"** (`tSw2LGlOYVg9antR`) — 6 tools:
| Tool | Endpoint | Workflow id |
| --- | --- | --- |
| `listar_agendamentos_do_dia` | `GET /lista/{data}` | `hynQtoURFqpDUGTI` |
| `consultar_agendamento_por_id` | `GET /{id}` | `JXEaNHZSmiBWxDej` |
| `buscar_agendamento_pendente_por_cpf` | `GET /lista_ultimo_por_cpf` | `OJYHMUQlYv48Gw7h` |
| `confirmar_presenca` | `POST /confirmar` | `jdFH4T2fyd7YKNOa` |
| `registrar_nps` | `POST /nps` | `khUIxWFx7zF0tygP` |
| `consultar_bloqueios` | `GET /bloqueios` | `TfYHBjRkaL8gDmYR` |
Campos confirmados direto no spec OpenAPI real
(`api.swaggerhub.com/apis/agsx30/klingo-api-telefonia/1.0.0`) durante a
implementação — nada inventado. Pendências documentadas explicitamente:
- **Schema de `/lista_ultimo_por_cpf` e `/bloqueios` não documentado**
no spec — as respostas são tratadas como objeto genérico, sem assumir
campos específicos.
- **Obrigatoriedade de `callid` em `/confirmar` é ambígua**: o texto
descritivo do endpoint sugere obrigatoriedade, mas nenhum campo do
schema JSON está marcado `required`. Tratado como opcional — vazio
para confirmações originadas no WhatsApp (sem chamada telefônica
real).
- **Inconsistência de tipo do `id_marcacao`**: aparece como `string` no
schema `marcacao` (retornado por `/lista/{data}` e `/{id}`), mas como
`number` obrigatório no body de `/confirmar` e `/nps`. Os
sub-workflows convertem com `Number(...)` antes de enviar.
- Os endpoints GET desta API aceitam filtros no **corpo da
requisição** (incomum para GET) — implementado no node `HTTP Request`
do n8n com `method: "GET"` + `sendBody: true`.
**Workflow proativo "Lembrete de Confirmação - Klingo (exemplo)"**
(`IvLzmASwa9LGaMxf`): cron diário às 8h → calcula a data de amanhã →
chama `listar_agendamentos_do_dia` (mesmo sub-workflow usado pela tool
do MCP, via `Execute Workflow` direto — fluxo determinístico, sem IA)
→ loop pelos agendamentos → monta a mensagem de lembrete → chama
`enviar_texto` do **MCP Server - Mensageria ZPRO** (`Y4ndG7GuIUrmVRxV`,
seção 17), reaproveitando o envio já validado. Validado
estruturalmente (0 erros), mas deixado **inativo de propósito** — sem
clínica Klingo real, um cron ativo geraria só chamadas com erro contra
a API e tentativas de envio com dados vazios.
**Workflow de exemplo "Confirmação de Agenda - GChat - Klingo+Z-PRO
(exemplo)"** (`F5jEliFFE3K9cW14`): réplica estrutural do padrão da
clínica exemplo (webhook → `AI Agent` → memória Postgres), mas **sem
cliente real associado** — mesmo espírito dos MCP Servers de
agendamento Klingo/SysLife/ClinicAll construídos sem clínica real.
Contrato do webhook idêntico ao já documentado na seção 6 (`body.protocol`
→ `sessionId`, `body.entrada` → `mensagem`, `body.contactName` →
`nomeContato`, `body.number` → `telefone`). Modelo de chat usa a
credencial OpenAI real e compartilhada já em uso na clínica exemplo
(`29vMIDah5DTIhUYJ`, "GPT") — não é uma credencial inventada. A memória
Postgres fica com credencial placeholder (sem cliente real, sem banco
provisionado ainda). Tools conectadas via `MCP Client Tool` (nativo),
restritas a `buscar_agendamento_pendente_por_cpf` e
`confirmar_presenca` (as outras 4 tools do MCP existem mas não foram
atribuídas a este agente de exemplo). `systemMessage` fixo instruindo o
agente a interpretar confirmação/recusa, buscar o `id_marcacao`
pendente pelo CPF do paciente, e então confirmar (`status: "C"`) ou
recusar (`status: "N"`). Validado estruturalmente (0 erros), deixado
**inativo de propósito** pela mesma razão do workflow de lembrete.
**Resultado**: MCP Server de confirmação de agenda (Klingo Telefonia)
completo e validado estruturalmente, junto com os dois workflows de
exemplo (lembrete proativo + AI Agent reativo) prontos pra ativar assim
que houver uma clínica Klingo real com credencial `X-APP-TOKEN` e banco
Postgres provisionado. Sem teste de ponta a ponta contra a API real —
pendência explícita até então.
## 19. Confirmação de Agenda via WhatsApp (ClinicAll)
**Data:** 11/08/2026. Ver design completo em
`docs/superpowers/specs/2026-08-11-confirmacao-agenda-clinicall-design.md`
e plano em
`docs/superpowers/plans/2026-08-11-confirmacao-agenda-clinicall.md`.
**Motivação**: mesmo objetivo da seção 18 (Klingo), agora para o
ClinicAll — permitir que o paciente confirme presença numa consulta
via WhatsApp. Diferente do Klingo, o ClinicAll não tem uma API de
"Telefonia" separada: os endpoints de confirmação fazem parte do mesmo
`TCalendarController`/`TDomainController` já usado no MVP de
agendamento (seção 14.3), verificados diretamente no manual oficial
(`docs/API Clinicall.pdf`, v1.1.16, relido nesta sessão).
**MCP Server "ClinicAll - Confirmação de Agenda"** (`lHU5H0cZP4BJ6dWp`) — 5 tools:
| Tool | Endpoint | Workflow id |
| --- | --- | --- |
| `listar_agendamentos_do_dia` | `GET /TCalendarController/agendamento/<data>` | `9pudW1QzBMh7c4ve` |
| `buscar_agendamento_por_cpf` | `GET /TCalendarController/agendamentoSemPaciente` (cpf + dataNascimento) | `i7Dw7xJZPpSbV3Ak` |
| `confirmar_presenca` | `POST /TCalendarController/AgendamentoFlags` | `3mAkzfGwGeRSvWvO` |
| `consultar_estados_agenda` | `GET /TDomainController/estadoAgenda` | `EGX5whFSJX6ew50a` |
| `consultar_situacoes_agenda` | `GET /TDomainController/situacaoAgenda` | `YzFJ5evrw2bzJTGi` |
**Diferenças reais em relação ao Klingo** (mesma disciplina de não
generalizar padrões entre providers sem verificar):
- **Strings exatas em vez de código curto**: o Klingo aceita `status:
'C'/'S'/'N'` pra confirmar. O ClinicAll exige duas strings longas e
exatas (`estadoAgenda`, `situacaoAgenda` — ex.: `"CONFIRMADO PELA
TELEFONISTA"`, `"Agendado"`), documentadas só nas tabelas de domínio
— por isso `consultar_estados_agenda`/`consultar_situacoes_agenda`
são tools obrigatórias no fluxo, não apoio opcional.
- **CPF + data de nascimento, não só CPF**: `agendamentoSemPaciente`
exige os dois campos, diferente do `lista_ultimo_por_cpf` do Klingo.
- **Telefone aninhado**: no ClinicAll, o telefone do paciente vem em
`item.Paciente.telefoneCelular` (objeto aninhado), não um campo
direto do item como no Klingo (`item.celular`) — refletido na
extração de dados do workflow de lembrete.
- **Sem NPS nem consulta de bloqueios**: não documentados no manual do
ClinicAll para este módulo — ficaram fora, sem inventar.
- **Pendência não verificada**: o manual não esclarece se
`agendamentoSemPaciente` (`GET, POST`) espera os parâmetros como
query string ou body. Implementado como `GET` + query string, por
analogia com todos os outros GETs deste manual (nenhum usa body) —
suposição registrada, não testada contra a API real.
- **Mesma pendência de autenticação já registrada no MVP de
agendamento do ClinicAll**: `authentication: "none"` em todos os
nodes HTTP (não `httpHeaderAuth` como no Klingo) — o manual do
ClinicAll não documenta nenhum mecanismo de auth por requisição.
**Workflow proativo "Lembrete de Confirmação - ClinicAll (exemplo)"**
(`liBLpRUmbcnWk05V`): mesmo desenho do Klingo (cron 8h → lista amanhã →
loop → `enviar_texto` do MCP Mensageria ZPRO), com um filtro a mais —
descarta itens com `situacaoAgenda: "Livre"` (sem `idPaciente`/
`Paciente`) antes de montar a mensagem. Validado (0 erros), deixado
**inativo de propósito**.
**Workflow de exemplo "Confirmação de Agenda - GChat - ClinicAll+Z-PRO
(exemplo)"** (`ylc3Jcz239ra6XZK`): mesmo padrão do Klingo (webhook →
AI Agent → memória Postgres), `systemMessage` ajustado pra pedir CPF
**e** data de nascimento, e pra sempre consultar
`consultar_estados_agenda`/`consultar_situacoes_agenda` antes de
chamar `confirmar_presenca` (nunca inventar a string exata). Tools
conectadas via `MCP Client Tool`: `buscar_agendamento_por_cpf`,
`confirmar_presenca`, `consultar_estados_agenda`,
`consultar_situacoes_agenda` (`listar_agendamentos_do_dia` fica de
fora, só serve ao workflow proativo). Validado (0 erros), deixado
**inativo de propósito**.
**Resultado**: MCP Server de confirmação de agenda (ClinicAll)
completo e validado estruturalmente, junto com os dois workflows de
exemplo, prontos pra ativar quando houver clínica ClinicAll real —
falta resolver a autenticação (mesma pendência do MVP de agendamento),
preencher o `baseUrl` real, e provisionar banco Postgres + credencial
pro cliente.