MCP Events: Run Automations from Signed Webhooks
Learn how MCP Events links a remote MCP server to existing Automations using signed webhooks. Covers the opt-in mcp-events plugin, callback setup, and supported profile limits.
Read this when
- Connecting an MCP event source to an automation
- Configuring or troubleshooting signed MCP Events callbacks
- Testing subscription renewal, duplicate delivery, or restart recovery
MCP Events links a remote MCP server to Automations. Change filtering happens on the MCP server, which then delivers signed webhooks. OpenClaw accepts those webhooks durably and executes the automation's existing instructions, tool policy, delivery, and history. No additional scheduler is introduced by events, and no MCP App view has to stay open.
Supported profile
The opt-in mcp-events plugin ships with OpenClaw and implements the
OpenAI webhook profile:
MCP protocol 2026-07-28, server/discover, events/list,
events/subscribe, and events/unsubscribe over Streamable HTTP.
It relies on the MCP connections and authentication already configured in OpenClaw.
Legacy MCP tool connections keep their existing handshake.
This integration is versioned against an experimental specification. It does not claim support for every draft Events feature. Polling, push streams, and the draft's gap and terminated webhook notifications fall outside this profile.
Configure the callback
Set up the server via the existing MCP settings, then turn on the plugin with a public HTTPS callback origin:
{
mcp: {
servers: {
reviews: {
transport: "streamable-http",
url: "https://mcp.example.com/mcp",
},
},
},
plugins: {
entries: {
"mcp-events": {
enabled: true,
config: {
callbackOrigin: "https://events.example.com",
},
},
},
},
}
That origin must carry no credentials, path, query, or fragment. Through your HTTPS reverse proxy, route only /plugins/mcp-events/callback/ to the Gateway.
The Control UI, Gateway WebSocket, and other administrative paths must not be exposed.
OpenClaw neither opens a tunnel nor modifies your network configuration.
The remote server needs to reach this origin and verify its TLS certificate. Public DNS validation is a prerequisite, not proof of reachability; subscription callback verification provides that proof. Infrastructure requiring an extra interactive sign-in or bearer header cannot accept these callbacks unless a separate route is configured: MCP Events supplies its own per-subscription HMAC authentication.
Use the MCP credential configuration or sign-in flow that already exists. MCP bearer tokens and webhook signing secrets must not be placed in the automation's instructions.
Create an event automation
The mcp_events discovery tool is exposed by the plugin. For a configured serverName, it lists one page of webhook events along with their argument schemas. It does not create a separate subscription or scheduling system. The Automation editor offers the same catalog.
Build an automation with an event source, an agent-turn payload, and explicit output delivery. The source points at the configured MCP server; it holds no second connection URL or credential store.
The automation job shape is:
{
"name": "Review new document comments",
"agentId": "main",
"enabled": true,
"schedule": {
"kind": "event",
"source": "mcp-events",
"options": {
"server": "reviews",
"name": "comment.created",
"arguments": { "document_id": "doc_example" }
}
},
"sessionTarget": "isolated",
"wakeMode": "now",
"payload": {
"kind": "agentTurn",
"message": "Summarize the new review comment. Do not modify the document."
},
"delivery": { "mode": "none" }
}
In this example, results are kept in automation run history and no chat announcement is sent. When results should go to a channel or conversation, configure the usual delivery destination. For external comments and documents, isolated runs are preferable. Event data is untrusted, separate from the instructions the user authorized; signatures do not turn payload text into trusted instructions or grant additional tools.
Event arguments must satisfy the server's inputSchema. Delivered data must satisfy its payloadSchema. Subscription filters are applied by the server before events are sent. Event names, descriptions, and schemas originate from that server and are not trusted authorization facts.
Subscription lifecycle
Before subscribing, OpenClaw prepares a callback binding and signing secret. The server can therefore send its signed verification challenge before it returns the subscription ID. Verification confirms the callback; it never starts an agent.
Subscriptions carry finite leases. OpenClaw refreshes them ahead of the server's refreshBefore deadline, using the existing Gateway scheduler and the last persisted cursor. Protocol maintenance does not invoke a model. When cron.triggers.enabled is set to false, event-run admission pauses along with the existing trigger surfaces, and already accepted events are not discarded. Subscription state survives Gateway restarts; the originating chat turn or app view does not own its lifetime. While its automation stays enabled, an unavailable source keeps retrying with backoff capped at five minutes; pausing or removing the automation cancels that recovery work.
Pausing, removing, or replacing a source prevents new event runs from the retired source generation. Late requests cannot reactivate it. Remote unsubscribe uses the original event name, arguments, and callback URL. If cleanup cannot establish current credential authority, it fails closed; the finite remote lease bounds orphan lifetime. Re-enabling an automation does not revive callbacks from its previous generation.
Requester-scoped connections retain their authenticated owner. An unavailable or revoked requester connection does not fall back to shared operator credentials. Intake and queued dispatch revalidate through the credential owner. A successful token refresh preserves the binding; disconnect/reconnect creates a different authorization lifetime and does not revive old callbacks. Temporary credential store failures return retryable responses and retain queued work. Custom requester connection resolvers must supply a live authorization observation, not only a URL and headers. Remote-only revocation is detectable only when the provider reports it or an authenticated request observes rejection. Durable auth-profile sources use their actual shared or agent credential owner; personal model-account IDs, external-only credentials, and bounded or environment-only auth scopes are not promoted to ambient shared authority.
Receipt, execution, and recovery
A successful webhook response acknowledges durable receipt, not model completion or output delivery. OpenClaw verifies the signature over the exact request body, checks the signing timestamp and source binding, and stores the event before returning 2xx. One request contains one event, no larger than 256 KiB.
The existing ingress queue retains pending events while an automation is busy. Duplicate event IDs are scoped to their source binding; they do not suppress unrelated events from another server or subscription. The handoff to a run transfers the ingress claim and records the automation receipt together. Before that transfer, restart recovery can retry admission. After it, the run receipt owns completion or interruption; OpenClaw does not automatically repeat a possibly side-effecting run to hide an uncertain outcome.
The plugin's maxPendingEvents setting bounds pending intake (default 10,000). Full intake refuses new events rather than evicting already accepted work. Pending and claimed events are never retention-evicted. Completed and failed ingress records use the shared 30-day / 20,000-record-per-account retention bounds; deduplication is bounded by that retained history. Retired subscription secrets are removed after remote cleanup or the bounded lease cleanup window. Execution errors, intentional pauses, invalidated sources, and successful receipt are different outcomes; inspect automation history and plugin health rather than treating a webhook 2xx as task success.
Limits of replay
MCP Events does not guarantee end-to-end exactly-once effects or that every upstream event arrives. Servers can exhaust their retry budget. Event delivery can be out of order. A null cursor means the source offers no replay, and truncated: true means requested history is no longer available.
Cursors are opaque watermarks, not values to sort or compare numerically. The draft permits a watermark to pass events abandoned by the server. For critical workflows, reconcile against authoritative source state and make write actions idempotent. A durable local queue protects accepted events; it cannot recover changes the source never delivered and no longer retains.
Disable or downgrade
Pause or remove event automations before disabling the plugin. Before downgrading to a version that predates event schedules, remove those jobs using the current version and check the target release's database compatibility against a copied state database. Removing event jobs does not undo database schema upgrades. If the target release rejects the newer schema, restore a compatible pre-upgrade backup instead of opening the upgraded state with it. Older versions do not understand the persisted event schedule kind; disabling the plugin alone does not convert it to a timed schedule.
Developer proof server
The repository includes scripts/mcp-events-test-server.mjs and test/fixtures/mcp-events/README.md. This is a real authenticated HTTP MCP server with signed callbacks, JSON and request-scoped SSE responses, durable fixture subscriptions, and controls for duplicates, invalid signatures, bursts, expiry, rotation, revocation, restart, and replay.
Use only synthetic records and fixture-only credentials. Run the Gateway with isolated state and a dedicated profile. A fixture's delivery response alone is not end-to-end proof: verify the actual automation run, persisted outcome, and intended effect. Never disable TLS verification or weaken production callback validation to make a test pass.
Related
- Automations
- Inbound webhooks, distinct hooks that authenticate with a bearer token
- MCP connections
- Plugin service runtime