Repository navigation
Conversation
Ankcorn
marked this pull request as ready for review
September 30, 2026 07:44
- advertise events.listChanged: false - accept cursor and maxAgeMs; clamp ttlMs instead of rejecting it - make delivery.mode optional on events/unsubscribe, as in the draft - map failures to -32011 NotFound, -32012 Forbidden and -32015 with a categorized reason - treat receiver 410/413 as non-retryable for that delivery only; retry every other non-2xx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Agents can now subscribe to Workers Observability real-time issues over MCP. When the user asks ChatGPT, for example, to watch a Worker, it gets a signed webhook each time Cloudflare detects a new or recurring issue.
This implements webhook delivery from the MCP Events draft, which is also what ChatGPT supports. Poll and push delivery are not offered.
sequenceDiagram participant C as MCP client participant M as /mcp participant CF as Cloudflare API participant A as /events/ans/{id} participant R as Client callback C->>M: events/subscribe {name, arguments, delivery: {url, secret}} M->>R: signed {type: "verification", challenge} R-->>M: {challenge} M->>CF: ANS webhook destination + policy, Vega issue automation M-->>C: {id, refreshBefore, cursor: null} Note over CF: Vega detects an issue CF->>A: ANS webhook (cf-webhook-auth) A->>CF: re-check policy and access A->>R: signed EventOccurrence R-->>A: 2xx A-->>CF: 204 (or 503 so ANS retries)Methods
server/discoveradvertisesevents: { listChanged: false }.events/listreturnscloudflare.alert.workers_observability_real_time_issuewhen the account can use ANS and Vega issue automations.events/subscribecreates or refreshes a subscription. The subscription ID issub_<sha256>over(MCP resource, principal, url, name, arguments), so repeat calls reuse the same resources.events/unsubscribedisables the policy, then deletes the automation, policy and destination. It returns{}even when nothing matches.Arguments take exactly one trigger.
afterOccurrences: 1fires on new issues.afterInactivitySeconds(3,600 to 31,536,000) fires when an issue comes back after that much quiet.State without a database
The Worker stores nothing itself. Each subscription maps onto existing Cloudflare resources in the user's account:
MCP Events sub_.../events/ans/{id}cf-webhook-auth. Both keys derive fromMCP_COOKIE_ENCRYPTION_KEYand bind toMCP_RESOURCEand the subscription ID.ttlMsis clamped to between one minute and one hour, and to before the MCP token expires. Expired subscriptions stop delivering without a scheduler.Delivery
src/events/callback.tstakes the ANS webhook, decrypts the ticket, re-reads the policy with the user's current credentials, and converts the alert into anEventOccurrence:2xxgives204.410and413give204and drop that one delivery, but the subscription stays active. Anything else gives503and ANS retries.terminatedandgapcontrol messages are not sent. ChatGPT doesn't handle them.Errors
-32602whsec_secret-32011{kind: "event"}-32012-32015{reason}challenge_failed,timeout,connection_refused,tls_error,http_4xx,http_5xx-32603Testing
npm run test:eventsruns the real Worker, OAuth, MCP transport, encryption and callback route in workerd, with MSW standing in for the Cloudflare API and the receiver.docs/mcp-events.mdcovers a live test against a real account usingscripts/mcp-events-receiver.mjsandscripts/mcp-events-client.mjs.