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>
208 lines
11 KiB
Markdown
208 lines
11 KiB
Markdown
# 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.
|