Deixe um servidor MCP te chamar: o cliente de eventos experimental e a entrega por webhook


Você escreveu um loop de monitoramento que faz o agente perguntar “tem ticket novo?” a cada dez minutos. Na maioria das vezes a resposta é não. Você pagou por dez chamadas ao modelo para receber dez “não”, e o único ticket que importava esperou até dez minutos para ser notado.

A extensão events do MCP existe para resolver esse tipo de problema: o servidor empurra um evento no momento em que algo acontece, em vez de o agente perguntar sem parar. O Hermes agora implementa o modo de entrega por webhook — PR #140121, mesclado em 22 de outubro de 2026. É experimental e vive na main, mas o caminho está completo: assinar, responder ao handshake de verificação e deixar um evento iniciar uma execução real do agente.

Por que isso era impossível antes

O Hermes já tinha a maior parte das peças. O listener de webhook do gateway verifica assinaturas Standard Webhooks whsec_ e carrega rotas dinâmicas, e essas rotas conseguem entregar resultados em qualquer lugar. O que faltava era o lado MCP: events/list, events/subscribe, events/unsubscribe e um loop de refresh para manter as concessões vivas.

Sem eles, registrar uma rota de webhook num servidor de eventos na mão falha. O servidor faz POST de um payload assinado {"type":"verification"}; o Hermes tratava como evento comum, iniciava uma execução, respondia 202 e nunca devolvia o challenge. O servidor concluía que o callback era inutilizável, devolvia challenge_failed e a assinatura morria.

Três passos para ligar

mcp_servers:
  ambush:
    url: "https://example.com/mcp"
    events: true          # registra as ferramentas de evento deste servidor

platforms:
  webhook:
    enabled: true
    extra:
      public_url: "https://hermes.example.com"

Três coisas precisam ser verdadeiras ao mesmo tempo:

  1. events: true é por servidor. Sem isso, as três ferramentas de evento nunca aparecem.
  2. A plataforma de webhook precisa estar habilitada — é por ali que os eventos chegam.
  3. public_url precisa ser HTTPS. Servidores recusam callback em HTTP puro, então coloque um proxy reverso que termina TLS ou um túnel na frente da porta 8644.

As três ferramentas

Com a chave ligada, o servidor ganha três ferramentas utilitárias:

  • list_event_types — os tipos de evento que o servidor oferece, mais as suas assinaturas atuais. Pergunte antes de adivinhar nomes.
  • subscribe_event — assina com os argumentos do próprio evento, o que fazer quando ele chegar (instructions, até 2000 caracteres) e para onde vai o resultado de cada execução (deliver, deliver_chat_id). Toda assinatura é aprovada por você, porque uma assinatura é um gatilho permanente para execuções sem supervisão. A aprovação mostra as instruções e o destino por inteiro.
  • unsubscribe_event — cancela.

instructions é a parte em que você deve pensar mais. Não é “me avise quando X acontecer”; é “quando X acontecer, faça isto”. Escreva de forma vaga e você ganha um agente que dispara toda vez e faz a coisa errada toda vez.

Por baixo: uma rota de webhook dinâmica

Cada assinatura é guardada como uma rota de webhook dinâmica — hermes webhook list mostra. Ela carrega o próprio segredo de assinatura whsec_, enquanto a concessão e o cursor de replay ficam ao lado, em mcp_event_subscriptions.json. A rota em si só muda quando você reassina com instruções ou destino diferentes.

Alguns detalhes de runtime que vale conhecer:

  • O challenge de verificação é respondido sem iniciar uma execução.
  • A deduplicação é por eventId, não pelo webhook-id de cada tentativa. Um servidor que reenvia o mesmo evento dispara uma única execução.
  • As concessões são renovadas antes de acabar o tempo que o servidor concedeu, enquanto a conexão está de pé, e de novo a cada reconexão.
  • Se o servidor encerra uma assinatura, o Hermes remove a rota e avisa o destino de deliver.
  • Um refresh que o servidor recusa de forma definitiva — acesso revogado, tipo de evento removido, argumentos que não valem mais — remove a rota; outras falhas tentam de novo com back-off.

O payload do evento chega inteiro à execução, até 32.000 caracteres de JSON, com um aviso quando é cortado. Payloads de evento são dados externos, então essas execuções usam o mesmo conjunto restrito de ferramentas de webhook de qualquer outra rota de webhook.

Cinco formas de expirar em silêncio

Isso não é assinar uma vez e esquecer. Os caminhos de falha conhecidos:

  1. A conexão precisa estar de pé. Com lazy: true ou idle_timeout_seconds, a conexão não está sempre ativa, então uma assinatura pode expirar até a próxima conexão reassiná-la.
  2. O gateway precisa estar rodando. Se o hermes gateway run não está de pé, nenhum evento chega — o motivo mais comum de um teste parecer que “o recurso está quebrado”.
  3. Falhas de refresh fazem back-off, mas uma recusa permanente apaga a rota.
  4. Limites de taxa descartam eventos. O limite por rota do listener (platforms.webhook.extra.rate_limit, 30 por minuto por padrão) vale também para entregas de evento, e um servidor pode não tentar de novo uma entrega rejeitada. Aumente para um tipo de evento movimentado.
  5. Desligar a rota no dashboard de webhook deixa a assinatura expirar; ligar de novo reassina.

O que explicitamente não é suportado

A documentação é direta sobre as lacunas, então não perca tempo ali:

  • Os modos de entrega poll e push não estão implementados — só webhook.
  • Um profile secundário sob gateway.multiplex_profiles não consegue assinar. Rotas dinâmicas carregam do home do profile de lançamento.

Todo o desenho também se apoia no esboço de design dos eventos MCP, que ainda é um rascunho de grupo de trabalho. É por isso que o recurso é rotulado como experimental: o formato do protocolo pode mudar.

Quando vale a pena

Se a sua necessidade é “olhar uma página uma vez por hora e ver se mudou”, um job de cron ainda é mais simples: sem URL pública, e ele não expira em silêncio quando o gateway para.

Eventos MCP compensam quando o upstream já tem um fluxo de eventos e a latência importa — tickets novos num issue tracker, um deploy que falhou, um item novo num feed. Nesses casos “ser avisado” e “perguntar a cada dez minutos” são experiências diferentes, e a primeira também elimina uma pilha de chamadas ao modelo que não retornaram nada.

Se você ainda está pesando push contra poll, nosso walkthrough de notificações por webhook compara os dois. E custos e limites de gasto de ferramentas MCP cobre outro conjunto de chaves novas no mesmo bloco mcp_servers que você provavelmente vai configurar junto com estas.

Status de release

O recurso está só na main: mesclado nas primeiras horas de 22 de outubro de 2026, fora da v0.22.4 publicada e de qualquer tag. Acompanhe a main se quiser testar, e espere que o formato do protocolo mude antes de estabilizar.