Spec: MCP Server nativo no n8n para o Klingo

Segunda aplicação do padrão nativo (MCP Server Trigger + Call n8n
Sub-Workflow Tool) já validado em produção pro Z-PRO, agora pro
Klingo. Diferença principal: autenticação em dois estágios
(X-APP-TOKEN estático + Bearer de sessão do paciente dinâmico, não
armazenável como credential). Limitação conhecida aceita nesta etapa:
consultar_horarios sempre retorna SLOT_MAPPING_UNAVAILABLE (lacuna de
contrato já existente no KlingoAdapter TS, não desta migração). Sem
clínica Klingo real ainda — escopo desta etapa é só o MCP Server,
validação estrutural, sem chamada HTTP real.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Gustavo
2026-08-10 23:33:57 -03:00
co-authored by Claude Sonnet 5
parent 786a97083e
commit acad79f175
@@ -0,0 +1,208 @@
# Design — MCP Server nativo no n8n para o Klingo
**Data:** 2026-08-11
**Status:** Aprovado pelo usuário, aguardando plano de implementação.
## Objetivo
Replicar, para o Klingo, o mesmo padrão já implementado e validado em
produção para o Z-PRO (`docs/superpowers/specs/2026-08-10-mcp-server-nativo-n8n-zpro-design.md`):
um "MCP Server" construído inteiramente dentro do n8n com nodes oficiais
(`MCP Server Trigger` + `Call n8n Sub-Workflow Tool`), portando fielmente a
lógica de negócio já testada em `gti-scheduling-mcp/src/adapters/klingo`
(`KlingoAdapter`) — sem inventar contrato novo, sem processo Node.js
externo.
## Relação com o trabalho anterior
Este design é a segunda aplicação do padrão nativo estabelecido pelo
Z-PRO. A arquitetura (trigger + sub-workflows por tool) é idêntica; a
diferença está inteiramente no **conteúdo** dos sub-workflows, porque o
Klingo tem um modelo de autenticação e de dados diferente do Z-PRO (ver
seções abaixo). O `gti-scheduling-mcp` (TypeScript) continua como
referência da lógica já testada — não é apagado, nem alterado.
## Diferença fundamental em relação ao Z-PRO
O Z-PRO usa um único Bearer token estático por clínica. O Klingo usa
**dois mecanismos de autenticação diferentes dentro do mesmo fluxo de
agendar**:
1. `X-APP-TOKEN` — token estático da aplicação/clínica, usado em
`POST /paciente/identificar` e `GET /agenda/horarios`.
2. `Authorization: Bearer <access_token>` — token de **sessão do
paciente**, obtido dinamicamente na resposta de
`POST /paciente/identificar` e usado apenas em
`POST /agenda/reservar`. Não é um segredo fixo de configuração — muda a
cada paciente/chamada, então **não pode ser uma Credential do n8n**:
precisa ser passado adiante entre nodes dentro da mesma execução, via
expressão.
## Limitação conhecida, aceita nesta versão
`consultarHorarios()` no `KlingoAdapter` atual **sempre falha** com
`SLOT_MAPPING_UNAVAILABLE`: a API responde com sucesso, mas o Swagger
público (`docs/klingo-openapi.json`) não documenta o formato interno de
cada horário individual dentro de `GET /agenda/horarios` — não é seguro
inventar nomes de campo. Consequência direta: `agendar()` exige um
`slotId` (o `id` do horário) que, na prática, hoje não há como obter pela
própria integração.
**Decisão tomada nesta etapa** (explícita, do usuário): replicar a
estrutura fielmente, com essa limitação conhecida e documentada — sem
tentar resolver o mapeamento de horários agora (isso é um problema de
contrato da API do Klingo, fora do escopo desta migração de padrão
arquitetural) e sem inventar um fluxo alternativo de obtenção de slot.
## Escopo desta etapa
Apenas os workflows do **MCP Server** (servidor + 2 sub-workflows). Sem
workflow de agente cliente (AI Agent + prompt + MCP Client Tool) — não há,
ainda, uma clínica Klingo real nem canal de mensagens (GChat/WhatsApp)
definido para testar de ponta a ponta. O workflow de agente cliente fica
para quando essas duas coisas existirem, seguindo o mesmo padrão já usado
no workflow "Agendamento - GChat - Z-PRO (clínica exemplo)".
Sem clínica Klingo real disponível para teste agora — a validação nesta
etapa é estrutural (`n8n_validate_workflow`, inspeção manual dos nodes),
**sem nenhuma chamada HTTP real** (não há `baseUrl`/`X-APP-TOKEN` reais
disponíveis). Quando houver uma clínica real, repete-se a mesma disciplina
de teste manual usada no Z-PRO (dado fictício, execução única por vez,
conferência visual antes de qualquer nova tentativa).
## Arquitetura
```
[MCP Server Trigger] ──Tools──┬── [Call Sub-Workflow Tool: "agendar"] ──▶ "MCP Server - Klingo - agendar"
(workflow "MCP Server - Klingo") │
└── [Call Sub-Workflow Tool: "consultar_horarios"] ──▶ "MCP Server - Klingo - consultar_horarios"
```
Três workflows, mesma estrutura de componentes do Z-PRO.
## Componentes
### Workflow "MCP Server - Klingo" (o servidor)
- `MCP Server Trigger` — `path` próprio (ex.:
`mcp-server-klingo-clinica-exemplo`, ajustar por clínica ao duplicar).
- Dois nodes `Call n8n Sub-Workflow Tool`:
- `agendar` → sub-workflow "MCP Server - Klingo - agendar".
- `consultar_horarios` → sub-workflow "MCP Server - Klingo -
consultar_horarios".
### Sub-workflow "MCP Server - Klingo - consultar_horarios"
```
[Execute Workflow Trigger] → [Code: retorna SLOT_MAPPING_UNAVAILABLE]
```
Sem chamada HTTP — mesma decisão de produto já tomada no
`KlingoAdapter.consultarHorarios()`. Retorna:
```json
{
"success": false,
"error": {
"code": "SLOT_MAPPING_UNAVAILABLE",
"message": "A API do Klingo responde com sucesso, mas o formato dos horários individuais não está documentado publicamente (ver docs/klingo-openapi.json). Não é possível consultar horários disponíveis por esta integração no momento."
}
}
```
### Sub-workflow "MCP Server - Klingo - agendar"
```
[Execute Workflow Trigger] → [Config Klingo] → [Montar corpo identificar]
→ [POST /paciente/identificar] → [Checar identificação]
→ [Extrair access_token] → [Montar corpo reservar]
→ [POST /agenda/reservar] → [Classificar resultado]
```
- **Execute Workflow Trigger**: parâmetros da tool — `patientPhone`
(obrigatório), `patientDocument` (opcional, CPF), `slotId`
(obrigatório — id do horário, ver limitação conhecida acima),
`specialtyId` (obrigatório — código do procedimento).
- **Config Klingo** (`Set`): `baseUrl` fixo desta clínica — placeholder
até existir uma clínica Klingo real; ajustar ao duplicar pra outra
clínica (mesmo espírito do `Config ZPRO`).
- **Montar corpo identificar** (`Code`): porta de
`buildIdentificarPacienteBody` — `telefone` obrigatório (lança
`VALIDATION_ERROR` sem chamada de rede se ausente); `cpf` quando
`patientDocument` vier preenchido, senão `apenas_telefone: true`.
- **`POST /paciente/identificar`**: autenticado por `X-APP-TOKEN`
(Credential Header Auth do n8n, estática). `neverError: true` +
`fullResponse: true`, mesmo padrão do Z-PRO.
- **Checar identificação** (`Code`): se status fora de 2xx, classifica o
erro (mesma taxonomia abaixo) e **encerra o fluxo aqui** — esta etapa
não cria nenhum dado no Klingo, sem risco de duplicidade, então é seguro
abortar com erro em vez de seguir adiante (mesmo raciocínio do
`KlingoAdapter`).
- **Extrair access_token** (`Code`): porta de `extractAccessToken` —
parseia o corpo de `identificar`, procura `access_token` string não
vazio. Se não achar, retorna `AUTHENTICATION_ERROR` explícito (nunca
segue adiante com um valor inventado).
- **Montar corpo reservar** (`Code`): porta de `buildReservarBody` —
`procedimento: specialtyId`, `id: slotId`,
`confirmado: "Confirmado via MCP Server - Klingo"` (marcador de
auditoria, mesmo texto fixo já usado no `KLINGO_CONFIRMADO_MARKER`,
adaptado). Valida `slotId`/`specialtyId` obrigatórios antes de montar
(mesmo padrão de `VALIDATION_ERROR` sem chamada de rede).
- **`POST /agenda/reservar`**: `Authorization: Bearer <access_token>`
montado **por expressão**, referenciando a saída do node "Extrair
access_token" — não é uma Credential do n8n. `neverError: true` +
`fullResponse: true`.
- **Classificar resultado** (`Code`): a partir daqui, **qualquer HTTP 2xx
é sucesso operacional**, sem exceção — mesma proteção contra
duplicidade já usada no Z-PRO e no Klingo TS. Tenta mapear o envelope
`reserva` documentado (`id` numérico, `data`, `hora`, `profissional`);
se o corpo não bater com esse formato, devolve um resultado "aceito,
mapeamento pendente" construído só com os dados do input (nunca inventa
campos do corpo) — porta fiel de
`buildAppointmentFromReserva`/`buildPendingMappingAppointment`. Fora do
intervalo 2xx: mesma classificação de erro do passo de identificação.
## Taxonomia de erro (compartilhada entre identificar e reservar)
Portada de `klingo.errors.ts`:
- 401/403 → `AUTHENTICATION_ERROR`
- 404 → `NOT_FOUND`
- 400/422 → `VALIDATION_ERROR`
- demais status → `KLINGO_API_ERROR`
- timeout de rede → `KLINGO_TIMEOUT`
Cada uso inclui uma mensagem de contexto (qual chamada falhou:
identificação ou reserva), sem nunca expor token, CPF, telefone ou corpo
bruto da resposta na mensagem de erro — mesma disciplina do código
TypeScript original.
## Credenciais
Uma única Credential nova no n8n: Header Auth `X-APP-TOKEN`, usada nos
nodes de `identificar`/`horarios`. Nome sugerido:
`Klingo APP Token - <clínica>` — criada com valor de rascunho/placeholder
nesta etapa (sem clínica real ainda) e substituída quando o token real
estiver disponível, do mesmo jeito que a credencial do Z-PRO foi corrigida
nesta sessão. O Bearer de sessão do paciente **não** vira credential —
nunca é armazenado, só trafega entre nodes dentro da mesma execução.
## Fora de escopo (adiado)
- Resolver `SLOT_MAPPING_UNAVAILABLE` de verdade (capturar uma resposta
real de `GET /agenda/horarios` e mapear o formato de cada horário) —
lacuna de contrato da API do Klingo, não desta migração de padrão.
- Workflow de agente cliente (AI Agent + prompt + MCP Client Tool) para o
Klingo — fica para quando houver uma clínica real e um canal de
mensagens definido.
- ClinicAll, SysLife via este mesmo padrão — replicar depois.
- O monorepo TypeScript (`packages/*-mcp`) continua pausado.
## Testes
Sem clínica Klingo real disponível: validação estrutural apenas
(`n8n_validate_workflow`, inspeção manual dos nodes e das expressões —
principalmente a passagem do `access_token` dinâmico entre nodes). Nenhuma
chamada HTTP real nesta etapa. Quando uma clínica real estiver disponível,
mesma disciplina já usada no Z-PRO: dado fictício, execução única por vez,
conferência visual no Klingo antes de qualquer nova tentativa.