4 tasks: sub-workflow consultar_horarios (SLOT_MAPPING_UNAVAILABLE), sub-workflow agendar (fluxo de 2 chamadas HTTP com autenticação em dois estágios — X-APP-TOKEN estático + Bearer de sessão dinâmico via expressão, 3 pontos de saída antecipada por erro/validação), servidor MCP com as 2 tools, e revisão final + commit. Sem clínica Klingo real disponível — validação só estrutural, workflows ficam inativos até haver um workflow de agente cliente pra consumi-los. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
26 KiB
MCP Server Nativo no n8n para o Klingo Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task (não subagent-driven-development — as tasks são chamadas de API do n8n, não commits de código; não há diff de git pra revisar por task). Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Construir o "MCP Server" do Klingo inteiramente dentro do n8n (nodes oficiais MCP Server Trigger + Call n8n Sub-Workflow Tool), segunda aplicação do padrão já validado em produção pro Z-PRO.
Architecture: 3 workflows n8n — o servidor (MCP Server Trigger + 2 tools) e dois sub-workflows (agendar, consultar_horarios) que portam a lógica já validada de klingo.adapter.ts/klingo.mapper.ts/klingo.errors.ts para nodes Code/HTTP Request/If. Diferente do Z-PRO, agendar tem duas chamadas HTTP em sequência com dois mecanismos de autenticação (X-APP-TOKEN estático + Bearer de sessão dinâmico), e três pontos de saída antecipada por erro/validação.
Tech Stack: n8n (devn8n.gtigrupo.com.br), nodes oficiais @n8n/n8n-nodes-langchain (mcpTrigger, toolWorkflow), n8n-nodes-base (executeWorkflowTrigger, httpRequest, code, set, if).
Global Constraints
- Nenhum código TypeScript é alterado —
gti-scheduling-mcpfica intocado como referência. - Qualquer HTTP 2xx de
POST /agenda/reservaré sucesso operacional, sem exceção — nunca reportar erro numa operação que o Klingo já aceitou (risco de agendamento duplicado).POST /paciente/identificaré diferente: não cria nenhum dado no Klingo, então é seguro abortar com erro se essa etapa falhar, sem seguir adiante. consultar_horariossempre retorna erroSLOT_MAPPING_UNAVAILABLE— decisão já registrada noKlingoAdapter(lacuna de contrato da API, não uma falha técnica), sem chamada HTTP.- O Bearer de
POST /agenda/reservarnunca é uma Credential do n8n — é construído por expressão a partir doaccess_tokenretornado porPOST /paciente/identificar, dentro da mesma execução. - Nenhuma credencial é inserida por quem executa este plano de forma automática — a credencial
X-APP-TOKENé criada manualmente pelo usuário na UI do n8n (mesmo sem valor real disponível ainda, ver Pré-requisito). - Sem clínica Klingo real disponível nesta etapa: nenhuma chamada HTTP real é feita. Validação é só estrutural (
n8n_validate_workflow). Os 3 workflows ficam inativos ao final (nada os aciona ainda — sem workflow de agente cliente).
Spec de referência: docs/superpowers/specs/2026-08-11-mcp-server-nativo-n8n-klingo-design.md
Pré-requisito (ação manual do usuário)
Criar no n8n uma Credential do tipo Header Auth (Settings → Credentials → New → "Header Auth"):
- Name:
X-APP-TOKEN - Value: um valor de rascunho por enquanto (ex.:
SUBSTITUIR_PELO_TOKEN_REAL_DA_CLINICA) — sem clínica Klingo real ainda, não há token de verdade. Trocar pelo valor real assim que houver uma clínica configurada, do mesmo jeito que a credencial do Z-PRO foi corrigida nesta sessão.
Nomeie como Klingo APP Token - clínica pendente. Anote o ID gerado — será necessário na Task 2.
Task 1: Sub-workflow "MCP Server - Klingo - consultar_horarios"
Interfaces:
-
Produces: um workflow n8n chamado
MCP Server - Klingo - consultar_horarios, com umExecute Workflow Trigger(modo passthrough) →Codeque sempre retorna{ success: false, error: { code: "SLOT_MAPPING_UNAVAILABLE", message: "..." } }. -
Step 1: Criar o workflow
Chamar n8n_create_workflow:
{
"name": "MCP Server - Klingo - consultar_horarios",
"nodes": [
{
"id": "trigger",
"name": "Execute Workflow Trigger",
"type": "n8n-nodes-base.executeWorkflowTrigger",
"typeVersion": 1.2,
"position": [0, 0],
"parameters": { "inputSource": "passthrough" }
},
{
"id": "slot-mapping-unavailable",
"name": "Retornar SLOT_MAPPING_UNAVAILABLE",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [260, 0],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "return {\n json: {\n success: false,\n error: {\n code: \"SLOT_MAPPING_UNAVAILABLE\",\n 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.\",\n },\n },\n};"
}
}
],
"connections": {
"Execute Workflow Trigger": {
"main": [[{ "node": "Retornar SLOT_MAPPING_UNAVAILABLE", "type": "main", "index": 0 }]]
}
}
}
Guarde o id retornado como <wf-klingo-consultar-horarios>.
- Step 2: Validar
n8n_validate_workflow com {"id": "<wf-klingo-consultar-horarios>"}. Esperado: sem erros.
- Step 3: Testar manualmente
Na UI do n8n, abrir o workflow e clicar em "Execute Workflow". Esperado: o node "Retornar SLOT_MAPPING_UNAVAILABLE" produz { success: false, error: { code: "SLOT_MAPPING_UNAVAILABLE", ... } }.
Task 2: Sub-workflow "MCP Server - Klingo - agendar"
Interfaces:
-
Consumes: credencial
Klingo APP Token - clínica pendentedo pré-requisito. -
Produces: um workflow n8n chamado
MCP Server - Klingo - agendar, que recebepatientPhone(obrigatório),patientDocument(opcional),slotId(obrigatório),specialtyId(obrigatório), e retorna{ success: true, data: {...} }ou{ success: false, error: {...} }. -
Step 1: Criar o workflow com todos os nodes e conexões
Chamar n8n_create_workflow:
{
"name": "MCP Server - Klingo - agendar",
"nodes": [
{
"id": "trigger",
"name": "Execute Workflow Trigger",
"type": "n8n-nodes-base.executeWorkflowTrigger",
"typeVersion": 1.2,
"position": [0, 0],
"parameters": {
"inputSource": "workflowInputs",
"workflowInputs": {
"values": [
{ "name": "patientPhone", "type": "string" },
{ "name": "patientDocument", "type": "string" },
{ "name": "slotId", "type": "string" },
{ "name": "specialtyId", "type": "string" }
]
}
}
},
{
"id": "config",
"name": "Config Klingo",
"type": "n8n-nodes-base.set",
"typeVersion": 3.5,
"position": [260, 0],
"parameters": {
"mode": "manual",
"includeOtherFields": true,
"assignments": {
"assignments": [
{ "id": "cfg-baseurl", "name": "baseUrl", "type": "string", "value": "https://SUBSTITUIR-PELA-URL-REAL-DA-CLINICA.klingo.com.br" }
]
}
}
},
{
"id": "montar-identificar",
"name": "Montar corpo identificar",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [520, 0],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const input = $json;\nconst phone = (input.patientPhone || \"\").trim();\n\nif (!phone) {\n return {\n json: {\n _outcome: \"final\",\n success: false,\n error: {\n code: \"VALIDATION_ERROR\",\n message: \"O telefone do paciente é obrigatório para identificação no Klingo.\",\n },\n },\n };\n}\n\nconst identificarBody = { telefone: phone };\nif (input.patientDocument && String(input.patientDocument).trim()) {\n identificarBody.cpf = String(input.patientDocument).trim();\n} else {\n identificarBody.apenas_telefone = true;\n}\n\nreturn {\n json: {\n _outcome: \"continue\",\n baseUrl: input.baseUrl,\n slotId: input.slotId,\n specialtyId: input.specialtyId,\n identificarBody,\n },\n};"
}
},
{
"id": "if-validacao-identificar",
"name": "Dados de identificação válidos?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [780, 0],
"parameters": {
"conditions": {
"options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 2 },
"combinator": "and",
"conditions": [
{
"id": "cond-outcome-continue-1",
"leftValue": "={{ $json._outcome }}",
"rightValue": "continue",
"operator": { "type": "string", "operation": "equals" }
}
]
},
"options": {}
}
},
{
"id": "post-identificar",
"name": "POST /paciente/identificar",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [1040, -120],
"parameters": {
"method": "POST",
"url": "={{ $json.baseUrl }}/paciente/identificar",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"contentType": "json",
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify($json.identificarBody) }}",
"options": {
"response": { "response": { "fullResponse": true, "neverError": true, "responseFormat": "autodetect" } },
"timeout": 15000
}
},
"credentials": {
"httpHeaderAuth": { "id": "SUBSTITUIR_PELO_ID_DA_CREDENCIAL", "name": "Klingo APP Token - clínica pendente" }
}
},
{
"id": "processar-identificar",
"name": "Processar identificação",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [1300, -120],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const status = $json.statusCode;\nconst rawBody = $json.body;\nconst bodyText = typeof rawBody === \"string\" ? rawBody : JSON.stringify(rawBody ?? \"\");\n\nfunction classifyError(status, context) {\n if (status === 401 || status === 403) {\n return { code: \"AUTHENTICATION_ERROR\", message: `Falha de autenticação na API do Klingo ao ${context} (status ${status}).` };\n }\n if (status === 404) {\n return { code: \"NOT_FOUND\", message: `Recurso não encontrado na API do Klingo ao ${context} (status ${status}).` };\n }\n if (status === 400 || status === 422) {\n return { code: \"VALIDATION_ERROR\", message: `A API do Klingo rejeitou os dados enviados ao ${context} (status ${status}).` };\n }\n return { code: \"KLINGO_API_ERROR\", message: `A API do Klingo retornou um erro inesperado ao ${context} (status ${status}).` };\n}\n\nif (status < 200 || status >= 300) {\n return { json: { _outcome: \"final\", success: false, error: classifyError(status, \"identificar o paciente\") } };\n}\n\nlet parsed = null;\ntry {\n parsed = JSON.parse(bodyText);\n} catch (e) {\n parsed = null;\n}\n\nconst accessToken = parsed && typeof parsed.access_token === \"string\" ? parsed.access_token.trim() : \"\";\n\nif (!accessToken) {\n return {\n json: {\n _outcome: \"final\",\n success: false,\n error: {\n code: \"AUTHENTICATION_ERROR\",\n message: \"A resposta de identificação do paciente no Klingo não trouxe um access_token válido.\",\n },\n },\n };\n}\n\nconst prev = $('Montar corpo identificar').item.json;\nreturn {\n json: {\n _outcome: \"continue\",\n baseUrl: prev.baseUrl,\n slotId: prev.slotId,\n specialtyId: prev.specialtyId,\n accessToken,\n },\n};"
}
},
{
"id": "if-identificacao-ok",
"name": "Identificação OK?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [1560, -120],
"parameters": {
"conditions": {
"options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 2 },
"combinator": "and",
"conditions": [
{
"id": "cond-outcome-continue-2",
"leftValue": "={{ $json._outcome }}",
"rightValue": "continue",
"operator": { "type": "string", "operation": "equals" }
}
]
},
"options": {}
}
},
{
"id": "montar-reservar",
"name": "Montar corpo reservar",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [1820, -240],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const input = $json;\nconst slotId = (input.slotId || \"\").toString().trim();\nconst specialtyId = (input.specialtyId || \"\").toString().trim();\n\nif (!slotId) {\n return {\n json: {\n _outcome: \"final\",\n success: false,\n error: { code: \"VALIDATION_ERROR\", message: \"slotId é obrigatório para agendar no Klingo (obtido via consultarHorarios()).\" },\n },\n };\n}\nif (!specialtyId) {\n return {\n json: {\n _outcome: \"final\",\n success: false,\n error: { code: \"VALIDATION_ERROR\", message: \"specialtyId é obrigatório para agendar no Klingo (código do procedimento do horário selecionado).\" },\n },\n };\n}\n\nconst reservarBody = {\n procedimento: specialtyId,\n id: slotId,\n confirmado: \"Confirmado via MCP Server - Klingo\",\n};\n\nreturn {\n json: {\n _outcome: \"continue\",\n baseUrl: input.baseUrl,\n accessToken: input.accessToken,\n slotId,\n specialtyId,\n reservarBody,\n },\n};"
}
},
{
"id": "if-validacao-reservar",
"name": "Dados de reserva válidos?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [2080, -240],
"parameters": {
"conditions": {
"options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 2 },
"combinator": "and",
"conditions": [
{
"id": "cond-outcome-continue-3",
"leftValue": "={{ $json._outcome }}",
"rightValue": "continue",
"operator": { "type": "string", "operation": "equals" }
}
]
},
"options": {}
}
},
{
"id": "post-reservar",
"name": "POST /agenda/reservar",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [2340, -360],
"parameters": {
"method": "POST",
"url": "={{ $json.baseUrl }}/agenda/reservar",
"authentication": "none",
"sendHeaders": true,
"specifyHeaders": "keypair",
"headerParameters": {
"parameters": [
{ "name": "Authorization", "value": "={{ 'Bearer ' + $json.accessToken }}" }
]
},
"sendBody": true,
"contentType": "json",
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify($json.reservarBody) }}",
"options": {
"response": { "response": { "fullResponse": true, "neverError": true, "responseFormat": "autodetect" } },
"timeout": 15000
}
}
},
{
"id": "classificar-resultado",
"name": "Classificar resultado",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [2600, -360],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const status = $json.statusCode;\nconst rawBody = $json.body;\nconst bodyText = typeof rawBody === \"string\" ? rawBody : JSON.stringify(rawBody ?? \"\");\n\nfunction classifyError(status, context) {\n if (status === 401 || status === 403) {\n return { code: \"AUTHENTICATION_ERROR\", message: `Falha de autenticação na API do Klingo ao ${context} (status ${status}).` };\n }\n if (status === 404) {\n return { code: \"NOT_FOUND\", message: `Recurso não encontrado na API do Klingo ao ${context} (status ${status}).` };\n }\n if (status === 400 || status === 422) {\n return { code: \"VALIDATION_ERROR\", message: `A API do Klingo rejeitou os dados enviados ao ${context} (status ${status}).` };\n }\n return { code: \"KLINGO_API_ERROR\", message: `A API do Klingo retornou um erro inesperado ao ${context} (status ${status}).` };\n}\n\nif (status < 200 || status >= 300) {\n return { json: { success: false, error: classifyError(status, \"reservar o horário\") } };\n}\n\n// Qualquer 2xx é sucesso operacional. Tenta mapear o envelope 'reserva'\n// documentado (id numérico, data, hora, profissional); nunca inventa campos\n// que o corpo não confirma.\nlet parsed = null;\ntry {\n parsed = JSON.parse(bodyText);\n} catch (e) {\n parsed = null;\n}\n\nconst reservaOk = parsed && typeof parsed.id === \"number\";\n\nif (reservaOk) {\n return {\n json: {\n success: true,\n data: {\n id: String(parsed.id),\n data: typeof parsed.data === \"string\" ? parsed.data : undefined,\n hora: typeof parsed.hora === \"string\" ? parsed.hora : undefined,\n professionalId: parsed.profissional && parsed.profissional.id != null ? String(parsed.profissional.id) : undefined,\n professionalName: parsed.profissional && typeof parsed.profissional.nome === \"string\" ? parsed.profissional.nome : undefined,\n status: \"confirmed\",\n responseMappingPending: false,\n },\n },\n };\n}\n\nconst input = $('Montar corpo reservar').item.json;\nreturn {\n json: {\n success: true,\n data: {\n slotId: input.slotId,\n specialtyId: input.specialtyId,\n status: \"pending\",\n responseMappingPending: true,\n },\n },\n};"
}
}
],
"connections": {
"Execute Workflow Trigger": {
"main": [[{ "node": "Config Klingo", "type": "main", "index": 0 }]]
},
"Config Klingo": {
"main": [[{ "node": "Montar corpo identificar", "type": "main", "index": 0 }]]
},
"Montar corpo identificar": {
"main": [[{ "node": "Dados de identificação válidos?", "type": "main", "index": 0 }]]
},
"Dados de identificação válidos?": {
"main": [
[{ "node": "POST /paciente/identificar", "type": "main", "index": 0 }],
[]
]
},
"POST /paciente/identificar": {
"main": [[{ "node": "Processar identificação", "type": "main", "index": 0 }]]
},
"Processar identificação": {
"main": [[{ "node": "Identificação OK?", "type": "main", "index": 0 }]]
},
"Identificação OK?": {
"main": [
[{ "node": "Montar corpo reservar", "type": "main", "index": 0 }],
[]
]
},
"Montar corpo reservar": {
"main": [[{ "node": "Dados de reserva válidos?", "type": "main", "index": 0 }]]
},
"Dados de reserva válidos?": {
"main": [
[{ "node": "POST /agenda/reservar", "type": "main", "index": 0 }],
[]
]
},
"POST /agenda/reservar": {
"main": [[{ "node": "Classificar resultado", "type": "main", "index": 0 }]]
}
}
}
Antes de chamar a API, substitua "SUBSTITUIR_PELO_ID_DA_CREDENCIAL" pelo ID real da credencial criada no pré-requisito (buscar via n8n_manage_credentials action: "list", procurando "Klingo APP Token - clínica pendente").
Guarde o id do workflow retornado como <wf-klingo-agendar>.
Nota sobre os três outputs "erro final" (Dados de identificação válidos?, Identificação OK?, Dados de reserva válidos? no ramo FALSE): cada um desses ramos termina sem conexão de saída — o item que sai da branch FALSE de cada If já tem o formato final { success: false, error: {...} } (produzido pelo Code node anterior), e é isso que o Call n8n Sub-Workflow Tool devolve como resultado da tool quando o sub-workflow termina por ali. Não precisa de nenhum node adicional depois.
- Step 2: Validar
n8n_validate_workflow com {"id": "<wf-klingo-agendar>"}. Esperado: sem erros de estrutura/conexão. Se a credencial não tiver sido resolvida corretamente, o erro vai apontar isso — corrigir o ID antes de continuar.
- Step 3: Revisão manual das expressões (sem chamada HTTP real)
Sem clínica Klingo real disponível, não há como testar com uma execução de verdade nesta etapa. Em vez disso, abrir cada node Code e If na UI do n8n e conferir visualmente:
Montar corpo identificarlê$json.patientPhone/patientDocument/slotId/specialtyId/baseUrl(vindos doConfig Klingo, que passa adiante os campos do trigger viaincludeOtherFields).Processar identificaçãoreferencia$('Montar corpo identificar').item.jsoncorretamente (nome do node bate exatamente com o nome usado na expressão).Montar corpo reservarreferencia$json.baseUrl/accessToken(saída deProcessar identificação).Classificar resultadoreferencia$('Montar corpo reservar').item.jsoncorretamente.- O header
AuthorizationdePOST /agenda/reservarusa={{ 'Bearer ' + $json.accessToken }}— não é uma Credential.
Task 3: Workflow "MCP Server - Klingo" (o servidor)
Interfaces:
-
Consumes:
<wf-klingo-agendar>(Task 2),<wf-klingo-consultar-horarios>(Task 1). -
Produces: endpoint MCP em
https://devn8n.gtigrupo.com.br/mcp/mcp-server-klingo(path a ajustar quando houver uma clínica real — ver Step 1). -
Step 1: Criar o workflow com o trigger e os dois nodes de tool
{
"name": "MCP Server - Klingo",
"nodes": [
{
"id": "mcp-trigger",
"name": "MCP Server Trigger",
"type": "@n8n/n8n-nodes-langchain.mcpTrigger",
"typeVersion": 2,
"position": [0, 0],
"parameters": { "path": "mcp-server-klingo", "authentication": "none" }
},
{
"id": "tool-agendar",
"name": "agendar",
"type": "@n8n/n8n-nodes-langchain.toolWorkflow",
"typeVersion": 2.2,
"position": [260, -100],
"parameters": {
"description": "Reserva e confirma um horário já escolhido na clínica (Klingo). Exige slotId (id do horário) e specialtyId (código do procedimento) — ambos obtidos previamente, fora desta tool, já que consultar_horarios não está disponível para este provider no momento. Exige também o telefone do paciente para identificação.",
"source": "database",
"workflowId": { "value": "<wf-klingo-agendar>", "mode": "id" }
}
},
{
"id": "tool-consultar",
"name": "consultar_horarios",
"type": "@n8n/n8n-nodes-langchain.toolWorkflow",
"typeVersion": 2.2,
"position": [260, 100],
"parameters": {
"description": "Consulta horários disponíveis. Este provider (Klingo) não suporta essa consulta no momento — sempre retorna erro SLOT_MAPPING_UNAVAILABLE (lacuna de contrato conhecida da API pública do Klingo, não uma falha temporária).",
"source": "database",
"workflowId": { "value": "<wf-klingo-consultar-horarios>", "mode": "id" }
}
}
],
"connections": {
"agendar": {
"ai_tool": [[{ "node": "MCP Server Trigger", "type": "ai_tool", "index": 0 }]]
},
"consultar_horarios": {
"ai_tool": [[{ "node": "MCP Server Trigger", "type": "ai_tool", "index": 0 }]]
}
}
}
Substitua <wf-klingo-agendar>/<wf-klingo-consultar-horarios> pelos IDs reais das Tasks 1 e 2.
Guarde o id deste workflow como <wf-mcp-server-klingo>.
- Step 2: Mapear os parâmetros da tool "agendar" na UI (passo manual — resourceMapper não é confiável via API)
Abrir o node agendar na UI do n8n. Em "Workflow Inputs", mapear cada campo que o sub-workflow espera (patientPhone, patientDocument, slotId, specialtyId) para uma expressão $fromAI(...) com uma descrição clara pro modelo, mesmo padrão já validado no Z-PRO (ver docs/n8n-agendamento-runbook.md, seções 9 e 12). Como slotId/specialtyId não vêm de uma consulta de horários funcional neste provider, deixar claro na descrição de cada $fromAI(...) que esses valores só existem se o cliente/atendente já os informou por outro meio (ex.: repassados manualmente por um atendente humano) — não inventar uma forma de obtê-los.
- Step 3: Validar (sem ativar)
n8n_validate_workflow com {"id": "<wf-mcp-server-klingo>"}. Esperado: sem erros. Não ativar nenhum dos 3 workflows nesta etapa — não há workflow de agente cliente que os chame ainda (ver Global Constraints), e sub-workflows chamados via Call n8n Sub-Workflow Tool precisam estar active: true só quando estiverem realmente em uso (lição aprendida na migração do Z-PRO, documentada em docs/n8n-agendamento-runbook.md, seção 13). Ativar os 3 quando um workflow de agente cliente for construído, junto com esse trabalho.
Task 4: Revisão final e commit
Interfaces:
-
Consumes: workflows das Tasks 1-3.
-
Step 1: Validação estrutural final dos 3 workflows
Rodar n8n_validate_workflow nos três IDs (<wf-klingo-consultar-horarios>, <wf-klingo-agendar>, <wf-mcp-server-klingo>) com {"options": {"profile": "ai-friendly"}}. Esperado: sem erros nos três. Anotar os 3 IDs e o path do endpoint MCP (mcp-server-klingo) em algum lugar acessível para quando o Step 3 da Task 3 for revisitado com uma clínica real.
- Step 2: Atualizar a spec com os IDs reais
Editar docs/superpowers/specs/2026-08-11-mcp-server-nativo-n8n-klingo-design.md, adicionando ao final uma seção curta "Status desta etapa" com os 3 IDs de workflow criados, o path do endpoint MCP, e uma nota reafirmando que nada está ativo/testado com dado real ainda — pendente de uma clínica Klingo real.
- Step 3: Commit
cd "D:\PROJETOS\KLINGO-CLINICAL-SYSLIFE-GCHAT-AGENDAMENTO"
git add docs/superpowers/specs/2026-08-11-mcp-server-nativo-n8n-klingo-design.md
git commit -m "docs: registra IDs dos workflows do MCP Server nativo (Klingo)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>"
(Os workflows em si vivem no n8n, não em arquivo — não há mais nada pra commitar além da atualização da spec com os IDs. Opcional: exportar os 3 workflows com n8n_get_workflow e salvar em docs/n8n-workflows/, mesmo processo já usado para o Z-PRO.)
Observação sobre execução
Assim como no plano do Z-PRO, estas tasks são chamadas de API ao n8n, não commits de código — a skill executing-plans deve tratar cada Step como uma chamada de ferramenta + validação, não um ciclo de teste automatizado. Sem clínica Klingo real disponível, a Task 2/Step 3 e a Task 3/Step 3 são revisão manual/estrutural, não execução real — isso é intencional e está registrado em Global Constraints, não é um passo pulado por engano.