Most MCP clients read a resource once and move on — a file, a config, a ticket, fetched when the model asks for it. But some resources change while a session is still open: a log file still being written, a document someone else is editing, a build that’s still running. MCP resource subscriptions are the protocol’s answer to that case — a way for a client to ask a server to push an update when a specific resource changes, instead of re-reading it over and over to find out. It’s one of the smaller utilities in MCP, easy to confuse with the tools, resources, and prompts primitives it rides on top of, and it only matters for a narrow slice of servers.
How it actually works
A server that supports subscriptions declares a subscribe capability alongside its resources capability at initialization. From there, a client that wants to watch a specific resource sends a resources/subscribe request naming that resource’s URI. The server keeps track of which clients asked for which URIs, and when the underlying content changes, it sends a notifications/resources/updated notification carrying that same URI back to the subscribed client. The client decides what to do next — typically re-reading the resource with an ordinary resources/read call, since the update notification itself carries no content, only a signal that something changed. A client can later send resources/unsubscribe to stop watching, and a session ending implicitly ends every subscription tied to it.
That’s a different, narrower notification than notifications/resources/list_changed, which exists for a different question entirely: whether the set of resources a server offers has changed — a new file appeared, one was deleted — not whether the content of one you already know about was edited. A server can implement either, both, or neither; they’re independent capabilities, and seeing one in a server’s declared capabilities doesn’t imply the other.
What it doesn’t give you
Subscriptions are opt-in on both sides, and advisory the same way the rest of MCP’s metadata signals are. The spec doesn’t require a server to implement subscribe at all, and plenty of resource-backed servers never do, because the resource they expose rarely changes mid-session or the server simply wasn’t built with change-tracking in mind. There’s also no guarantee about latency: an update notification can arrive instantly or minutes after the underlying change, depending on how the server detects it — a database trigger, a polling loop inside the server itself, a webhook from somewhere else — none of which the protocol specifies. And the notification doesn’t deliver a diff or a new value; it’s purely a signal to go re-read, which means the client still pays the cost of a full resources/read on every update, just fewer of them than it would polling blind.
This is a quieter cousin of the gap we described for progress notifications: both are optional side channels that make a client’s view of server-side state less stale, and neither one is a guarantee anything downstream actually reads or acts on the message. A server can fire resources/updated into the void and a client can simply never have subscribed in the first place; neither side is wrong, and nothing in the handshake forces either one to engage with the feature.
Where this actually shows up
Subscriptions matter most for servers backing something genuinely live: a shared document, a running job’s log output, a ticket whose status other people are changing. For the far more common case — a resource that’s effectively static for the life of a session, like a config file or a reference doc — there’s nothing to subscribe to and no reason for a server to bother implementing it. If you’re deciding whether to connect a server from gate’s MCP server directory, this is a detail worth checking only if the server’s whole value is in resources that move while you’re looking at them; for most tool-shaped servers it’s irrelevant.
If you build a server
Implement subscribe only when a resource can genuinely change out from under an open session, and when a client re-reading it on a timer would either miss updates or waste calls. Fire notifications/resources/updated from the actual event that changed the resource — not a fixed-interval poll pretending to be push — and track subscriptions per-connection so a client that disconnects doesn’t leave you holding state for no one. If you’re generating a server from an existing API with gate’s MCP server builder, this is another piece an OpenAPI spec has no way to express on its own, the same gap we noted for Tasks and sampling — an endpoint definition says nothing about whether the underlying resource is ever edited after the model first reads it.
resources/read triggered by an update notification as to one the model asked for on its own — a push channel is still just a reason to make a call, not an exemption from the policy in front of it. And a resource that updates on its own is one more thing worth running through gate’s free MCP security scanner before you trust what shows up in it unattended.The bottom line
Resource subscriptions let a client ask a server to say “this changed” instead of checking back on its own, for the narrow set of resources that are actually live. They carry no content, no latency guarantee, and no obligation on either side to implement them — useful exactly where a resource genuinely moves mid-session, and simply absent everywhere else. See tools, resources, and prompts for how this fits into MCP’s broader primitive model.