Deja que un servidor MCP te llame: el cliente de eventos experimental y su entrega por webhook

Escribiste un bucle de monitorización que hace que el agente pregunte “¿tickets nuevos?” cada diez minutos. Casi siempre la respuesta es no. Pagaste diez llamadas al modelo para recibir diez “no”, y el único ticket que importaba esperó hasta diez minutos para ser visto.
La extensión events de MCP existe para arreglar ese tipo de problema: el servidor empuja un evento en el momento en que algo pasa, en lugar de que el agente pregunte una y otra vez. Hermes ya implementa su modo de entrega por webhook — PR #140121, fusionado el 22 de octubre de 2026. Es experimental y vive en main, pero el camino está completo: suscribirse, responder al handshake de verificación y dejar que un evento arranque una ejecución real del agente.
Por qué antes esto era imposible
Hermes ya tenía casi todas las piezas. El listener de webhooks del gateway verifica firmas whsec_ de Standard Webhooks y carga rutas dinámicas, y esas rutas pueden entregar resultados donde sea. Lo que faltaba era el lado MCP: events/list, events/subscribe, events/unsubscribe y un bucle de refresco para mantener vivos los grants.
Sin eso, registrar a mano una ruta de webhook con un servidor de eventos falla. El servidor hace POST de un payload firmado {"type":"verification"}; Hermes lo trataba como un evento normal, arrancaba una ejecución, respondía 202 y nunca devolvía el challenge. El servidor decidía que el callback no servía, devolvía challenge_failed, y la suscripción moría.
Tres pasos para activarlo
mcp_servers:
ambush:
url: "https://example.com/mcp"
events: true # register the event tools for this server
platforms:
webhook:
enabled: true
extra:
public_url: "https://hermes.example.com"
Tres cosas tienen que cumplirse a la vez:
events: truees por servidor. Sin eso, las tres herramientas de eventos nunca aparecen.- La plataforma de webhook debe estar activada — así es como llegan los eventos.
public_urldebe ser HTTPS. Los servidores rechazan callbacks en HTTP plano, así que pon delante del puerto 8644 un proxy inverso que termine TLS o un túnel.
Las tres herramientas
Con el interruptor activo, el servidor gana tres herramientas de utilidad:
list_event_types— los tipos de evento que ofrece el servidor, más tus suscripciones actuales. Pregunta antes de adivinar nombres.subscribe_event— suscríbete con los argumentos propios del evento, qué hacer cuando llegue (instructions, hasta 2000 caracteres) y a dónde va el resultado de cada ejecución (deliver,deliver_chat_id). Cada suscripción la apruebas tú, porque una suscripción es un disparador permanente de ejecuciones sin supervisión. La aprobación muestra las instrucciones y el destino completos.unsubscribe_event— cancelar.
instructions es la parte sobre la que hay que pensar más. No es “avísame cuando pase X”; es “cuando pase X, haz esto”. Si lo escribes de forma vaga, tendrás un agente que se dispara cada vez y hace lo equivocado cada vez.
Por dentro: una ruta de webhook dinámica
Cada suscripción se guarda como una ruta de webhook dinámica — hermes webhook list la muestra. Lleva su propio secreto de firma whsec_, mientras el grant y el cursor de replay quedan al lado en mcp_event_subscriptions.json. La ruta en sí solo cambia cuando te vuelves a suscribir con instrucciones distintas o a otro destino.
Algunos detalles de ejecución que conviene conocer:
- El challenge de verificación se responde sin arrancar ninguna ejecución.
- La deduplicación es por
eventId, no por elwebhook-idde cada intento. Un servidor que reenvía el mismo evento dispara una sola ejecución. - Los grants se refrescan antes de que venza el tiempo de vida concedido por el servidor mientras la conexión está arriba, y otra vez cada vez que reconecta.
- Si el servidor pone fin a una suscripción, Hermes elimina la ruta y avisa al destino de
deliver. - Un refresco que el servidor rechaza de forma definitiva — acceso revocado, tipo de evento eliminado, argumentos ya inválidos — elimina la ruta; otros fallos reintentan con back-off.
El payload del evento llega entero a la ejecución, hasta 32.000 caracteres de JSON, con una nota cuando se recorta. Los payloads de eventos son datos externos, así que esas ejecuciones usan el mismo conjunto de herramientas de webhook restringido que cualquier otra ruta de webhook.
Cinco formas en que caduca sin hacer ruido
Esto no es suscribirse una vez y olvidarse. Las rutas de fallo conocidas:
- La conexión tiene que estar arriba. Con
lazy: trueoidle_timeout_seconds, la conexión no está siempre arriba, así que una suscripción puede caducar hasta que la siguiente conexión la vuelva a suscribir. - El gateway tiene que estar corriendo. Si
hermes gateway runno está arriba, no llega ningún evento — la razón más habitual de que una prueba parezca “la función está rota”. - Los fallos de refresco hacen back-off, pero un rechazo permanente borra la ruta.
- Los rate limits tiran eventos. El límite por ruta del listener (
platforms.webhook.extra.rate_limit, 30 por minuto por defecto) se aplica también a las entregas de eventos, y un servidor puede no reintentar una entrega rechazada. Súbelo para un tipo de evento con mucho tráfico. - Apagar la ruta en el dashboard de webhooks deja caducar la suscripción; volver a encenderla la vuelve a suscribir.
Lo que explícitamente no está soportado
La documentación es franca sobre los huecos, así que no pierdas tiempo ahí:
- Los modos de entrega
pollypushno están implementados — solo webhook. - Un perfil secundario bajo
gateway.multiplex_profilesno puede suscribirse. Las rutas dinámicas se cargan desde el home del perfil de lanzamiento.
Todo el diseño se apoya además en el sketch de diseño de eventos de MCP, que sigue siendo un borrador del grupo de trabajo. Por eso la función está etiquetada como experimental: la forma del protocolo puede cambiar.
Cuándo vale la pena
Si tu necesidad es “mira una página una vez por hora y comprueba si cambió”, un job de cron sigue siendo más simple: sin URL pública, y no caduca en silencio cuando el gateway se para.
Los eventos MCP compensan cuando aguas arriba ya existe un flujo de eventos y la latencia importa — tickets nuevos en un gestor de incidencias, un despliegue fallido, un nuevo elemento en un feed. En esos casos, “que te avisen” y “preguntar cada diez minutos” son experiencias distintas, y lo primero además elimina un montón de llamadas al modelo que no devolvían nada.
Si todavía estás sopesando push contra poll, nuestro recorrido por las notificaciones por webhook compara los dos. Y costes y límites de gasto en herramientas MCP cubre otro conjunto de claves nuevas en el mismo bloque mcp_servers que probablemente configurarás junto a estas.
Estado en las releases
La función está solo en main: fusionada en las primeras horas del 22 de octubre de 2026, no forma parte de la v0.22.4 publicada ni de ninguna etiqueta. Sigue main si quieres probarla, y cuenta con que la forma del protocolo se mueva antes de estabilizarse.