# Design — MCP Server nativo no n8n para o ClinicAll **Data:** 2026-08-11 **Status:** Aprovado pelo usuário, aguardando plano de implementação. ## Objetivo Quarta aplicação do padrão de "MCP Server" nativo do n8n (`MCP Server Trigger` + `Call n8n Sub-Workflow Tool`), já validado em produção pro Z-PRO e estruturalmente pronto pro Klingo e pro SysLife, agora pro ClinicAll — a partir do manual oficial fornecido pelo usuário (`docs/API Clinicall.pdf`, "Documentação da API de Integração com ClinicAll para Módulo de Agendamento", versão 1.1.16, Focus Tecnologia em Sistemas). É a documentação mais completa dos 4 providers até agora: REST/JSON, campos obrigatórios explícitos, exemplos reais de request/response para todos os endpoints relevantes. ## Relação com o trabalho anterior Mesma arquitetura de base do Z-PRO/Klingo/SysLife. O ClinicAll não tem adapter TypeScript prévio em `gti-scheduling-mcp` — a fonte de verdade é exclusivamente o manual PDF fornecido nesta sessão. ## Contexto verificado (manual oficial, v1.1.16) Todos os endpoints usam o contexto fixo `/datasnap/rest`, formato REST/JSON. | Controller | Endpoint | Método | Parâmetros | Retorna | | --- | --- | --- | --- | --- | | `TEntityController` | `/unidades` | GET | `nome` (opcional) | lista `{idUnidade, nomeUnidade, cnpjUnidade}` | | `TEntityController` | `/especialidades` | GET | `nome` (opcional) | lista `{idEspecialidade, nomeEspecialidade}` | | `TEntityController` | `/convenios` | GET | `nome` (opcional) | lista `{idConvenio, nomeConvenio, cnpj, numeroANS, situacaoConvenio}` | | `TEntityController` | `/profissionais` | GET | `nome` (opcional) | lista `{idProfissional, nomeProfissional, situacaoProfissional, ..., especialidades[], horarios[]}` | | `TEntityController` | `/procedimentos` | GET | `nome`, `grp` (opcionais) | lista `{idProcedimento, nomeProcedimento, grupoProcedimento}` | | `TPatientController` | `/paciente` | GET/POST/PUT/DELETE | `busca` (nome ou id) | lista de objeto Paciente completo | | `TCalendarController` | `/disponibilidade//` | GET | route: `idUnidade`, `idProfissional`; query opcionais: `m` (meses), `p` (procedimento), `e` (especialidade), `c` (convênio) | lista de `{idUnidade, nomeUnidade, idEspecialidade, nomeEspecialidade, idMedico, conselho, nomeMedico, dataAgenda, horarios: [{idAgenda, horaAgenda}]}` | | `TCalendarController` | `/agendamento` | GET/POST/PUT/DELETE | corpo com `idAgenda, idUnidade, idPaciente, idProfissional, idProcedimento, dataAgenda, horaAgenda, idConvenio, carteiraConvenio, validadeCarteiraConvenio, usuario (obrigatório), senhaAutorizacao` | objeto agendamento (echo com dados conciliados) | **Fluxo real de agendamento** (conforme diagrama do manual, seção 3): o sistema externo consulta `/disponibilidade` para obter horários livres (cada um já é um registro `idAgenda` existente, `situacaoAgenda: "Livre"`) e depois "concilia" esse registro — atualiza o mesmo `idAgenda` com os dados do paciente/procedimento/convênio via `/agendamento`. Não é criar um registro novo do zero; é atualizar um que já existe. ## Ambiguidade real: qual verbo HTTP cria/atualiza o agendamento O manual lista `/agendamento` como aceitando `GET, POST, PUT, DELETE`, sem mapear explicitamente qual verbo corresponde a "conciliar um `idAgenda` já existente". Dado que o fluxo real é uma **atualização** de um registro que já existe (o `idAgenda` livre obtido via `/disponibilidade`), a decisão desta etapa é usar **`PUT`**. Esta é uma suposição não verificada contra a API real — fica registrada explicitamente, não é um fato confirmado pelo manual. Se, ao testar contra uma clínica real, `PUT` não funcionar como esperado, tentar `POST` antes de qualquer outra hipótese. ## Ambiguidade real: mecanismo de autenticação por requisição O manual não documenta nenhum header de autenticação (token, Bearer, API key) nos exemplos de `TEntityController`, `TPatientController` ou `TCalendarController`. Existe um `TAuthenticateController/login` separado (`POST`, `{userName, password}` → `{login, nomeUsuario}` ou `401` com mensagem de erro), mas o manual não explica se é obrigatório chamá-lo antes das demais requisições, nem que token/sessão ele geraria para as chamadas seguintes. **Decisão desta etapa**: não inventar um mecanismo de autenticação. Todos os nodes HTTP são construídos com `authentication: none`. Isso é uma limitação deliberada e documentada — não uma omissão. Resolver isso exige acesso a uma clínica ClinicAll real (para testar empiricamente) ou contato com o suporte da Focus Tecnologia em Sistemas. ## Escopo **8 tools**, cobrindo tudo que um agendamento completo precisa perguntar e confirmar: `unidade`, `especialidade`, `convenio`, `profissional`, `procedimento`, `paciente`, `consultar_horarios`, `agendar`. ## Arquitetura ``` [MCP Server Trigger] ──Tools──┬── unidade ──▶ GET /unidades ("MCP Server - ClinicAll") ├── especialidade ──▶ GET /especialidades ├── convenio ──▶ GET /convenios ├── profissional ──▶ GET /profissionais ├── procedimento ──▶ GET /procedimentos ├── paciente ──▶ GET /paciente ├── consultar_horarios ──▶ GET /disponibilidade/{idUnidade}/{idProfissional} └── agendar ──▶ PUT /agendamento ``` 9 workflows no total (servidor + 8 sub-workflows). Sem workflow de agente cliente — mesma decisão já tomada no Klingo e no SysLife (sem clínica real nem canal de mensagens definido ainda). ## Componentes ### Sub-workflows de catálogo (`unidade`, `especialidade`, `convenio`, `profissional`, `procedimento`) Mesmo esqueleto simples, cada um só troca endpoint/parâmetro: ``` [Execute Workflow Trigger] → [Config ClinicAll] → [Montar query]* → [GET ] → [Classificar resultado] ``` (*`Montar query` só existe quando há parâmetro opcional a passar — todos os 5 aceitam um parâmetro de busca por nome, exceto `procedimento` que aceita `nome` e `grp`.) - **Execute Workflow Trigger**: `nome` (opcional) em `unidade`, `especialidade`, `convenio`, `profissional`; `nome` e `grp` (ambos opcionais) em `procedimento`. - **Config ClinicAll** (`Set`): `baseUrl` desta clínica (placeholder, formato `http://:`). - **GET ``** (`HTTP Request`): URL `{{baseUrl}}/datasnap/rest/TEntityController/`, `authentication: none`, `neverError: true` + `fullResponse: true`. - **Classificar resultado** (`Code`): 2xx → devolve o corpo cru como veio (sem reformatar — sem exemplo de erro real capturado pra validar contra, mesmo raciocínio do SysLife/Klingo). Fora de 2xx → taxonomia genérica por status HTTP (`AUTHENTICATION_ERROR` 401/403, `NOT_FOUND` 404, `VALIDATION_ERROR` 400/422, `CLINICALL_API_ERROR` demais) — o manual não documenta nenhum código de erro específico do ClinicAll para estes controllers. ### Sub-workflow `paciente` ``` [Execute Workflow Trigger] → [Config ClinicAll] → [Montar query] → [GET /paciente] → [Classificar resultado] ``` - **Execute Workflow Trigger**: `busca` (nome ou id do paciente, conforme documentado). - Resto idêntico ao padrão de catálogo acima, endpoint `TPatientController/paciente`. ### Sub-workflow `consultar_horarios` ``` [Execute Workflow Trigger] → [Config ClinicAll] → [Montar URL/query] → [GET /disponibilidade/{idUnidade}/{idProfissional}] → [Classificar resultado] ``` - **Execute Workflow Trigger**: `idUnidade`, `idProfissional` (obrigatórios, route params), `meses`, `procedimento`, `especialidade`, `convenio` (opcionais, query params `m`/`p`/`e`/`c`). - **Montar URL/query** (`Code`): monta a URL com os route params interpolados e os query params opcionais só quando presentes. - Resto idêntico ao padrão de catálogo. ### Sub-workflow `agendar` ``` [Execute Workflow Trigger] → [Config ClinicAll] → [Montar corpo] → [PUT /agendamento] → [Classificar resultado] ``` - **Execute Workflow Trigger**: `idAgenda` (obrigatório — vem de `consultar_horarios`), `idUnidade`, `idPaciente`, `idProfissional`, `idProcedimento`, `dataAgenda`, `horaAgenda`, `idConvenio`, `carteiraConvenio`, `validadeCarteiraConvenio`, `usuario` (obrigatório, conforme documentado), `senhaAutorizacao` (opcional). - **Montar corpo** (`Code`): monta exatamente o JSON documentado, sem campo extra. - **PUT `/datasnap/rest/TCalendarController/agendamento`**: `authentication: none`, `neverError: true` + `fullResponse: true`. - **Classificar resultado** (`Code`): mesma proteção contra duplicidade já usada nos outros três providers — **qualquer 2xx é sucesso operacional**, sem exceção. Tenta mapear o objeto de agendamento documentado (echo com `idAgenda`, `situacaoAgenda`/`estadoAgenda` atualizados); se o corpo não bater, devolve "aceito, mapeamento pendente" construído só com os dados do input. Fora de 2xx: mesma classificação genérica. ## Credenciais **Nenhuma credencial é criada nesta rodada.** Todos os 8 nodes HTTP ficam com `authentication: none`, até sabermos o mecanismo real de autenticação do ClinicAll (ver seção de ambiguidade acima). ## Fora de escopo (adiado) - `TDomainController` (tabelas de domínio genéricas: sexo, raça, UF, município, operadora, dia da semana, estados/situações de agenda — não específicas de agendamento). - `TDocumentController`, `TReportController`, `TBIController` (documentos, relatórios, indicadores de BI — não fazem parte de realizar um agendamento). - `cobertura` (procedimentos cobertos por convênio), `valorprocedimentos`, `gruposProcedimento` (informativos complementares — podem ser adicionados depois se o agente precisar confirmar cobertura/valor antes de agendar, mas não são essenciais ao MVP). - `TAuthenticateController/login` — fica pendente até resolvermos o mecanismo de autenticação real. - Workflow de agente cliente (AI Agent + prompt + MCP Client Tool) — mesma decisão já tomada no Klingo e no SysLife, sem clínica real nem canal de mensagens definido. ## Testes Sem clínica ClinicAll real disponível: validação estrutural apenas (`n8n_validate_workflow`), nenhuma chamada HTTP real nesta etapa. Quando houver uma clínica real, a autenticação precisa ser resolvida **antes** de qualquer teste (ver ambiguidade acima) — não adianta testar o fluxo de agendamento sem isso. Depois de resolvida, mesma disciplina já usada nos outros providers: dado fictício, execução única por vez, conferência visual no ClinicAll antes de qualquer nova tentativa.