> ## Documentation Index
> Fetch the complete documentation index at: https://smithery.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Triggers

> Expose events from your MCP server so consumers can receive them as webhooks.

<Warning>
  Triggers are in preview. Breaking changes may happen without notice.
</Warning>

Triggers let an MCP server surface events from its upstream service. When a consumer activates a trigger via Smithery, Smithery proxies the subscribe call straight through to your server with the consumer's webhook URL and signing secret. When the upstream provider fires, your server POSTs the event **directly to the consumer** — Smithery is not in the delivery path.

Your server is not in the hot path for events — only for setup and teardown. This keeps trigger support compatible with serverless runtimes.

## Alignment with the MCP Events proposal

This extension tracks the webhook slice of the MCP committee's [events proposal](https://github.com/modelcontextprotocol/experimental-ext-triggers-events/pull/1). Smithery is intentionally minimal: it implements only what the spec requires for webhook delivery, and skips optional surface area while the proposal is still draft.

What Smithery implements today:

* `events/list` — full parity
* `events/subscribe` in **webhook** mode only, with mandatory TTL refresh
* `events/unsubscribe`

What Smithery does not implement:

* `poll` and `push` (`events/stream`) delivery modes
* Cursor replay — events start "from now"
* `truncated`, `maxAge`, `maxEvents` parameters
* Control envelopes (`gap`, `terminated`)
* `deliveryStatus` reporting
* Asymmetric endpoint verification or server signing

Smithery acts as the spec's "client" against your server: it forwards the consumer's `delivery.url` and Standard Webhooks `delivery.secret` unchanged. Your server signs deliveries with that secret and POSTs them to that URL.

## How it works

```mermaid theme={null}
sequenceDiagram
    participant App
    participant Smithery
    participant MCP Server
    participant Upstream

    App->>Smithery: ai.smithery/events/subscribe (params, delivery)
    Smithery->>MCP Server: ai.smithery/events/subscribe (params, delivery)
    MCP Server->>Upstream: register webhook
    MCP Server-->>Smithery: { id, refreshBefore }
    Smithery-->>App: { id, refreshBefore }

    Note over App,Smithery: App re-POSTs subscribe before<br/>refreshBefore to keep alive

    Upstream->>MCP Server: event fires
    MCP Server->>App: POST app's URL (signed with delivery.secret)
    App-->>MCP Server: 2xx
```

1. The consumer creates a trigger through Smithery, supplying their own webhook URL and Standard Webhooks secret.
2. Smithery, authenticated as the connection's principal, calls your `ai.smithery/events/subscribe` and forwards the consumer's `delivery.url` and `delivery.secret` unchanged.
3. Your server uses the connection's credentials to register an upstream webhook (or start whatever event source it needs) and returns `{ id, refreshBefore }`.
4. Before `refreshBefore`, the consumer re-POSTs through Smithery, which re-calls subscribe with the same key — your server treats this as an idempotent upsert and resets the TTL.
5. When the upstream fires, your server POSTs the event **directly to the consumer's `delivery.url`**, signed with `delivery.secret`. Smithery is not involved in delivery.
6. When the consumer deletes the trigger (or stops refreshing), Smithery calls `ai.smithery/events/unsubscribe` to tear down the upstream registration.

## Negotiation

Advertise the extension in your `initialize` response:

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2025-11-25",
    "capabilities": {
      "extensions": {
        "ai.smithery/events": {}
      }
    },
    "serverInfo": { "name": "NotionMCP", "version": "1.0.0" }
  }
}
```

## Methods

### `ai.smithery/events/list`

Return the catalog of event types your server supports. Each entry declares the params a subscriber must provide and the payload your server will deliver.

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "events": [
      {
        "name": "page.updated",
        "description": "Fires when a page in the watched workspace is updated.",
        "delivery": ["webhook"],
        "inputSchema": {
          "type": "object",
          "properties": {
            "workspace_id": { "type": "string" }
          },
          "required": ["workspace_id"]
        },
        "payloadSchema": {
          "type": "object",
          "properties": {
            "page_id": { "type": "string" },
            "updated_at": { "type": "string", "format": "date-time" }
          }
        }
      }
    ]
  }
}
```

| Field           | Type       | Description                                                                                                                                 |
| --------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`          | `string`   | Unique event name scoped to your server.                                                                                                    |
| `description`   | `string`   | Human-readable summary of when this event fires.                                                                                            |
| `delivery`      | `string[]` | Delivery modes this event supports. For Smithery, return `["webhook"]`.                                                                     |
| `inputSchema`   | `object`   | JSON Schema for the `params` a subscriber must supply (e.g. workspace or channel scoping). Mirrors the `inputSchema` convention from tools. |
| `payloadSchema` | `object`   | JSON Schema describing the `data` object your server emits.                                                                                 |

### `ai.smithery/events/subscribe`

Smithery calls this when a consumer activates a trigger, and again before each `refreshBefore` to keep the subscription alive. Register an upstream webhook pointing at `delivery.url`, retain `delivery.secret` to sign each POST, and return a stable subscription `id` plus a fresh `refreshBefore`.

Request:

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "ai.smithery/events/subscribe",
  "params": {
    "name": "page.updated",
    "params": { "workspace_id": "w_123" },
    "delivery": {
      "mode": "webhook",
      "url": "https://my-app.example.com/events",
      "secret": "whsec_<base64 of 24-64 random bytes>"
    }
  }
}
```

Response:

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "id": "sub_a3f1c8e2b0d49f7e",
    "refreshBefore": "2026-04-22T13:00:00.000Z"
  }
}
```

| Param             | Description                                                                                          |
| ----------------- | ---------------------------------------------------------------------------------------------------- |
| `name`            | The event name from `events/list`.                                                                   |
| `params`          | Subscriber-supplied arguments conforming to the event's `inputSchema`.                               |
| `delivery.mode`   | Always `"webhook"` for this extension.                                                               |
| `delivery.url`    | The consumer's HTTPS webhook endpoint. POSTs go here directly.                                       |
| `delivery.secret` | The consumer's Standard Webhooks `whsec_` secret. Sign each POST to `delivery.url` with this secret. |

| Result field    | Description                                                                                                                                                                       |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`            | Stable subscription handle deterministic over `(principal, name, params, delivery.url)`. The receiver uses it to look up the right secret via the `X-MCP-Subscription-Id` header. |
| `refreshBefore` | ISO 8601 timestamp at which the subscription expires unless refreshed. Pick a TTL that reflects how long you can hold soft state for (e.g. 30 minutes).                           |

#### Idempotent upsert and TTL refresh

`ai.smithery/events/subscribe` is keyed by `(principal, name, params, delivery.url)`. If a subscription with the same key already exists, treat the call as an upsert:

* **TTL** — reset, return a new `refreshBefore`.
* **`delivery.secret`** — replace. During rotation, the spec recommends dual-signing with the old and new secrets for a short grace window using Standard Webhooks' multi-signature support.
* **`id`** — unchanged.

If the subscription has expired (or your server lost it on restart), create a fresh one for the same key and return a new `id`.

If Smithery stops refreshing before `refreshBefore`, expire the subscription and tear down the upstream webhook — no explicit unsubscribe is required.

### `ai.smithery/events/unsubscribe`

Smithery calls this on eager teardown (consumer deleted the trigger). Tear down the upstream registration you created.

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 4,
  "method": "ai.smithery/events/unsubscribe",
  "params": {
    "name": "page.updated",
    "params": { "workspace_id": "w_123" },
    "delivery": {
      "url": "https://my-app.example.com/events"
    }
  }
}
```

Response:

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 4,
  "result": {}
}
```

Look up the subscription by `(principal, name, params, delivery.url)` and deregister the corresponding upstream webhook. The subscription `id` is not accepted here — the key is the canonical identifier.

## Event delivery

When an upstream event arrives, POST it to `delivery.url`, signed with the Standard Webhooks headers and the consumer's `delivery.secret`. The consumer verifies the signature and processes the event.

You are responsible for:

1. Declaring a `payloadSchema` that accurately describes the `data` field.
2. Configuring upstream webhook registration so the payload delivered to the consumer matches that schema.
3. Never including secrets or PII the consumer shouldn't see.

If the upstream payload requires transformation (unwrapping envelopes, hydrating IDs, redacting fields), do it at registration time — use the provider's webhook filter or payload-shape features.

The body sent to the consumer is an `EventOccurrence`:

```json theme={null}
{
  "eventId": "evt_01HW...",
  "name": "page.updated",
  "timestamp": "2026-04-22T12:00:00.000Z",
  "data": {
    "page_id": "...",
    "updated_at": "2026-04-22T12:00:00.000Z"
  }
}
```

| Field       | Description                                                                                                                                               |
| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `eventId`   | Stable event identifier. Use the upstream's stable id (Stripe `evt_*`, GitHub delivery GUID, Gmail message id) so the receiver can dedupe across retries. |
| `name`      | The event name from `events/list`.                                                                                                                        |
| `timestamp` | ISO 8601 timestamp of the upstream event.                                                                                                                 |
| `data`      | Payload conforming to the event's `payloadSchema`.                                                                                                        |

### Webhook signing

Sign every POST with the [Standard Webhooks](https://www.standardwebhooks.com/) HMAC profile.

Required headers:

| Header                  | Value                                                                                                                                   |
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `webhook-id`            | Stable event id for deduplication                                                                                                       |
| `webhook-timestamp`     | Unix seconds                                                                                                                            |
| `webhook-signature`     | `v1,<base64 HMAC-SHA256(delivery.secret, "id.timestamp.body")>`                                                                         |
| `X-MCP-Subscription-Id` | The subscription `id` you returned from `events/subscribe`. The receiver uses this to look up the right secret before parsing the body. |

Sign the raw request body with a Standard Webhooks library. Each retry attempt MUST regenerate the timestamp and signature so receivers do not reject a later attempt as stale.

## API reference

### Extension capability

Advertise during `initialize` under `capabilities.extensions`:

```json theme={null}
{ "ai.smithery/events": {} }
```

### MCP methods (Smithery → server)

| Method                           | Params                                              | Result                                                                      |
| -------------------------------- | --------------------------------------------------- | --------------------------------------------------------------------------- |
| `ai.smithery/events/list`        | —                                                   | `{ events: [{ name, description, delivery, inputSchema, payloadSchema }] }` |
| `ai.smithery/events/subscribe`   | `{ name, params, delivery: { mode, url, secret } }` | `{ id, refreshBefore }`                                                     |
| `ai.smithery/events/unsubscribe` | `{ name, params, delivery: { url } }`               | `{}`                                                                        |

## Learn more

* [Using triggers](/docs/use/triggers) — consumer-facing docs
* [MCP Events proposal (PR #1)](https://github.com/modelcontextprotocol/experimental-ext-triggers-events/pull/1)
* [Publish a server](/docs/build/publish)
* [Standard Webhooks spec](https://www.standardwebhooks.com/)
