What it does
The `resources/subscribe` method establishes a persistent watch on a single resource URI. The client sends a request object containing only the `uri` parameter; the server validates the URI, registers the subscription internally, and responds with an empty result object (`{}`). From that point forward, any time the server detects a change to that resource's contents it MUST emit a `notifications/resources/updated` notification carrying the same URI back to the subscribing client. The client then calls `resources/read` on that URI to fetch the updated bytes — the notification itself contains no content payload. \n\n Before sending this request the client MUST verify that the server advertised `resources.subscribe: true` in its `ServerCapabilities` during initialization. Calling `resources/subscribe` against a server that omitted this flag is a protocol error and the server MAY return a JSON-RPC `-32601 Method not found` or a custom application error. If the URI does not exist or the server has no watcher mechanism for that scheme, expect a `-32002` resource-not-found application error. Duplicate subscriptions on the same URI within the same session are generally idempotent — servers typically treat a second subscribe as a no-op rather than an error, but this is not guaranteed by the spec. \n\n Ordering matters: there is an inherent race between the moment the client receives the subscribe response and the moment the first notification arrives. A resource may have changed after the client last called `resources/read` but before the subscribe response was processed. To avoid missing an update, clients should call `resources/read` immediately after a successful subscribe to get a fresh baseline, then apply subsequent notifications on top of it. When the client no longer needs live updates it MUST call `resources/unsubscribe` with the same URI; leaving subscriptions open wastes server memory and may prevent the server from garbage-collecting watched file handles.
When to use
When the host wants live updates of a watched file or record.
When NOT to use
For static resources that never change.
Notes
Capability gate is mandatory
Check `serverCapabilities.resources.subscribe === true` after `initialize` completes. If the field is absent or false, never send this request. Servers that do not declare the capability are not required to implement the method and will likely return -32601 (Method not found), which is unrecoverable without reconnecting.
Error codes to handle
Two error codes matter in practice: `-32601` (method not found) when the server does not support subscriptions at all, and application-level errors (commonly `-32002` or a server-defined code in the `-32000` to `-32099` range) when the URI is invalid or the scheme cannot be watched. Always surface these to the caller rather than silently swallowing them so the host can fall back to polling.
Read after subscribe to close the race window
There is a TOCTOU gap between your last `resources/read` and the server registering the subscription. Always issue a fresh `resources/read` immediately after a successful subscribe response before trusting your cached content. Treat that read result as the baseline and apply `notifications/resources/updated` events on top — this is the safest way to stay consistent without polling.
Subscriptions are not paginated or batched
Each `resources/subscribe` call covers exactly one URI. There is no bulk-subscribe method in the MCP spec. If you need to watch many resources, send one request per URI. The server's notification stream is also per-URI — a single `notifications/resources/updated` carries one `uri` field, so fan-out tracking must be done client-side with a map keyed by URI.
Always unsubscribe on teardown
Open subscriptions hold server-side state (file descriptors, change listeners, registry entries). Call `resources/unsubscribe` with the same URI when the watcher is no longer needed — on component unmount, tab close, or session end. If the transport drops unexpectedly and the client reconnects, re-subscribe explicitly because the server will have discarded all prior subscription state on its end.
Request parameters
| Name | Type | Purpose |
|---|---|---|
| uri | string | URI to watch. |
Examples
Subscribe
{ "method": "resources/subscribe", "params": { "uri": "file:///log.txt" } }
Common mistakes
❌ Subscribing without checking resources.subscribe capability
✅ Capability gate first.