MCP para agentes¶
Além do site, este projeto expõe um servidor MCP — a interface feita para um agente de IA, não para um navegador.
Mesmo domínio do wiki. O roteamento é por caminho: /mcp vai para o servidor MCP,
todo o resto para o site. Quem faz essa separação é um gateway Caddy no mesmo stack
(o Coolify prende um domínio a um único serviço, então dois serviços HTTP atrás do
mesmo host exigem um gateway).
Por que existe¶
O site é feito para olhos: tabelas formatadas, navegação, tema. Um agente não quer isso — ele quer dados precisos, em Markdown, sem casca. E há uma coisa que só existe no MCP: os relacionamentos entre tabelas, que ficaram fora do repositório por volume (são ~75% do JSON de uma tabela) mas são exatamente o que um agente precisa.
| Necessidade do agente | Como o site resolve | Como o MCP resolve |
|---|---|---|
| Estrutura de uma tabela | página de busca, clique | campos("SD1") → Markdown |
| Em quais tabelas existe um campo | busca por campo | buscar_campo("D1_COD") |
| Ligação entre tabelas | não tem | relacionamentos("SD1") |
| O que já está escrito no wiki | ler a página | buscar_wiki / ler_pagina |
| Página TOTVS externa limpa | abrir no navegador | ler_externo(url) |
Ferramentas¶
| Ferramenta | O que responde |
|---|---|
buscar_tabela(termo, limite) |
código (SD1) ou nome (clientes) → tabelas |
campos(tabela) |
estrutura SX3 completa: campo, título, descrição, tipo, contexto, tamanho |
buscar_campo(campo, limite) |
em quais tabelas o campo existe (prefixo: D1_ = todo o SD1) |
indices(tabela) |
chaves e nicknames (SIX), na ordem |
relacionamentos(tabela, direcao, limite) |
ligações SX9 — só aqui |
buscar_texto(termo, limite) |
busca por significado em nomes e descrições |
buscar_wiki(termo, limite) |
trechos do wiki, com o caminho da página |
ler_pagina(pagina) |
Markdown integral de uma página, pelo slug |
ler_externo(url) |
página externa → Markdown limpo |
listar_exemplos(status) |
exemplos guardados, com o estado de avaliação |
ler_exemplo(slug) |
o código de um exemplo, com o aviso de estado |
salvar_exemplo(...) |
guarda código gerado como exemplo/template — escrita trancada |
marcar_avaliado(slug, como_testou) |
promove de nao-avaliada para avaliada — escrita trancada |
Duas decisões que valem explicar¶
Não há extração de HTML para o nosso próprio wiki. O fonte já é Markdown — a forma mais limpa que existe. Passar Defuddle no HTML gerado do site seria um passo para trás. O Defuddle entra onde ele realmente vale: página externa (TDN, fórum, Terminal de Informação), que chega como HTML de 720 KB cheio de casca. Medido: a página do SD1 no Terminal de Informação rende ~60 KB de Markdown limpo.
Não há servidor de banco. O índice é um arquivo SQLite com FTS5, construído no build da imagem a partir do repositório. Um escritor (o build), leitores somente-leitura, ~54 MB de dados: nada que justifique um Postgres para operar — backup, versão, conexões — sem retorno. O FTS5 ainda resolve a busca textual sem precisar montar um índice invertido à mão.
Exemplos gerados: nascem nao-avaliada¶
Um agente que gera código pode guardá-lo como exemplo ou template. O que ele não pode é apresentá-lo como confiável.
Todo exemplo salvo por salvar_exemplo entra no estado nao-avaliada — não foi
compilado, não foi executado, não foi testado contra um Protheus real. A
página do exemplo carrega o aviso, e o índice mostra o estado.
Para promover a avaliada existe marcar_avaliado, e ela recusa sem o campo
como_testou: o estado exige o registro de como o código foi testado e qual foi o
resultado. Sem isso, "avaliada" seria um selo de confiança sem lastro — exatamente o
que este repositório existe para não fazer.
A escrita falha fechada
Todas as ferramentas de escrita recusam enquanto GITHUB_TOKEN e
EXEMPLOS_CHAVE não estiverem definidos no serviço mcp. Um endpoint público com
poder de commit e sem trava seria entregar o repositório: qualquer um que
descobrisse a URL escreveria no repo. Sem os dois, as ferramentas apenas
respondem o que falta configurar.
Publicação¶
Site e MCP são reconstruídos juntos, da mesma imagem de repositório: um push em
main dispara um webhook do GitHub que o gateway encaminha ao Coolify. Não há etapa
de revisão entre o push e a produção — quem publica testa antes.
Conectar um cliente¶
Limites, declarados¶
Lacuna: o servidor responde sobre o que já foi coletado. A coleta é incremental;
buscar_tabeladevolve o que existe no banco naquele momento.Segurança:
ler_externosó aceita uma lista de domínios conhecidos (TOTVS, fórum, Terminal de Informação). Um endpoint público que busca URL arbitrária é um vetor de SSRF — a lista é a defesa, e é configurável porEXTERNO_PERMITIDO.