MCP Events ermöglichen Ihrem MCP-Server, Updates an ChatGPT zu pushen, sobald sie geschehen, sodass kein Agent sie abfragen muss. Seit dem OpenAI DevDay am 29. September 2026 unterstützt ChatGPT die vorgeschlagene MCP Events Spezifikation in Protokollversion 2026-07-28 (MCP 2.0) auf allen Plänen. Ihr Server fügt drei Methoden hinzu (events/list, events/subscribe, events/unsubscribe), bewirbt events in server/discover, besteht eine Callback-Verifizierungsanfrage und signiert jede Lieferung mit Standard Webhooks HMAC. ChatGPT akzeptiert ausschließlich Webhook-Zustellung.
Dieser Leitfaden behandelt jede Nachrichtenform, die OpenAI dokumentiert, die Sicherheitsregeln, die Ihr Server durchsetzt, und einen Testlauf für die gesamte Schleife. Neu im Protokoll? Beginnen Sie mit was MCP ist. Sie senden die JSON-RPC-Aufrufe und simulieren den Callback in Apidog.
MCP Events auf einen Blick
| Punkt | Was ChatGPT erwartet |
|---|---|
| Protokoll | MCP 2.0, Version 2026-07-28 |
| Fähigkeit | "events": {} in den server/discover Capabilities |
| Methoden | events/list, events/subscribe, events/unsubscribe, am selben authentifizierten Endpunkt wie Ihre Tools |
| Zustellung | Nur Webhook: kein Polling, Streaming, gap oder terminated Benachrichtigungen |
| Signierung | Standard Webhooks HMAC-SHA256 |
| Header | webhook-id, webhook-timestamp, webhook-signature, X-MCP-Subscription-Id |
| Geheimnis | whsec_ plus Base64, das zu 24-64 Bytes dekodiert wird, von ChatGPT bereitgestellt |
| Payload-Limit | 256 KiB (262.144 Bytes), ein Event pro Anfrage |
| Abonnement-ID | Deterministisch: Principal, Callback-URL, Event-Name, Argumente |
| Callbacks | HTTPS, Challenge-verifiziert, keine privaten Adressen, keine Weiterleitungen |
Quellen: OpenAIs MCP Events Leitfaden und der Entwurf MCP Events Design Sketch.
Warum Events statt Polling
Ohne Events ruft ein Agent, der auf Überprüfungskommentare wartet, ein Tool nach einem Timer auf und vergleicht die Ergebnisse, verbraucht Anfragen, wenn sich nichts geändert hat, und hinkt hinterher, wenn sich etwas geändert hat. MCP Events kehren die Richtung um: Ihr Server weiß, wann sich ein Datensatz ändert, und sendet dann das Update. Die Kompromisse sind die klassischen Webhooks vs. Polling, jetzt innerhalb von MCP.
Der DevDay-Rückblick von OpenAI verwendet ein Projektboard: Der Benutzer bittet ChatGPT, nach neuen Aufgaben zu suchen, und wenn eine eintrifft, liest ChatGPT die verknüpften Dokumente und entwirft einen Plan. Die Dokumente fügen zwei weitere Fälle hinzu: Bug-Reports in einem Kanal in Entwurf-Pull-Requests umwandeln (message.created, gefiltert nach channel_id) und Überprüfungskommentare auf ein Dokument anwenden (comment.created, gefiltert nach document_id).
Die Spezifikation stammt von der MCP Triggers and Events Working Group (Repo) und ist als experimentell gekennzeichnet, pinnen Sie Ihren Code daher an 2026-07-28. Den Rest der Einführung finden Sie im DevDay 2026 Hub.
Events bewerben und definieren
Fügen Sie events zu den Fähigkeiten in Ihrer server/discover Antwort hinzu:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"resultType": "complete",
"supportedVersions": ["2026-07-28"],
"capabilities": { "tools": {}, "events": {} }
}
}
Geben Sie dann Definitionen von events/list zurück. Jede trägt einen name, eine description, delivery: ["webhook"], ein inputSchema für Abonnementargumente (Ihre Filter, wie z.B. document_id) und ein payloadSchema für das data-Objekt in jeder Lieferung. Verwenden Sie stabile Namen und spezifische Beschreibungen, wenden Sie Filter auf Ihrem Server an und listen Sie nur Events auf, die das verbundene Konto sehen darf.
events/subscribe: Anfrage und Antwort
Wenn ein Benutzer ChatGPT bittet, etwas zu überwachen, ruft ChatGPT events/subscribe auf:
{
"jsonrpc": "2.0",
"id": 2,
"method": "events/subscribe",
"params": {
"name": "comment.created",
"arguments": { "document_id": "doc_123" },
"delivery": {
"mode": "webhook",
"url": "https://receiver.example.com/mcp-events/callback_123",
"secret": "whsec_<base64-encoded-signing-key>"
},
"cursor": null
}
}
Bevor Sie die Anfrage akzeptieren, autorisieren Sie den Benutzer für dieses Event und diese Argumente, validieren Sie beides anhand Ihrer Definition, fordern Sie ein whsec_-Geheimnis an, dessen Base64 zu 24-64 Bytes dekodiert wird, verifizieren Sie den Callback (nächster Abschnitt) und speichern Sie das Abonnement mit seinem Besitzer, Filtern, URL, Geheimnis und Ablaufdatum. Antworten Sie dann:
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"id": "sub_123",
"refreshBefore": "2026-10-02T12:00:00Z",
"cursor": null,
"truncated": false
}
}
Drei Regeln sorgen dafür, dass Abonnements korrekt bleiben:
- Deterministische IDs. Leiten Sie die
idvom authentifizierten Prinzipal, der Callback-URL, dem Event-Namen und den Argumenten ab. Der Designentwurf schlägt einen gekürzten SHA-256 dieses Schlüssels vor. - Idempotente Upserts. Ein wiederholtes Abonnement mit derselben Identität aktualisiert den bestehenden Datensatz. Vergleichen Sie Argumente als kanonisches JSON, damit die Schlüsselreihenfolge keine Duplikate erzeugen kann.
- Aktualisieren Sie vor
refreshBefore. ChatGPT ruftevents/subscribevor dieser Zeit mit derselben Identität und seinem zuletzt gespeicherten Cursor erneut auf; Sie geben ein neues Ablaufdatum zurück. Wenn eine Aktualisierung ein neues Geheimnis mit sich bringt, ersetzen Sie es und signieren Sie für ein kurzes Zeitfenster mit beiden Schlüsseln. Geben Siecursor: nullfür Events zurück, die Sie nicht wiederholen können.
Verifizieren Sie den Callback, bevor Sie Daten senden
Bevor Sie Anwendungsdaten senden, POSTen Sie einen signierten Verifizierungstext mit einer neuen, einmaligen, kurzlebigen Challenge:
{ "type": "verification", "challenge": "a-single-use-random-value" }
Geben Sie ihm eine eindeutige webhook-id (zum Beispiel msg_verification_123) und dieselben Signatur-Header wie eine Lieferung. ChatGPT wiederholt {"challenge": "a-single-use-random-value"} mit einem 2xx. Vergleichen Sie in konstanter Zeit und aktivieren Sie dann die Lieferung. Im Fehlerfall geben Sie den JSON-RPC-Fehler -32015 (CallbackEndpointError) mit einem data.reason wie challenge_failed oder timeout zurück. Zwischenspeichern Sie einen Pass pro Prinzipal und URL für einen begrenzten Zeitraum, damit Aktualisierungen keine erneute Challenge auslösen.
Die Challenge existiert, weil der Abonnent das Geheimnis liefert: Ohne sie könnte jeder Ihren Server auf die URL eines Opfers richten und es überfluten. Wenden Sie dieselbe Härtung auf jede ausgehende Anfrage an: nur HTTPS, Adresse zum Zeitpunkt der Verbindung auflösen und validieren, private, lokale und andere nicht-öffentliche Adressen blockieren und niemals Weiterleitungen folgen.
Events liefern und signieren
Wenn ein passendes Event auftritt, POSTen Sie ein Event-Objekt an die Callback-URL:
{
"eventId": "evt_456",
"name": "comment.created",
"timestamp": "2026-10-01T12:05:00Z",
"data": {
"document_id": "doc_123",
"comment_id": "comment_456",
"text": "Can we add the rollout dates to this section?",
"url": "https://docs.example.com/doc_123#comment_456"
},
"cursor": null
}
Senden Sie Content-Type: application/json, webhook-id (gleich eventId), webhook-timestamp (Unix-Sekunden bei der Signierung), webhook-signature und X-MCP-Subscription-Id. Serialisieren Sie den Body einmal und senden Sie genau diese Bytes, da die Signatur diese abdeckt.
Bleiben Sie unter 256 KiB; für große Datensätze senden Sie eine Zusammenfassung und legen ein Lese-Tool offen. Behandeln Sie vom Benutzer verfassten Text als Daten und fügen Sie niemals Modell-Anweisungen in eine Payload ein. Versuchen Sie vorübergehende Fehler mit einer begrenzten exponentiellen Backoff-Strategie erneut, wobei die Event-ID beibehalten und jeder Versuch neu signiert wird. Wiederholen Sie keine 410 oder 413. Events können außer der Reihe eintreffen, daher sollten Schreib-Tools idempotent sein. Der Leitfaden für zuverlässiges Webhook-Design behandelt Wiederholungszeitpläne.
Eine Standard Webhooks-Signatur verifizieren
Gemäß der Standard Webhooks Spezifikation ist der signierte Inhalt ${webhook-id}.${webhook-timestamp}.${body}, der HMAC-SHA256-Schlüssel ist das base64-dekodierte Geheimnis nach dem Entfernen von whsec_, und der Header enthält eine oder mehrere durch Leerzeichen getrennte v1,<base64> Signaturen. Der MCP-Entwurf weist Empfänger an, Zeitstempel abzulehnen, die älter als 5 Minuten sind, und nach webhook-id zu deduplizieren.
ChatGPT verifiziert auf seiner Seite, aber ein strenger eigener Empfänger ist ein guter lokaler Test-Orakel. Dieser verwendet nur Node-Built-ins:
// receiver.mjs: strict Standard Webhooks receiver for local tests (Node 18+)
import { createServer } from "node:http";
import { createHmac, timingSafeEqual } from "node:crypto";
const SECRET = process.env.WEBHOOK_SECRET; // whsec_...
const MAX_BYTES = 256 * 1024;
const TOLERANCE_S = 5 * 60;
const seen = new Set();
export function verify(raw, h, secret, now = Math.floor(Date.now() / 1000)) {
const id = h["webhook-id"], ts = h["webhook-timestamp"], sigs = h["webhook-signature"];
if (!id || !ts || !sigs) return false;
const t = Number(ts);
if (!Number.isInteger(t) || Math.abs(now - t) > TOLERANCE_S) return false;
const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64");
const expected = createHmac("sha256", key).update(`${id}.${ts}.`).update(raw).digest();
return sigs.split(" ").some((s) => {
const [version, b64] = s.split(",");
const got = Buffer.from(b64 ?? "", "base64");
return version === "v1" && got.length === expected.length && timingSafeEqual(got, expected);
});
}
if (SECRET) createServer((req, res) => {
const chunks = [];
let size = 0;
req.on("data", (c) => { size += c.length; if (size <= MAX_BYTES) chunks.push(c); });
req.on("end", () => {
if (size > MAX_BYTES) return res.writeHead(413).end();
const raw = Buffer.concat(chunks);
if (!verify(raw, req.headers, SECRET)) return res.writeHead(401).end();
let body;
try { body = JSON.parse(raw); } catch { return res.writeHead(400).end(); }
if (body.type === "verification") {
res.writeHead(200, { "Content-Type": "application/json" });
return res.end(JSON.stringify({ challenge: body.challenge }));
}
const id = req.headers["webhook-id"];
if (!seen.has(id)) { seen.add(id); console.log(req.headers["x-mcp-subscription-id"], body.name, id); }
res.writeHead(200).end();
});
}).listen(8787);
Führen Sie es mit WEBHOOK_SECRET=whsec_... node receiver.mjs aus. Es gibt 413 bei über 256 KiB und 401 für eine schlechte oder veraltete Signatur zurück, wiederholt Challenges und protokolliert jede webhook-id einmal. Wir haben verify() gegen den Signaturtestvektor in der Standard Webhooks JavaScript-Bibliothek geprüft. Für die Theorie lesen Sie Webhook-Signatur-Verifizierung.
Ihr Server blockiert private Adressen, daher liefert er standardmäßig nicht an localhost. Der Entwurf erlaubt nicht-öffentliche Ziele nur, wenn sie explizit konfiguriert sind: Fügen Sie einen Nur-Entwickler-Zulassungslisteneintrag hinzu oder legen Sie den Empfänger über einen Tunnel frei.
Testen Sie es, bevor ChatGPT es tut
Hier ist ein Apidog Testlauf für die Fehlerfälle, die OpenAI auflistet. Erstellen Sie eine Umgebung mit MCP_URL, MCP_TOKEN, CALLBACK_URL und WEBHOOK_SECRET und senden Sie jeden JSON-RPC-Aufruf als POST an {{MCP_URL}} mit Authorization: Bearer {{MCP_TOKEN}} plus den Headern, die die Streamable HTTP-Bindung erfordert: Accept: application/json, text/event-stream, MCP-Protocol-Version: 2026-07-28 und Mcp-Method, gesetzt auf die Methode. Jeder Body benötigt auch params._meta mit io.modelcontextprotocol/protocolVersion und io.modelcontextprotocol/clientCapabilities. OpenAIs Beispiele lassen dies weg, und ein fehlendes _meta gibt ebenfalls -32602 zurück, so dass Schritt 3 aus dem falschen Grund bestehen würde.
- Erkennung. Senden Sie
server/discoverund bestätigen Sie, dass$.result.capabilities.eventsexistiert und$.result.supportedVersions2026-07-28enthält. Senden Sieevents/listund bestätigen Sie, dass diedeliveryjedes Eventswebhookenthält. - Idempotente Abonnement. Senden Sie den oben genannten Abonnement-Body unter Verwendung von
{{CALLBACK_URL}}und{{WEBHOOK_SECRET}}, mit einem Extraktions-Variablen-Postprozessor, der$.result.idalsSUB_IDspeichert. Senden Sie ihn unverändert erneut, dann mit neu angeordnetenarguments-Schlüsseln, und bestätigen Sie, dass$.result.idbeide Male{{SUB_ID}}entspricht. - Validierung. Senden Sie ein Geheimnis, das zu weniger als 24 Bytes dekodiert, dann einen
http://-Callback. Der Entwurf ordnet beides-32602(InvalidParams) zu; bestätigen Sie dies auf$.error.code. Ein privater IP-Callback muss ebenfalls fehlschlagen. - Verifizierungs-Challenge. Erstellen Sie einen Apidog-Mock-Endpunkt, der mit
{"challenge": "wrong"}antwortet, und abonnieren Sie mit seiner Cloud-Mock-URL als Callback. Bestätigen Sie, dass$.error.code-32015ist und$.error.data.reasonchallenge_failedist. Richten Sie dannCALLBACK_URLauf Ihren Empfänger und bestätigen Sie, dass das Abonnement erfolgreich ist. - Übergröße Payload. Lösen Sie ein Event aus, dessen Body 262.144 Bytes überschreitet. Ihr Sender sollte es ablehnen; falls doch eines durchrutscht, antwortet der Empfänger mit
413, und Ihre Serverprotokolle sollten einen Versuch zeigen, keine Wiederholung. - Wiederholung und Manipulation. Kopieren Sie die Header und den Body einer signierten Lieferung aus dem ausgehenden Protokoll Ihres Servers in eine neue Apidog-Anfrage. Senden Sie sie sofort erneut:
200, und keine zweite Protokollzeile. Ändern Sie ein Body-Byte:401. Senden Sie nach 5 Minuten erneut:401.
Speichern Sie die Schritte als Szenario und führen Sie sie in CI mit der Apidog CLI aus. Das MCP-Server-Test-Playbook behandelt Tool-Aufrufe, und wie man Webhooks testet geht tiefer auf Empfänger ein. Verbinden Sie dann den Server über ein Plugin mit ChatGPT und führen Sie OpenAIs Lebenszyklus-Checkliste aus.
FAQ
Was sind MCP Events? Eine experimentelle MCP-Erweiterung, die es einem Server ermöglicht, Ereignisbenachrichtigungen an einen Client zu pushen, anstatt dass der Client sie abfragt. ChatGPT unterstützt den Webhook-Modus unter 2026-07-28.
Unterstützt ChatGPT Polling oder Streaming für MCP Events? Nein. ChatGPT unterstützt ausschließlich Webhook-Zustellung und Callback-Verifizierung. Polling, Streaming sowie die Benachrichtigungen gap und terminated werden nicht unterstützt.
Welche ChatGPT-Pläne erhalten MCP Events? OpenAIs DevDay-Rückblick besagt, dass die Funktion allen Plänen zur Verfügung steht.
Wer erstellt das Signiergeheimnis? Der Abonnent. ChatGPT sendet ein whsec_-Geheimnis in delivery.secret; Ihr Server validiert, speichert und signiert damit und generiert niemals ein eigenes.
Worin unterscheidet sich dies von der Agents API? MCP Events pushen Daten von Ihrem Server an ChatGPT. Die OpenAI Agents API führt von Ihnen erstellte Agenten aus und meldet deren Fortschritt per Streaming oder Webhooks.
Nächster Schritt
Fügen Sie "events": {} zu einem Server hinzu, legen Sie ein Event mit einem Filter frei und führen Sie die sechs Prüfungen an einem lokalen Empfänger durch, bevor Sie ChatGPT verbinden. Laden Sie Apidog herunter, um diese Prüfungen als Szenario zu speichern, das bei jedem Commit ausgeführt wird.
