---
title: Subscriptions
---

<div id="enable-section-numbers" />

`subscriptions/listen` opens a long-lived notification stream from the server to the
client. Unlike one-off requests, the stream stays open and delivers notifications until
the client cancels it. It replaces the former `resources/subscribe` RPC and the HTTP GET
endpoint.

## Opening a Stream

The client sends a `subscriptions/listen` request with a `notifications` filter
specifying which event types it wants to receive. The server **MUST NOT** send
notification types the client has not explicitly requested.

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "subscriptions/listen",
  "params": {
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientInfo": {
        "name": "ExampleClient",
        "version": "1.0.0"
      },
      "io.modelcontextprotocol/clientCapabilities": {}
    },
    "notifications": {
      "toolsListChanged": true,
      "resourceSubscriptions": ["file:///project/config.json"]
    }
  }
}
```

### Notification Filter

| Field                   | Type       | Description                                                       |
| ----------------------- | ---------- | ----------------------------------------------------------------- |
| `toolsListChanged`      | `boolean`  | Receive `notifications/tools/list_changed` when tools change      |
| `promptsListChanged`    | `boolean`  | Receive `notifications/prompts/list_changed` when prompts change  |
| `resourcesListChanged`  | `boolean`  | Receive `notifications/resources/list_changed` when list changes  |
| `resourceSubscriptions` | `string[]` | Receive `notifications/resources/updated` for these resource URIs |

All fields are optional. Omitting a field is equivalent to not subscribing to that
notification type.

## Acknowledgment

The server **MUST** send `notifications/subscriptions/acknowledged` as the first message
carrying the subscription's ID in `_meta` under `io.modelcontextprotocol/subscriptionId`,
and **MUST NOT** send any notification on the
subscription before it. On stdio, where every subscription shares one channel, this
ordering is defined per subscription ID and not per channel: messages belonging to other
subscriptions **MAY** be interleaved before it.

The `notifications` field in the acknowledgment reflects the subset the server agreed to
honor. Notification types the server does not support are omitted.

```json
{
  "jsonrpc": "2.0",
  "method": "notifications/subscriptions/acknowledged",
  "params": {
    "_meta": {
      "io.modelcontextprotocol/subscriptionId": 1
    },
    "notifications": {
      "toolsListChanged": true,
      "resourceSubscriptions": ["file:///project/config.json"]
    }
  }
}
```

The client **SHOULD** check the acknowledged filter against what it requested and handle
any unsupported types gracefully.

## Receiving Notifications

All notifications delivered on the stream carry
`io.modelcontextprotocol/subscriptionId` in `_meta`, identifying the
`subscriptions/listen` request that opened the stream. The value is the JSON-RPC ID of
the `subscriptions/listen` request. In the examples above, the request used `"id": 1`,
so the acknowledgment and all subsequent notifications carry the subscription ID `1`.
On stdio, where all messages
share a single channel, clients **MUST** use this field to correlate notifications
with their originating subscription.

```json
{
  "jsonrpc": "2.0",
  "method": "notifications/resources/updated",
  "params": {
    "_meta": {
      "io.modelcontextprotocol/subscriptionId": 1
    },
    "uri": "file:///project/config.json"
  }
}
```

## Multiple Concurrent Subscriptions

A client **MAY** have multiple active subscriptions concurrently — for example,
one listening for tools-list changes and another for resource updates. Each
subscription is identified by the JSON-RPC request ID of its
`subscriptions/listen` request, and every notification on the stream carries
that ID in
`io.modelcontextprotocol/subscriptionId` so clients can demultiplex them.

## Cancellation

A subscription ends when:

- The **client** cancels it — close the SSE stream (HTTP) or send
  `notifications/cancelled` referencing the `subscriptions/listen` request ID (stdio).
- The **server** tears it down (e.g., during shutdown) — it **SHOULD** send a
  successful `subscriptions/listen` response to signal a graceful end (see
  [Graceful Closure](#graceful-closure)), then close the stream.
- The underlying transport closes (HTTP timeout, TCP disconnect, stdio process
  exit).

### Graceful Closure

When the server ends a subscription on its own initiative (for example, during
shutdown), it **SHOULD** respond to the original `subscriptions/listen` request
with a completion result before closing the stream. The result carries no
method-specific data beyond the standard result fields and subscription
metadata. This is the JSON-RPC response to the long-lived request, correlated by
its `id`, and signals that the subscription ended gracefully — as opposed to an
abrupt transport drop, which carries no response.

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "resultType": "complete",
    "_meta": {
      "io.modelcontextprotocol/subscriptionId": 1
    }
  }
}
```

Like every other message on the stream, the response carries
`io.modelcontextprotocol/subscriptionId` in `_meta`, identifying which
subscription it closes. The value matches the JSON-RPC `id` of the originating
`subscriptions/listen` request.

A client that receives this response knows the subscription closed cleanly; a
transport that closes without it indicates an unexpected disconnect, which the
client **MAY** treat as a trigger to reconnect.

On **stdio**, if the connection is terminated and then re-established, the
client **MUST** re-send `subscriptions/listen` to re-establish its
subscriptions — the server holds no subscription state across reconnections.

See [Cancellation][cancellation] for the full rules.

[cancellation]: /specification/draft/basic/patterns/cancellation
