fix(test-servers): address review on the cancellation showcase

- `slow_task` sleeps through a helper that removes both listeners on
  whichever outcome wins and returns early on an already-aborted signal.
  `{ once: true }` detaches on the event firing, not on the waiter losing
  interest, so the old loop left one listener per second attached and a
  60s run tripped MaxListenersExceededWarning.
- Report the outcome on stderr. A successful cancel closes the stream the
  result would travel on, so the tool's return value is undeliverable by
  construction — the README now points at the server's terminal rather
  than at a result panel that cannot show it.
- Drop a double cast the file's own next hunk shows is unnecessary, and a
  floating observer promise whose assertion was false anyway: `postSend`
  rethrows before awaiting the SSE wait, so the send rejects on abort
  whether or not that wait was cancelled.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hzwzS8UvBsr4yZm7o7sev
Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
cliffhall 2026-08-28 01:11:30 -04:00
commit 56d3c04643

View file

@ -168,15 +168,24 @@ Each config below is a ready-made server for exercising one feature by hand. Loa
#### Cancelling a call
`cancellation-modern-http.json` serves `slow_task`, which reports progress once
a second for up to 60 seconds and returns early — saying how far it got — if it
is cancelled. Connect with **Protocol Era = Modern**.
a second for up to 60 seconds and stops early if it is cancelled, printing how
far it got to **the server's terminal**. Connect with **Protocol Era = Modern**.
Run `slow_task` from the Tools tab and click **Cancel** after a few seconds. The
progress must stop immediately, and the result must read `Cancelled after Ns`.
On the broken build the progress kept arriving until the tool completed all 60
seconds, because the Inspector was sending the wrong cancellation signal
Watch the terminal you started the server in, run `slow_task` from the Tools
tab, and click **Cancel** after a few seconds. The progress must stop
immediately, the Inspector must report the call cancelled, and the server must
print `[slow_task] cancelled after Ns`. On the broken build the progress kept
arriving until the tool completed all 60 seconds and the server printed
`completed all 60s without being cancelled`, because the Inspector was sending
the wrong cancellation signal
([#2140](https://github.com/modelcontextprotocol/inspector/issues/2140)).
The server's terminal is the place to watch, not the Inspector's result panel:
cancellation closes the very stream the tool's result would travel on, so on a
successful cancel the tool's return value is undeliverable by construction and
the Inspector shows a cancelled call rather than a result. Which is the point —
what has to stop is the *work*, and only the server can report that.
The 2026-07-28 spec makes this
[transport-specific](https://modelcontextprotocol.io/specification/2026-07-28/basic/patterns/cancellation#transport-specific-cancellation):
for Streamable HTTP, **closing the request's SSE response stream is the