Ein MCP-Server ruft dich an: der experimentelle Events-Client und seine Webhook-Zustellung


Du hast eine Monitoring-Schleife geschrieben, die den Agent alle zehn Minuten fragen lässt: „Neue Tickets?“ Meistens lautet die Antwort nein. Du hast zehn Modellaufrufe bezahlt, um zehn Neins zu bekommen, und das eine Ticket, auf das es ankam, wartete bis zu zehn Minuten, bis es jemand bemerkte.

Die MCP-Erweiterung events existiert genau für diese Art Problem: Der Server pusht ein Event in dem Moment, in dem etwas passiert, statt dass der Agent immer wieder fragt. Hermes implementiert jetzt ihren Webhook-Zustellmodus — PR #140121, gemergt am 22. Oktober 2026. Das Ganze ist experimentell und lebt auf main, aber der Pfad ist vollständig: abonnieren, den Verifizierungs-Handshake beantworten und ein Event einen echten Agent-Run starten lassen.

Warum das bisher unmöglich war

Hermes hatte die meisten Teile bereits. Der Webhook-Listener des Gateways prüft whsec_-Signaturen nach Standard Webhooks und lädt dynamische Routen, und diese Routen können Ergebnisse überallhin zustellen. Was fehlte, war die MCP-Seite: events/list, events/subscribe, events/unsubscribe und eine Refresh-Schleife, die Grants am Leben hält.

Ohne sie scheitert der Versuch, eine Webhook-Route von Hand bei einem Events-Server zu registrieren. Der Server POSTet eine signierte {"type":"verification"}-Payload; Hermes behandelte sie als gewöhnliches Event, startete einen Run, antwortete 202 und gab das Challenge nie zurück. Der Server befand den Callback für unbrauchbar, lieferte challenge_failed und das Abo war tot.

Drei Schritte zum Einschalten

mcp_servers:
  ambush:
    url: "https://example.com/mcp"
    events: true          # registriert die Event-Tools für diesen Server

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

Drei Dinge müssen alle zutreffen:

  1. events: true gilt pro Server. Ohne das erscheinen die drei Event-Tools nie.
  2. Die Webhook-Plattform muss aktiviert sein — so kommen die Events an.
  3. public_url muss HTTPS sein. Server lehnen reine HTTP-Callbacks ab, also stell einen TLS-terminierenden Reverse-Proxy oder einen Tunnel vor Port 8644.

Die drei Tools

Mit eingeschaltetem Schalter bekommt der Server drei Utility-Tools:

  • list_event_types — die Event-Typen, die der Server anbietet, plus deine aktuellen Abos. Frag nach, bevor du Namen rätst.
  • subscribe_event — abonnieren mit den Argumenten des Events selbst, der Angabe, was bei Eintreffen passieren soll (instructions, bis 2000 Zeichen), und wohin das Ergebnis jedes Runs geht (deliver, deliver_chat_id). Jedes Abo wird von dir genehmigt, denn ein Abo ist ein dauerhafter Auslöser für unbeaufsichtigte Runs. Die Genehmigung zeigt die Instructions und das Ziel vollständig.
  • unsubscribe_event — kündigen.

instructions ist der Teil, über den man am längsten nachdenken sollte. Es ist nicht „benachrichtige mich, wenn X passiert“, sondern „wenn X passiert, tu das“. Schreib es vage, und du bekommst einen Agent, der jedes Mal feuert und jedes Mal das Falsche tut.

Darunter: eine dynamische Webhook-Route

Jedes Abo wird als dynamische Webhook-Route gespeichert — hermes webhook list zeigt sie. Sie trägt ihr eigenes whsec_-Signing-Secret, während der Grant und der Replay-Cursor daneben in mcp_event_subscriptions.json liegen. Die Route selbst ändert sich nur, wenn du mit anderen Instructions oder einem anderen Ziel neu abonnierst.

Ein paar Laufzeitdetails, die man kennen sollte:

  • Die Verifizierungs-Challenge wird beantwortet, ohne einen Run zu starten.
  • Dedupliziert wird über eventId, nicht über die webhook-id des einzelnen Versuchs. Schickt ein Server dasselbe Event erneut, startet ein Run.
  • Grants werden erneuert, bevor die vom Server gewährte Laufzeit abläuft, solange die Verbindung steht — und erneut bei jedem Reconnect.
  • Beendet der Server ein Abo, entfernt Hermes die Route und informiert das deliver-Ziel.
  • Ein Refresh, den der Server endgültig ablehnt — Zugriff entzogen, Event-Typ entfernt, Argumente nicht mehr gültig — entfernt die Route; andere Fehler werden mit Back-off wiederholt.

Die Event-Payload erreicht den Run vollständig, bis 32.000 Zeichen JSON, mit einem Hinweis, wenn sie gekürzt wurde. Event-Payloads sind externe Daten, deshalb nutzen diese Runs dasselbe eingeschränkte Webhook-Toolset wie jede andere Webhook-Route.

Fünf Wege, auf denen es still verfällt

Das ist kein Einmal-Abonnieren-und-vergessen. Die bekannten Fehlerpfade:

  1. Die Verbindung muss stehen. Mit lazy: true oder idle_timeout_seconds steht sie nicht immer, ein Abo kann also verfallen, bis die nächste Verbindung es neu abonniert.
  2. Das Gateway muss laufen. Wenn hermes gateway run nicht läuft, kommt kein Event an — der häufigste Grund, warum ein Test wie „das Feature ist kaputt“ aussieht.
  3. Refresh-Fehler gehen in Back-off, aber eine endgültige Ablehnung löscht die Route.
  4. Rate-Limits lassen Events fallen. Das Limit pro Route im Listener (platforms.webhook.extra.rate_limit, standardmäßig 30 pro Minute) gilt auch für Event-Zustellungen, und ein Server wiederholt eine abgelehnte Zustellung möglicherweise nicht. Erhöhe es für einen stark frequentierten Event-Typ.
  5. Die Route im Webhook-Dashboard abzuschalten lässt das Abo verfallen; wieder einzuschalten abonniert neu.

Was ausdrücklich nicht unterstützt wird

Die Docs sind bei den Lücken deutlich, also verschwende dort keine Zeit:

  • Die Zustellmodi poll und push sind nicht implementiert — nur Webhook.
  • Ein sekundäres Profile unter gateway.multiplex_profiles kann nicht abonnieren. Dynamische Routen laden aus dem Home des Start-Profils.

Das ganze Design stützt sich außerdem auf die MCP-Events-Design-Skizze, die noch ein Entwurf der Working Group ist. Deshalb ist das Feature als experimentell markiert: Die Protokollform kann sich ändern.

Wann es sich lohnt

Wenn dein Bedarf lautet „schau einmal pro Stunde auf eine Seite und prüfe, ob sie sich geändert hat“, ist ein Cron-Job weiterhin einfacher: keine öffentliche URL, und er verfällt nicht leise, wenn das Gateway stoppt.

MCP-Events zahlen sich aus, wenn oben bereits ein Event-Stream existiert und Latenz zählt — neue Tickets in einem Issue-Tracker, ein fehlgeschlagenes Deployment, ein neuer Eintrag in einem Feed. In diesen Fällen sind „informiert werden“ und „alle zehn Minuten fragen“ unterschiedliche Erfahrungen, und ersteres spart zusätzlich einen Haufen Modellaufrufe, die nichts geliefert haben.

Wenn du noch Push gegen Poll abwägst, vergleicht unser Leitfaden zu Webhook-Benachrichtigungen die beiden. Und MCP-Tool-Kosten und Spend-Limits behandelt einen weiteren Satz neuer Keys im selben mcp_servers-Block, die du hier vermutlich gleich mitkonfigurierst.

Release-Status

Das Feature ist nur auf main: gemergt in den frühen Stunden des 22. Oktober 2026, nicht Teil des ausgelieferten v0.22.4 und in keinem Tag. Folge main, wenn du es ausprobieren willst, und erwarte, dass sich die Protokollform noch bewegt, bevor sie stabil ist.