Back to Library
MCP

MCP Events: When a Subscription Becomes a Credential You Cannot Revoke

Last updated: October 3, 2026

Key takeaways

  • MCP Events adds a third wake-up trigger for agents — beyond cron schedules and human messages, an MCP server can now push a signed webhook to ChatGPT when something changes in a connected app.
  • The subscription outlives the access token that created it — ttlMs: null requests a subscription with no expiry, and the tuple that keys it carries no token, scopes, or expiry.
  • ChatGPT supports the webhook delivery mode but not the draft's terminated revocation envelope — the only remaining revocation signal is a failed refresh returning -32012 Forbidden.
  • The working group's own success criterion — a filed SEP — has not shipped — the charter table still reads "Ideating" with champion "TBD" and a single changelog entry from March 24, 2026.
  • WorkOS frames the subscription as a credential — a durable record, created under one user's access token, that authorizes your server to push that user's data at an agent long after the token expires.

Until now, an AI agent woke up for one of two reasons: a cron schedule fired, or a human typed a message. On September 29, 2026, at DevDay, OpenAI said it is adding support for the proposed MCP Events specification, so plugins can start automations when something happens in a connected app. The documentation describes how an MCP server can push updates to ChatGPT — a message.created event filtered by channel, or a comment.created event filtered by document — so an agent acts when a bug report arrives or a review comment is posted, not when you remember to ask. Nathan Baschez, who works on product design at Notion, posted on October 3 that he had not seen near enough excitement about it: "Event-driven triggers are a huge deal."

This article maps what MCP Events actually implements, the credential-lifetime gap at its center, and what a production MCP server must store and verify before it ships a webhook. It builds on MCP 2026-07-28: What the Stateless Protocol Means for B2B Agent Deployments, which covered the stateless core; here we focus on the event-driven extension and the subscription-lifecycle problem the working group has not finished specifying.

What ChatGPT actually implements

The MCP Triggers and Events Working Group charter lists one active work item: "SEP: Events in MCP v1 RFC," status "Ideating," target date "End April," champion "TBD." The charter has a single changelog entry, dated 2026-03-24: "Initial charter." The working group is led by Clare Liguori at AWS and Peter Alexander at Anthropic.

The incubation repository tells a different story. It holds a design sketch marked "Status: Draft proposal," authored by Peter Alexander, dated 2026-02-19. The README is blunt: the contents are exploratory and "do not represent official MCP specifications or recommendations." Implementers are already filing field reports against it. What there is not is a filed SEP — the charter's own stated success criterion: "An accepted SEP defining the trigger/callback mechanism and its subscription lifecycle."

ChatGPT implements a slice of that unfinished document. OpenAI's MCP Events guide requires MCP 2.0, protocol version 2026-07-28, and supports webhook delivery and callback verification from the draft. Polling, streaming, and the draft's gap and terminated control notifications are not supported. That last one matters.

The mechanics are straightforward. A server advertises an events capability in its server/discover response. The user tells ChatGPT what to monitor and how to respond. ChatGPT calls events/subscribe with the event name, filter arguments, a callback URL, and a signing secret. The server verifies the callback with a single-use challenge, stores the subscription, and pushes matching events as signed webhooks. Three methods — events/list, events/subscribe, events/unsubscribe — run on the same authenticated endpoint as the tools.

The subscription is a credential

The part that deserves attention is what the draft asks your server to store. As WorkOS's analysis frames it, an event subscription is a credential: a durable record, created under one user's access token, that authorizes your MCP server to push that user's data at an agent long after the token that created it has expired.

Compare the subscription to the token that authorized the call. Under the 2026-07-28 authorization spec, servers must validate that access tokens were issued specifically for them, authorization must be included in every HTTP request, and invalid or expired tokens must receive a 401. Short-lived, narrow in scope, re-checked on every request. The subscription inherits none of that. The design sketch keys a webhook subscription on the tuple (principal, delivery.url, name, arguments) — the principal is the server's canonical identifier for the authenticated subject. The tuple does not include the token itself, its scopes, or its expiry. And the lifetime is negotiable all the way to forever: ttlMs: null requests a subscription with no expiry, and a server that grants it returns refreshBefore: null.

Access token Event subscription
Lifetime Short, fixed expiry Whatever TTL you grant, up to no expiry (ttlMs: null)
Bound to Your server as audience, plus scopes (principal, url, name, arguments) — no token, scopes, or expiry
Checked On every HTTP request, 401 when expired MUST at subscribe time; SHOULD "periodically" after, no interval
Ends when It expires or the authorization server revokes it The TTL lapses, the client unsubscribes, or your server terminates it

The access token expires. The subscription it created keeps delivering.

Revocation is asymmetric in ChatGPT

The design sketch is clear about the obligation and vague about the cadence. At subscribe time, the principal must be authenticated and authorized. At delivery time: "The server SHOULD periodically re-verify permissions. If the user's access is revoked (e.g., removed from a Slack channel)," the server terminates the subscription. OpenAI's guide restates the same duty: "Recheck the user's access during the subscription's lifetime and stop delivery if access is revoked."

"Periodically" is doing a lot of work in that sentence. There is no interval, no MUST, and no conformance test behind it.

Then there is the signal itself. In the draft, each delivery mode has its own way to say "stop." For webhooks it is a signed {"type":"terminated"} envelope POSTed to the callback URL. After that the subscription no longer exists, so a later refresh returns -32012 Forbidden if the cause of termination still applies. That envelope is the protocol's way of telling the agent "this stopped, and here is why." It is also one of the two control notifications ChatGPT's integration does not support.

Delivery mode How the draft says "stop" In ChatGPT
Poll An error on the next poll Mode not supported
Push stream notifications/events/terminated Mode not supported
Webhook Signed terminated envelope POSTed to callback Mode supported, envelope not supported
Any mode Next refresh fails with -32012 Forbidden The only signal left

In ChatGPT's integration, the only remaining revocation signal is a failed refresh. If a user's access is revoked and your server stops delivery, the agent learns it only when the subscription's TTL expires and the refresh fails. If you granted ttlMs: null, the agent never learns.

What your server must verify

The draft and OpenAI's guide between them specify a real security surface. The parts that are not negotiable:

  • Require an authenticated principal. events/subscribe and events/unsubscribe must be called with an authenticated principal; calls that fail authorization get -32012 Forbidden.
  • Verify the endpoint before the first real delivery. HMAC stops forgery, not flooding. The server must not begin delivering to a callback URL until the endpoint's intent to receive deliveries is confirmed — a challenge handshake, an allowlist, or prior out-of-band verification.
  • Run SSRF checks at delivery time. Callback URLs must use HTTPS. Resolve and validate the destination address on each connection, block private and local ranges, never follow redirects, and apply all of it to verification requests as well as deliveries.
  • Keep payloads minimal. Event payloads carry the same injection risk as tool results. OpenAI's guide says to send a summary and expose a read tool for the full record, to treat user-authored text as data, and not to add instructions telling the model how to behave inside the payload.
  • Make writes idempotent. Events can arrive out of order, so repeated calls must not duplicate changes. Deliveries cap at 256 KiB, and 410 and 413 responses are not retried.
  • Authorize at action time, not at receipt. Event receipt does not constitute authorization to act. The tool call the agent makes in response goes through your normal checks — the same ones that gate MCP tool calls beyond OAuth scopes.

Everything above is in the documents. The two things that are not: how often you recheck access, and how a user or an admin sees what their account is pushing.

Three decisions you must make yourself

Subscription lifecycle is the exact thing the working group chartered itself to specify and has not shipped a SEP for yet. Until it does, three decisions are yours, and defaults will decide them badly.

First, grant short finite TTLs and refuse ttlMs: null. The TTL is the interval at which a revoked subscription becomes visible to the client. A subscription with no expiry is an OAuth grant nobody can see.

Second, re-verify access on a schedule you can state in a sentence — not "periodically." If you cannot say "we recheck every 15 minutes" and point to the job that does it, you are relying on a SHOULD with no interval and no conformance test.

Third, keep a per-user subscription index. Without it, offboarding a user means assuming their subscriptions died rather than confirming it. When an employee leaves, the revocation question is not "did their token expire" — it is "did every webhook they authorized stop firing."

MCP Events changes the trigger model for agents, and that is the real architectural shift. But the credential-lifetime gap is the part that will bite production deployments first. The protocol made the server stateless; the events extension made the server stateful again — and the state it holds is a credential with no specified revocation cadence.

The diagram below maps the subscription lifecycle, the credential-lifetime gap, and the asymmetric revocation surface:

MCP Events: Subscription Lifecycle & the Credential Gap The first new MCP transport primitive since the 2026-07-28 stateless spec 1 Subscribe — events/subscribe ChatGPT calls your server with event name, filter args, callback URL, signing secret (HMAC) Server verifies callback (challenge handshake), stores subscription with owner, filters, URL, secret, expiry Requires MCP 2.0, protocol version 2026-07-28 · Available to 1.2B weekly ChatGPT users 2 The Credential Gap — subscription outlives the token Access token: short-lived, scopes checked on every request, 401 when expired Subscription: keyed on (principal, url, name, arguments) — no token, scopes, or expiry in the key ttlMs: null requests no expiry · refreshBefore: null granted · WorkOS: "a credential you cannot see" Token: ~1 hour TTL Checked every request Subscription: up to forever SHOULD "periodically" — no interval 3 Deliver — signed webhook to callback URL Standard Webhooks: webhook-id, webhook-timestamp, webhook-signature · 256 KiB cap · one event per request SSRF checks at delivery time: HTTPS only, block private ranges, no redirects · Payload = injection surface Idempotent writes (events arrive out of order) · Event receipt ≠ authorization to act 4 Revoke — asymmetric in ChatGPT Draft: signed {"type":"terminated"} envelope POSTed to callback → agent learns "this stopped, and why" ChatGPT: webhook mode supported, terminated envelope NOT supported Draft revocation path terminated envelope → agent notified immediately ChatGPT revocation path TTL expires → refresh fails → -32012 Forbidden (only signal left) ttlMs: null No expiry → refresh never fires → agent never learns 5 Working Group — SEP not filed Leads: Clare Liguori (AWS) + Peter Alexander (Anthropic) · Charter changelog: one entry, 2026-03-24 Active work item: "SEP: Events in MCP v1 RFC" — Status: Ideating · Target: End April · Champion: TBD Design sketch exists (2026-02-19, Draft) · Implementers filing field reports · No filed SEP = no conformance tests Subscription lifecycle is explicitly in scope and explicitly unfinished Three decisions you must make before shipping 1. Grant short finite TTLs — refuse ttlMs: null 2. Re-verify access on a schedule you can state in a sentence 3. Keep a per-user subscription index — confirm, don't assume, on offboarding

Related reading


A mid-market SaaS company running a customer support MCP server — the kind that connects ChatGPT to a ticketing system and a knowledge base — wants to add event-driven triggers so an agent drafts a response when a new high-priority ticket arrives. The engineering team implements events/subscribe, stores the subscription, and ships webhook delivery. Three weeks later, a support agent leaves the company. Their access token expired in an hour. Their event subscription, created under that token, is still firing webhooks at ChatGPT because nobody granted a finite TTL and the per-user subscription index does not exist. The terminated envelope that would have told ChatGPT "this stopped" is not supported in the integration. The agent keeps acting on tickets the departed employee can no longer see in the source system.

Request a scoped build. One-week discovery. You get a system inventory, workflow map, and fixed scope — whether or not you build with us.

Want this built for your systems?

Every document here comes from real production work. If you have a target system and a workflow in mind, we can scope a build in one week.

Request a scoped build

One-week discovery. You get a system inventory, workflow map, and fixed scope — whether or not you build with us.