GBR: ADR-0007 Servidor MCP Headless (Gleam Tools SDK) | Wiki
GBR: ADR-0007 Servidor MCP Headless (Gleam Tools SDK)
- *Status:* Aceito
- *Data:* 14 de Maio, 2026
*Contexto:*
A aplicação gleambr_ai foi desenhada primariamente como um Cliente/Agente, possuindo um Cérebro (LLM) próprio e uma Interface (REPL) focada no usuário humano. No entanto, o verdadeiro valor de engenharia da Frente ALPHA reside nas suas *Ferramentas Seguras (Tools), especificamente a execução via *Chroot (Sandboxing lógico) e a auditoria transacional via gbr_disk_log (HITL). Atualmente, agentes externos de mercado (como o gemini-cli, Cursor, Claude Desktop) sofrem de graves riscos de segurança ao receberem acesso total ao sistema de arquivos dos desenvolvedores. Limitar as nossas ferramentas seguras apenas ao nosso próprio REPL subutiliza o potencial do sistema.
*Decisão:*
Implementaremos a *Inversão de Papéis MCP*. O gleambr_ai passará a suportar um modo de execução *Headless (Servidor MCP)*.
-
*Segundo Entrypoint:* Criaremos um novo módulo ou uma flag de CLI (ex:
gleam run --module gleambr_ai/mcp_server) que contornará a inicialização do Cérebro (LLM) e da Interface (REPL). -
*I/O Estrito:* Neste modo, o sistema operará exclusivamente escutando requisições JSON-RPC na entrada padrão (
stdin) e respondendo na saída padrão (stdout). -
*Isolamento de Logs:* Para evitar a corrupção do protocolo JSON-RPC, todos os logs internos, avisos ou alertas visuais do Gleam/Erlang serão redirecionados compulsoriamente para a saída de erro (
stderr) ou para o arquivodisk_log. -
*Roteamento:* O loop Headless mapeará as requisições recebidas (ex:
tools/call) diretamente para o nosso módulo nativofilesystem.gleame devolverá o resultado codificado.
*Consequências:*
- *Positivas:* Transforma o projeto em um "Gleam Tools SDK" universal; permite que qualquer plataforma de IA do mercado consuma a segurança desenvolvida por nós; promove a linguagem Gleam e a Erlang VM como a base mais segura para operações de I/O em IA.
-
*Negativas:* Exige disciplina draconiana no uso de
io.printem todo o código-fonte (uma impressão de debug não intencional quebraria a integração com o cliente externo).
Análise da negativa
O problema do "Rogue Print" (o io.print desgarrado) é o pesadelo de todo protocolo baseado em Standard I/O (LSP, MCP). Um simples io.println("Chegou aqui!") esquecido por um desenvolvedor júnior no meio de uma função de cálculo destrói a resposta JSON-RPC, fazendo o gemini-cli ou Cursor capotar com um erro de Parse.
Para mitigarmos isso sem destruir a Experiência do Desenvolvedor (DX) — porque desenvolvedores precisam debugar código —, nós vamos implementar a *Tríade de I/O Seguro* no nosso monorepo.
A Tríade de I/O Seguro (Política e Arquitetura)
Nós vamos resolver isso em três camadas: Política (Linting), Encapsulamento (Nossa Lib) e a "Opção Nuclear" (Erlang VM).
Camada 1: A Política de Monorepo (Banimento do gleam/io)
A regra número um, que deve estar no README do repositório, é: **É expressamente proibido importar e usar gleam/io em bibliotecas e módulos de negócio.**
-
O
gleam/iosó pode existir em arquivos de entrypoint focados no usuário (como o nossorepl.gleam). - Se um desenvolvedor quiser debugar ou logar algo, ele deverá usar a nossa própria biblioteca de log (Camada 2).
Camada 2: A Casca de Log (gbr_log)
Nós criaremos um pequeno wrapper no nosso shared/gbr_log. A DX fica perfeita: o desenvolvedor chama gbr_log.debug("Meu dado").
Por baixo dos panos, o gbr_log *não* chama io.println. Ele chama o módulo nativo logger da Erlang. O logger da Erlang é assíncrono, estruturado e configurável em tempo de execução!
Camada 3: A "Opção Nuclear" (Redirecionamento de Fluxo na Erlang VM)
Aqui está o truque de mestre. A Erlang trata todos os io:format (o equivalente ao io.print do Gleam) não como impressões diretas no terminal, mas como mensagens enviadas para um processo especial chamado *Group Leader*.
No momento em que iniciarmos a aplicação com a flag --mcp-server (Modo Headless), a primeira coisa que o nosso main() fará é **reconfigurar o handler padrão do Erlang para cuspir tudo no stderr (Saída de Erro)**, que é ignorada pelo parser do JSON-RPC!