subscriptions
Server-originated change notifications on the 2026-07-28 protocol. A client
opens one subscriptions/listen request whose response is the stream; the
server publishes with ctx.notify_resource_updated(uri) /
ctx.notify_tools_changed() and the SDK does the wire work (ack-first,
per-stream filtering, subscription-id tagging). Replaces the handshake-era
resources/subscribe + standalone-GET notification path.
The client opens the stream with client.listen(...), edits a note it did
not subscribe to (silence), edits the one it did (a typed ResourceUpdated),
registers a tool at runtime (a typed ToolsListChanged, then re-lists and
calls it), and finally leaves the async with block, which ends the
subscription while the connection lives on.
Run it
# HTTP: the client self-hosts the server on a free port, runs, then tears it
# down (subscriptions/listen is 2026-era only)
uv run python -m stories.subscriptions.client --http
# same, against the lowlevel-API server variant
uv run python -m stories.subscriptions.client --http --server server_lowlevel
What to look at
client.py: the whole subscription is one context manager,async with client.listen(...) as sub. Entering waits for the server's acknowledgment, sosub.honoredis already in hand on the first line of the block. Events arrive as typed values fromanext(sub); the edit to the unsubscribed note never shows up, because the filter is enforced server-side. Leaving the block ends the subscription (over HTTP the SDK closes that request's response stream) and the session carries on, which the finalsearchcall proves.server.py: publishing is oneawait ctx.notify_*()line per change; the filter, the tagging, and the ack ordering are the SDK's job. Publishing with no subscribers is a no-op.server_lowlevel.py: the same machinery held by hand: anInMemorySubscriptionBus, handlers thatawait bus.publish(...), andListenHandler(bus)passed ason_subscriptions_listen=. A multi-replica deployment swaps the bus for one backed by its own pub/sub (MCPServer(subscriptions=...)on the high-level server).
Caveats
- 2026-era only: on a 2025 connection the method does not exist (clients there
use
resources/subscribeand unsolicited notifications instead), so the story pins the modern era and has no legacy leg. - No replay: events published while no stream is open are not queued. The contract after a dropped stream is re-listen and re-fetch.
Spec
Subscriptions, basic utilities
See also
streaming/ (request-scoped notifications), events/ (the events extension
on top of this channel, deferred), and the narrative versions:
docs/handlers/subscriptions.md (server) and docs/client/subscriptions.md
(client).