Back to Python Sdk

standalone-get

examples/stories/standalone_get/README.md

2.0.0b13.2 KB
Original Source

standalone-get

Legacy mechanism (2025 handshake era). The 2026-07-28 protocol delivers server-initiated notifications over a subscriptions/listen stream instead of the standalone GET stream. TODO(maxisbey): unify once subscriptions/listen lands (#2901).

Server-initiated notifications/resources/list_changed delivered over the standalone GET SSE stream of a sessionful Streamable-HTTP connection. The add_note tool mutates the resource list and emits the notification with no related request; the client's message_handler receives it on the GET stream, awaits it on an anyio.Event, then re-lists to observe the change.

Run it

bash
# HTTP only — the standalone GET stream is a Streamable-HTTP feature. The
# client self-hosts the server on a free port, runs, then tears it down.
uv run python -m stories.standalone_get.client --http --legacy
# same, against the lowlevel-API server variant
uv run python -m stories.standalone_get.client --http --legacy --server server_lowlevel

# against a server you run yourself
uv run python -m stories.standalone_get.server --http --port 8000 &
SERVER_PID=$!
uv run python -m stories.standalone_get.client --http http://127.0.0.1:8000/mcp --legacy
kill "$SERVER_PID"

What to look at

  • client.pyClient(target, mode=mode, message_handler=on_message). Unsolicited notifications have no typed callback, so the catch-all message_handler is wired at construction — it (and the anyio.Event it sets) must exist before the connection does. The notification is not guaranteed to arrive before the tool result (different streams), so the body awaits the event, bounded by anyio.fail_after(5).
  • server.pyawait ctx.session.send_resource_list_changed(). MCPServer.add_resource does not auto-emit (unlike the TypeScript SDK's registerResource); the explicit call is the teaching point. Because send_*_list_changed() carries no related_request_id, the only route to the client is the standalone GET stream.

Caveats

  • DNS-rebinding protection is disabled via transport_security=NO_DNS_REBIND because the in-process httpx client sends no Origin header. Drop the kwarg for a real deployment.
  • Neither MCPServer nor lowlevel Server auto-advertises resources.listChanged: true in capabilities, and MCPServer exposes no knob to set it. A spec-conformant client that gates on the capability flag would skip the handler.
  • ctx.session.* is the interim path; a later release will shorten it.
  • Tool-triggered, not timer-driven, for harness determinism. "Server pushes on its own schedule" is not demonstrated.

Spec

List Changed Notification, Streamable HTTP — Listening for Messages

See also

stickynotes/ (list_changed inside a feature capstone), sse_polling/ (the other GET-stream story — resumability), json_response/ (what happens when the server can't stream).