parallel-calls
Two Clients connected to the same server, each with a call_tool in flight
at once. The meet tool is a rendezvous: a handler signals its own arrival,
then blocks until every named peer has arrived too — so neither call can return
unless the server runs both handlers concurrently. Each caller's
progress_callback= sees only the notifications for its request — each
Client is a separate connection, so there's no shared wire for them to cross
on.
Run it
The tested legs run in-memory (Client(server)); the identical main body
works unchanged over HTTP — both clients just reach the same server. Under
--http the client self-hosts that server on a free port, runs, then tears it
down:
# --legacy because handler-emitted progress is dropped on the modern
# streamable-HTTP path today (see Caveats).
uv run python -m stories.parallel_calls.client --http --legacy
# same, against the lowlevel-API server variant
uv run python -m stories.parallel_calls.client --http --legacy --server server_lowlevel
There is no stdio run for this story: the stdio default spawns a fresh server subprocess per connection, so two clients there could never rendezvous.
What to look at
client.py— the two visibleClient(targets(), mode=...)blocks. Each connection is constructed insideattend(...);targets()yields a fresh target on every call and both land on the same server instance. The two blocks run in oneanyiotask group.server.py— thearrivalsbarrier. Each handler sets its ownanyio.Eventthen waits for every peer's. A server that processed requests sequentially would never set the second event, so the client would time out — the timeout is the concurrency assertion. No sleeps.client.py—progress_callback=per call. Each call passes its own callback;received == {"a": ["a"], "b": ["b"]}shows each connection delivered its own progress, and — combined with the rendezvous — that both calls were genuinely in flight at once.server_lowlevel.py— same wire contract on the lowlevelServer, reporting viactx.session.report_progress(...).
Caveats
- Over Streamable HTTP in the modern (2026-07-28) era, handler-emitted progress
is currently dropped (the single-exchange dispatch context no-ops
notify()). In-memory (both eras) and legacy-era HTTP deliver progress correctly — hence the--legacyabove.
Spec
See also
streaming/ (progress + cancellation on one call), reconnect/ (the other
multi-connection client), tools/ (basics).