# 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.