diff --git a/docs/superpowers/specs/2026-08-11-mcp-server-nativo-n8n-klingo-design.md b/docs/superpowers/specs/2026-08-11-mcp-server-nativo-n8n-klingo-design.md new file mode 100644 index 0000000..a817e28 --- /dev/null +++ b/docs/superpowers/specs/2026-08-11-mcp-server-nativo-n8n-klingo-design.md @@ -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 ` — 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 ` + 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 - ` — 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.