Delivery modes
Every event offers all three delivery modes of the extension. Your client picks the one it can use;events/list lists them in delivery.
All three read the same event log, so a cursor from one mode works in another.
Available events
A connection only sees the events its scopes allow, and only for resources that belong to its agent. Phone and social events appear only when those features are enabled for your organization.
events/list returns the exact list with input and payload schemas.
Payload
Every event has the same shape in every mode. A webhook receives it as the POST body, a poll returns it inevents, and a stream sends it as the params of notifications/events/event:
cursor is a position you can store: every event at or before it has been delivered. Pass it back after an interruption to receive what you missed, as cursor of events/subscribe, events/poll or events/stream. A cursor older than the 30-day event history, or a maxAgeMs that skips events, sets truncated: true on the response: events may be missing, so read the current state with the tools. Polls carry the cursor on the response instead of on each event.
Webhook delivery
- Before the first delivery to a callback URL, the server sends a signed
{"type": "verification", "challenge": "…"}request. The endpoint must answer2xxwith{"challenge": "…"}echoing the value. - Requests are signed with Standard Webhooks using the
whsec_secret the client supplied. The headers arewebhook-id(the event ID),webhook-timestamp,webhook-signatureandX-MCP-Subscription-Id. After a secret rotation, deliveries carry both signatures for a day. - A
2xxresponse acknowledges the event. Other responses are retried with increasing delays, six attempts in total.410 Goneremoves the subscription, and413is not retried. - After 50 failed attempts in a row, delivery pauses until the client subscribes again.
- When the connection loses access to what it watches, the server ends the subscription and sends one signed
{"type": "terminated", "error": {"code": -32012, "message": "Forbidden", "data": {"reason": "…"}}}request. - Callback URLs must use HTTPS and resolve to a public address. Redirects are not followed.
Subscriptions
Subscribing again with the same connection, callback URL, event and arguments updates the existing subscription instead of creating another.
Revoking the connection on the agent’s Tokens tab ends all of its subscriptions. Access is checked again before every delivery, so deliveries also stop when you remove a scope, delete the inbox or phone number, or remove the agent’s access to a social account.
Polling
events/poll keeps no state on the server. Each request names the event, its arguments and the cursor from the previous response:
- The first poll has no cursor. It returns no events, only the current position.
- Store the returned cursor every time, including after an empty response; it moves forward while nothing happens.
- When
hasMoreistrue, poll again right away. Otherwise waitnextPollMs. maxEventscaps a response at 1 to 100 events (50 when omitted).maxAgeMsskips events older than that and reportstruncated.
Streaming
events/stream takes the same parameters as a poll, without maxEvents, and answers with a server-sent event stream for that one subscription:
Every notification carries the request’s JSON-RPC
id in _meta["io.modelcontextprotocol/subscriptionId"], so a client with several streams can route them. An invalid or forbidden request gets a plain JSON-RPC error and no stream. Close the request to unsubscribe. The server ends each stream after five minutes; reconnect with the last cursor you received.
Limits for polls and streams
Too many requests get
-32013 with data.limit naming the limit. Access is checked on every poll and every minute of a stream.