chat / agent / coder, e — opcionalmente — aciona o agent automaticamente via um rules engine com três modos de operação.
sse(HTTP+SSE clássico) — listener persistente no GET/sse.http(Streamable HTTP da spec 2025-03-26) — listener opt-in via GET no endpoint configurado.stdio— qualquer mensagem JSON-RPC semidque o processo filho emita em stdout é tratada como notification.
confirm ou auto em ~/.chatcli/mcp/triggers.json).Arquitetura em uma figura
cli/mcp/channels.go, cli/mcp/triggers/triggers.go, cli/channel_triggers.go, cli/agent_system_prompt.go). A separação é proposital: o ChannelManager é process-local e independente do CLI; o engine de triggers é um pacote separado sem dependência reversa; o CLI pluga via OnMessage.
Como uma mensagem chega ao agent
Server emite notification
id (notification, no jargão do spec). Pode ser via SSE stream, GET de Streamable HTTP, ou linha de stdout em stdio.Transport extrai e roteia
ChannelManager.ProcessSSENotification. O método da notification vira o channel: notifications/ci-pipeline → channel ci-pipeline; channel/message com params.channel → channel params.channel.Filtro de subscription
channels: [...] e o channel não está na lista, a mensagem é descartada (log debug). Lista vazia significa “aceita tudo”.Persist + ring + unread
seq monotônico, vai pro ring em memória (FIFO, default 200), é gravada append-only em ~/.chatcli/mcp/channels.jsonl, e o contador de unread incrementa.Fan-out para subscribers
OnMessage registrado recebe uma cópia. O CLI registra dois: o engine de triggers (que avalia rules) e o renderer de banner (que prepara a UI).System prompt — turno seguinte
Trigger reativo (opcional)
Action que cai em uma das três filas: notify (banner discreto), confirm (pergunta yes/no), auto (executa o agent no próximo tick do prompt).Persistência
Garantias
- Durável entre sessões: mensagens recebidas enquanto o ChatCLI estava fechado ficam visíveis no próximo boot (até o limite de load).
- Append-only: nunca reescrevemos o arquivo. Crash do processo no meio de uma write deixa, no pior caso, uma linha truncada — o loader skip por linha, então uma linha corrompida nunca contamina as outras.
- Rotação automática: ao chegar em 10 MiB, o arquivo é renomeado para
.1e um novo é aberto. Só mantemos um histórico — channels são telemetria, não log de auditoria forense. - Best-effort: se a escrita falhar (disco cheio, permissão), o ChatCLI loga um warning uma vez, marca persistence como desabilitada só nessa sessão e continua servindo o ring em memória.
Boot — replay
Ao iniciar, o ChatCLI lê as últimas 200 linhas combinadas dechannels.jsonl.1 + channels.jsonl em ordem cronológica. Mensagens recarregadas:
- entram no ring;
- não contam como unread (já foram vistas);
- preservam seu
seqoriginal (o contador interno é avançado pro máximo observado).
Como configurar
Persistência é ligada por padrão quandoHOME é resolvível. Em ambientes containerizados sem HOME (raro), o ChannelManager cai para in-memory-only e loga um info no startup. Não há flag de configuração para “desligar persistence” — o caminho é deletar manualmente o arquivo se necessário (rm ~/.chatcli/mcp/channels.jsonl*).
Filtro por servidor — channels
O campo opcional channels na config do servidor MCP é um allow-list que decide quais channels desse servidor são aceitos pelo ring.
- Vazio/omitido → aceita todos os channels que o servidor emitir.
"*"explícito → equivalente a vazio.- Lista → só os channels listados literalmente entram no ring; outros são descartados (log debug).
- Whitespace nos entries é trimmado, então
" alerts "casa com"alerts".
enabledTools/disabledTools), aqui não há suporte a glob no channels da ServerConfig — é match literal. Globs (alerts/*, *) só existem nas rules de trigger, que rodam depois do filtro.Auto-injection no system prompt
Em todo turn (chat, agent, coder), as 5 mensagens mais recentes do ring são adicionadas ao system prompt como um bloco extra:
Garantias importantes
- Sem cache hint: esse bloco é volátil e propositalmente fica fora do prefixo cacheado da Anthropic. Inserir um bloco que muda toda turn dentro do cache trasharia o KV cache inteiro — pior do que não cachear nada.
- Mesmo conteúdo em chat / agent / coder: o builder de system prompt em todos os três modos foi unificado pra incluir o bloco.
- Ring vazio → bloco omitido: zero overhead quando você nunca recebeu nada.
Por que apenas 5?
Calibrado pra equilíbrio: o suficiente pra dar contexto recente (CI rodando + alert mais quente) sem ocupar tokens demais quando o usuário tem muitos servers conversando. Se você precisar de mais histórico em uma turn específica, use/channel inject que injeta as últimas 10.
Reactive Triggers (rules engine)
Esta é a parte opt-in. Quando você quer que o ChatCLI reaja a eventos (ex: “se o CI falhou, peça pra investigar”; “se houver alerta crítico, rode o agent”), defina rules em~/.chatcli/mcp/triggers.json.
Schema da rule
Match — exemplos práticos
alerts/critical, qualquer servidor.
alerts/critical, alerts/warning, alerts/info — qualquer sub-channel de alerts/. Não pega errors/critical (prefixo diferente).
prom-alerts, channel alerts/*, e content casando o regex. Todas precisam ser satisfeitas (AND).
Modos
mode: notify (default — zero surpresa)
Quando a rule casa:
- Imediatamente: um toast aparece no stderr com o nome da rule e um preview do content.
- Na próxima vez que você for digitar (próximo prompt tick), aparece um banner acima do prompt com o item na inbox.
- Nada roda. Você decide se quer agir.
mode: confirm (user-in-the-loop)
Quando a rule casa:
- Toast no stderr + prompt no banner: “run
/channel confirm <id> yesou/channel confirm <id> no”. - Nada roda até o usuário responder.
/channel confirm <id> yes(ou só/channel confirm <id>, default é yes) dispara o agent no template da rule./channel confirm <id> nodescarta a action.
mode: auto (autônomo — opt-in forte)
Requer tools whitelist obrigatória (validação rejeita rules auto sem tools). Quando casa:
- Toast no stderr informando que o trigger entrou na fila.
- No próximo prompt tick (depois de drainar park resumes e antes de processar input do usuário), o agent é acionado automaticamente no template da rule.
- A execução é renderizada dentro de uma
AUTO-AGENT envelope box— visualmente distinta de uma resposta a turno normal. Escaborta como em qualquer execução de agent.
Guard-rails do auto
toolswhitelist obrigatória — validação no startup rejeita ruleautosemtools.rateLimitededupWindowrecomendados — sem eles, um servidor barulhento pode disparar dezenas de turns por minuto.Escsempre aborta, não importa se o agent foi triggado por user ou por rule./channel pausedesliga tudo globalmente — usar antes de uma operação sensível para evitar interferência.
Configuração de rules
CI watcher (notify only — safe default)
Prod alerts → confirm
Canary deploys → auto (com whitelist apertada)
Validação
Falhas de schema rejeitam o arquivo inteiro (atomic apply — ou tudo entra, ou nada muda). Erros comuns:/channel rules reload.
Comandos /channel
Todos os subcomandos suportam autocomplete (Tab após /channel ), e a completude é multi-nível: clear oferece --all, rules oferece reload, confirm completa os IDs pendentes ao vivo e depois a decisão yes/no, e run completa os seqs de mensagens recentes. A command palette (bare /channel) compartilha a mesma fonte.
Exemplos
Listando
Filtrando
Rodando manualmente uma mensagem
Inspecionando rules
@channels — o agente lê o inbox
Tudo acima é dirigido pelo usuário. No agent/coder mode o modelo ganha a própria janela read-mostly para o mesmo inbox através do builtin @channels, para que eventos empurrados guiem uma tarefa em andamento sem esperar o usuário colar.
Auto-injection vs /channel inject — quando usar cada um
/channel inject é uma ação explícita que adiciona um system message ao histórico (permanece até a próxima compaction). /channel run é a opção “investigar agora” sem regra cadastrada.
Reconexão e fault tolerance
Mcp-Session-Id no initialize, o transport ecoa o header em todas as requests (incluindo o GET do listener). Reconexão preserva a sessão automaticamente.Casos de uso
CI/CD
rule notify em ci-pipeline. Você vê falhas no banner; /channel run <seq> lança o agent pra investigar com tools.Prod alerts (Prometheus/Datadog)
rule confirm em alerts/critical com prompt template. Cada alerta vira uma pergunta “investigate?” que vc responde sob demanda.Canary deploys
rule auto com whitelist apertada (kubectl_*, http_request). Toda canary inicia automaticamente as smoke checks.Webhooks externos (GitHub/Jira/Slack)
Limites e trade-offs
Troubleshooting
Recebo a notificação no /channel list mas o agent não reage automaticamente
Recebo a notificação no /channel list mas o agent não reage automaticamente
notify ou nenhuma rule. notify é passivo por desenho. Para ação:- Adicione uma rule
confirmouautoem~/.chatcli/mcp/triggers.jsone rode/channel rules reload. - Ou rode
/channel run <seq>manualmente em cima da mensagem.
O servidor HTTP envia push, mas nada chega no /channel list
O servidor HTTP envia push, mas nada chega no /channel list
/mcp logs <nome>— se aparecerserver returned 405 on GET <url>ou404ou501, o servidor não implementa push e o listener parou limpamente.- Caso contrário, verifica se o servidor está emitindo notifications no formato JSON-RPC sem
id. Conteúdodata:não-JSON também é capturado (cai no channelraw), mas não dispara rules específicas de outros channels.
A mensagem aparece em /channel list mas nenhuma rule dispara
A mensagem aparece em /channel list mas nenhuma rule dispara
/channel rules— confirma que a rule existe e está ativa./channel pausefoi rodado?/channel resumereativa.- Match: o
server/channel/contentRegexrealmente bate? Lembre quechannel: "alerts"não pega"alerts/critical"— use"alerts/*". rateLimitoudedupWindow— uma fire recente pode estar suprimindo.
O JSONL ficou enorme. Posso apagar?
O JSONL ficou enorme. Posso apagar?
rm ~/.chatcli/mcp/channels.jsonl*. Na próxima boot, o ring começa vazio.Em runtime, a rotação corta no limite de 10 MiB automaticamente — você pode chegar até ~20 MiB no total (ativo + .1) antes do próximo corte.Rules paradas depois de editar triggers.json
Rules paradas depois de editar triggers.json
/channel rules reload. Se a recarga falhar (schema inválido), o erro aparece na resposta e o ChatCLI mantém as rules anteriores ativas — sem fica num estado “sem rules”.Se o erro for de regex, copie-cole o regex pra https://regex101.com flavor “Golang” para isolar.Rule auto dispara em loop infinito
Rule auto dispara em loop infinito
- Bump
rateLimitsignificativamente (ex:"15m"). - Refine o
contentRegexpra só pegar o caso patológico. /channel pauseenquanto você investiga a config offline.
Quero ver TUDO que chegou (não só 5 últimas) no próximo turn
Quero ver TUDO que chegou (não só 5 últimas) no próximo turn
/channel inject — coloca as últimas 10 como system message no histórico. Persiste no histórico até a próxima compaction.Para visualizar sem injetar no LLM: /channel list mostra até 20.O agent é triggado em auto mode e abre uma tool que eu não autorizei
O agent é triggado em auto mode e abre uma tool que eu não autorizei
tools whitelist da rule. Em auto, só as tools listadas podem ser invocadas sem aprovação extra; tool fora da whitelist cai no fluxo normal de aprovação do agent — o que pode ser sua intenção ou não. Refine a whitelist no triggers.json.Próximos passos
MCP Integration
MCP Config
mcp_servers.json (campo channels incluído).Sistema de Hooks
Command Reference
/channel.