Spec: MCP Server nativo no n8n para o ClinicAll

Quarta aplicação do padrão nativo, a partir do manual oficial da
Focus Tecnologia (docs/API Clinicall.pdf) -- documentação mais
completa dos 4 providers até agora. 8 tools: unidade, especialidade,
convenio, profissional, procedimento, paciente, consultar_horarios
(GET /disponibilidade) e agendar (PUT /agendamento). Duas
ambiguidades reais documentadas explicitamente: mecanismo de
autenticação por requisição não especificado no manual (nenhuma
credencial criada, authentication:none) e verbo HTTP de
criação/atualização do agendamento não mapeado (assumido PUT, não
verificado). Sem clínica ClinicAll real ainda -- validação estrutural
apenas.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Gustavo
2026-08-11 08:48:32 -03:00
co-authored by Claude Sonnet 5
parent e2ed887a05
commit 04e5b189b0
@@ -0,0 +1,207 @@
# 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/<idUnidade>/<idProfissional>` | 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 <endpoint>] → [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://<host>:<porta>`).
- **GET `<endpoint>`** (`HTTP Request`): URL
`{{baseUrl}}/datasnap/rest/TEntityController/<endpoint>`,
`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.