AI Blog

A dropped subscription looks exactly like a quiet week

OpenAI shipped plugin automations on all plans on 29 September 2026 against MCP Events — a draft with no SEP number, in a repository whose README calls its contents exploratory. The draft gets the webhook hardening right and makes the two envelopes that report absence optional, so a revoked permission, a lost buffer and a genuinely quiet upstream all reach your agent as the same empty stream.

By Agentic AI Wiki 14 min read

An agent that polls a tool gets an error when something breaks; an agent that subscribes to events gets silence, and silence is also what a quiet week looks like. At DevDay on 29 September 2026 OpenAI shipped plugin automations against MCP Events — a design sketch that still carries no SEP number and lives in a repository whose README calls its contents "exploratory and do not represent official MCP specifications or recommendations" — and the parts of that draft its integration does not consume are precisely the two envelopes that tell a client its stream ended rather than emptied. The draft defines them. It makes neither mandatory. That is the whole problem with event-driven agents in one design decision.

At a glance

Four ways an event stream goes quiet, and who is supposed to tell you which one happened.

CauseSignal in the draftStrengthIn the shipped integration
Subscriber's access revoked notifications/events/terminated Re-check is a SHOULD Not consumed
Server restart drops the buffer {"type":"gap"} envelope At-most-once for emit-only types Not consumed
Callback unreachable deliveryStatus on refresh Visible only at the next refresh Refresh exists
Genuinely nothing happened — none needed — — Indistinguishable from the above
Four reasons nothing arrives, and the envelope that would say which Four causes of an empty event stream converge on one indistinguishable outcome. Access revoked is signalled by a terminated envelope; a server restart dropping an emit-only buffer is at-most-once and signalled by a gap envelope; a failed webhook delivery surfaces only in the delivery status on the next refresh; and genuinely quiet upstream is indistinguishable from all three. Without the control envelopes the agent sees the same empty stream in every case. Cause Signal the draft defines What the agent sees Access revoked removed from the channel notifications/events/terminated delivery-time re-check is a SHOULD Server restart emit-only buffer lost {"type":"gap"} at-most-once across restarts Delivery failing callback unreachable, retried deliveryStatus visible only on the next refresh Nothing happened a genuinely quiet week — no signal needed — An empty stream identical in all four cases, with no error Drop the control envelopes and the first three collapse into the fourth. The draft defines all of them; it makes none of them mandatory, and the first large implementation consumes neither terminated nor gap.
Drop the control envelopes and the first three causes collapse into the fourth.

What shipped, and what it shipped against

MCP Events is a Triggers and Events Working Group document — leads Clare Liguori of AWS and Peter Alexander of Anthropic — whose charter was merged on 25 March 2026 and whose single deliverable, "SEP: Events in MCP v1 RFC", still reads Ideating with a target date of "End April" that has not been revised since. The design sketch itself is dated 19 February 2026 and marked "Draft proposal". There is no SEP file for it in the specification repository's seps/ directory, and the incubation repo carries the experimental- prefix rather than the ext- prefix that marks an official MCP extension. The August roadmap lists server-initiated events as current work and asks for a composition review across the Agents, Transports and Triggers groups — which is to say the primitive is not finished and its interaction with Tasks is an open question.

OpenAI's own announcement wording is careful and correct: support for "the proposed MCP Events specification". The availability line is not careful in the same way — all plans. So a proposal with no SEP acquired, in one afternoon, the largest client implementation it will ever have, and the working group that owns it meets weekly on a thirty-minute cadence.

This is not an argument that OpenAI should have waited. Specs without implementations do not converge either, and the integration faithfully implements the webhook half. It is an argument about what happens next: the optional parts of a draft, once a dominant client has skipped them, stop being optional in the direction you would hope.

Three delivery modes, and the load-bearing sentence is "none is mandatory"

The draft specifies poll (events/poll), push (events/stream) and webhook (events/subscribe), advertised per event type, and it is explicit about the cost: "This goes against MCP's usual stance of offering one way to do a thing, and the cost is real: roughly 3× spec and SDK surface." None of the three is required of a server.

The three MCP Events delivery modes Three horizontal lanes between an MCP server and an agent client. Poll uses repeated request and response carrying a cursor. Push holds a long-lived stream with heartbeats. Webhook registers an HTTPS callback with a client-supplied secret and the server posts each event to it. The draft marks none of the three as mandatory; the ChatGPT integration implements only the webhook lane. MCP server Agent client upstream Slack, GitHub, PagerDuty… events/list SDK loop negotiates mode, model sees only arriving events Poll — events/poll request with cursor events[], cursor, nextPollMs Push — events/stream notifications/events/event heartbeat carries the cursor while quiet Webhook — events/subscribe signed POST to an https callback → client supplies the whsec_ secret; the only mode ChatGPT implements The draft advertises delivery modes per event type and marks none of them mandatory — so two servers can both conform and share no way to talk to each other.
Webhook is the lane the shipped integration implements; poll and push are not supported by it.

Optionality at the mode level has a direct consequence for anyone building a server: conformance is not interoperability. A server that implements only poll and a client that implements only webhook can both be correct and still have nothing to say to each other. Today that resolves to a de facto requirement — implement webhook, because that is what the largest client speaks, which also means persistent subscription storage and an outbound HTTPS path from your server, neither of which a tools-only MCP server needed.

Note which way the authorisation runs. Webhook subscriptions must be made by an authenticated principal — a server must reject an unauthenticated events/subscribe with -32012 Forbidden — and the subscription key is (principal, delivery.url, name, arguments). The client supplies the signing secret. Poll and push, by contrast, are available to unauthenticated servers. So the mode choice is also a choice about whether there is a principal behind the stream at all.

Silence is the failure mode, and the draft knows it

The draft's own summary states the point plainly: events let a client subscribe and "have the agent react when they occur, without the user being present". Nobody is waiting on the result, so nobody notices its absence. Four things then produce an empty stream.

The first is revocation. Authorisation is checked at subscribe time as a MUST; after that, "the server SHOULD periodically re-verify permissions", and when access is lost the subscription is terminated and a terminated envelope carries -32012 Forbidden with a reason. Note the modal verbs: the initial check is required, the ongoing check is advisory, and the notification is a message your client has to be listening for. A client that ignores it learns at its next refresh, because events/subscribe doubles as the refresh call — and the draft lets a server grant refreshBefore: null, meaning no expiry, at which point the SDK's refresh loop "drops to an occasional health-check cadence". Your agent's belief that it is still subscribed is only ever as fresh as its last refresh, and the draft permits that interval to be very long.

The second is a restart. Guarantees are at-least-once only "when the cursor is backed by a durable upstream"; for emit-only event types the draft is blunt — "at-most-once across server restarts — the in-memory buffer and its cursors do not survive". The third is delivery failure: webhook retries are independent per event, so arrivals reorder, and the client's view of failure is the deliveryStatus field it reads at the next refresh. The fourth is that nothing happened.

And the draft is honest about the floor: "This design intentionally does not provide protocol-level guarantees around event ordering, exactly-once delivery, or transactional consistency. There are no logical clocks, sequence numbers, or cross-subscription ordering constraints." Exactly-once requires application-level deduplication on eventId. That is a defensible choice — Stripe, GitHub and Shopify all ship it — but it lands on a consumer that is an agent loop, which is the least idempotent component in anybody's stack. A duplicate webhook does not re-render a page; it re-runs a plan.

There is already a field report of exactly this class of blindness in the incubation repo, filed on 24 August 2026 against long-polling: a 30-day pilot, 74 successful calls, median 45.9 seconds and p95 241.7 seconds, with 22 of them past 60 seconds. The author's stated show-stopper is one sentence — "Client abandonment is invisible. All 22 calls past 60 s were recorded ok." The server could not tell that nobody was listening any more. That is the same problem as the four silences, arriving from the other end.

The webhook plumbing is the part they got right

It is worth being precise about where the criticism does and does not apply, because the security engineering in this draft is better than the discourse around server-initiated events would suggest. Compared with A2A's push notifications — the nearest prior art, where an agent POSTs task updates to a client-provided webhook with credentials from a configured authentication field — MCP Events is stricter on every axis.

What each implementation covers Matrix comparing the MCP Events draft, the ChatGPT plugin integration and A2A push notifications across eight axes. The draft covers all three delivery modes and both control envelopes and specifies endpoint verification, mandatory HTTPS and a replay window. The ChatGPT integration covers webhook delivery and endpoint verification only. A2A push covers webhook delivery with authenticated headers but specifies no endpoint handshake and no replay window. Coverage by axis — draft, shipped integration, and the nearest prior art MCP EVENTS DRAFT CHATGPT PLUGINS A2A PUSH Webhook delivery Specified Implemented Specified Poll and push modes Both Neither Not in scope The “terminated” envelope Defined, optional Not consumed None The “gap” envelope Defined, optional Not consumed None Endpoint verification handshake Mandatory Implemented Unspecified HTTPS for callbacks MUST Required SHOULD Replay window on delivery 5 minutes Signed timestamp Unspecified Ordering / exactly-once Explicitly out Caller's problem Caller's problem Covered Partial or optional Absent
Hardening is covered; the control plane that reports absence is the optional column.

The callback URL must be https — a MUST, where A2A says SHOULD. Delivery carries a Standard Webhooks HMAC signature with a client-supplied whsec_ secret, so the server never mints the credential that authenticates its own deliveries. There is a mandatory endpoint-verification handshake before delivery activates, cached per (principal, url), specifically to stop a subscription being used as a reflection or amplification primitive against a third party — A2A specifies no equivalent. Replay is bounded by a webhook-timestamp freshness window the draft puts at five minutes, plus eventId deduplication and a single-use nonce. SSRF defence is delivery-time IP validation against the IANA special-purpose registries, connecting to the validated IP with the original Host and SNI, and no redirects.

The draft also closes the door the obvious way on the question everyone asks first. Event receipt "does NOT constitute authorization to act" — the agent's response goes through normal MCP authorisation — and payloads are "untrusted data with the same injection considerations as tool results". There is deliberately no token pass-through, no client-credentials grant and no OIDC identity token on a tenant's behalf. Whatever you think about event-driven agents, the people writing this have read the threat model.

Which is what makes the omission interesting. The hard security problems got MUSTs. The problem of telling a client that its stream ended got an optional envelope, and optional envelopes do not get implemented, because nobody files a bug about an event that never arrived.

What to change this week

If you are building an MCP server that will carry events, or wiring an agent to consume them:

  • Treat absence as an alertable condition, not a quiet state. Record an expected-arrival rate per subscription and alarm when the stream goes flat for longer than that rate predicts. This is the same discipline as watching for absence in scheduled and triggered agents, and it is the only thing that works regardless of which envelopes your client consumes.
  • Re-verify at delivery time even though it is a SHOULD. If you are the server, check the principal's permission on the way out, not only at subscribe. The alternative is delivering a Slack channel's messages to an agent whose user left the channel in March.
  • Make the event handler idempotent on eventId before you ship it. Not after the first duplicate. The draft hands you at-least-once at best and at-most-once for emit-only types; both of those break an agent loop in ways a web backend absorbs silently — idempotency and retries.
  • Bound what an event may cause. A trigger with no requester in the room is the configuration with the widest gap between intent and effect, so cap spend, scope writes, and keep reversibility in the path. Blast radius, and ambient authority for where the extra reach comes from.
  • Pin the protocol version and expect it to move. The integration requires 2026-07-28; the events surface itself is pre-SEP and the working group meets weekly. Write the version into your server's contract tests rather than your memory — the 2026-07-28 revision, and protocol revisions and deprecation windows.
  • Log the subscription as a grant. It is derived from a permission check that happened once, it refreshes itself, and it can outlive the project that asked for it. Put it in the registry next to the credentials — access reviews for agent credentials.

FAQ

Is MCP Events part of the MCP specification?

No. As of 1 October 2026 there is no SEP file for it in the specification repository, the working group's charter lists its RFC as "Ideating", the design sketch is marked "Draft proposal", and the incubation repository's README states that its contents "are exploratory and do not represent official MCP specifications or recommendations". OpenAI's own wording — "the proposed MCP Events specification" — is accurate.

Does a revoked permission keep delivering events?

Not if the server behaves as the draft intends: it terminates the subscription when it detects the loss. The gap is on the client's side of the boundary. The delivery-time re-verification is a SHOULD rather than a MUST, and the notification that the subscription ended is an envelope a client has to consume — so the realistic failure is an agent that believes it is still watching something and is not, for however long its refresh interval is.

Why does webhook-only matter if webhook works?

Because it changes what an MCP server is. Tools-only servers answer requests; a webhook publisher needs persistent subscription storage, an outbound HTTPS path, signature generation and a verification handshake. That is a stateful service with a delivery SLA, which is a different operational commitment from the one most MCP servers were built under — MCP ops in production.

How is this different from A2A push notifications?

A2A notifies about a task the client itself created, so there is always a prior user action behind the stream. MCP Events subscribes to an upstream third-party system with, in the draft's own words, the user not present. The webhook mechanics are similar and MCP's are stricter; the new risk is the missing requester, not the transport.

Should I wait for the SEP before building against it?

Build the server if the use case is real — the webhook surface is small and the hardening requirements are clear. Do not build business logic that assumes ordering, exactly-once delivery or a reliable termination signal, because the draft explicitly declines to provide the first two and makes the third optional. Those are the parts most likely to change, and the parts your correctness should not depend on either way.

Further reading

On this wiki:

Sources: