Repository navigation
feat(client): cancel an abandoned tools/call, prompts/get or resources/read - #1344
Merged
DaleSeo merged 3 commits intoOct 16, 2026
Conversation
…s/read A caller that drops `call_tool` (a `tokio::time::timeout`, a lost `select!` branch) abandoned the request locally while the server kept working on it. Send a best-effort `notifications/cancelled` for the request in flight when that happens. A drop guard, modelled on `PendingSubscriptionRequest`, is armed while the response is awaited and disarmed as soon as it arrives, so dropping the future while the client handler fulfils MRTR input requests cancels nothing. Closes modelcontextprotocol#1338 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DaleSeo
reviewed
Oct 12, 2026
Every request the client peer sends now goes through the drop guard, not only tools/call, prompts/get and resources/read. `initialize` is not sent through `Peer`, and the helper is client-only: a client must never cancel `initialize`, and from 2026-07-28 a server may send `notifications/cancelled` only to end a subscriptions/listen stream. The guard no longer unregisters a subscription that these requests never register, and the test helper fails when the call never reaches the point it is to be abandoned at instead of dropping the call anyway. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DaleSeo
reviewed
Oct 16, 2026
… in the guard Per review: server/discover is part of the handshake like initialize, so it goes back to plain send_request. AbandonedRequestGuard borrows the peer instead of cloning it, and the helper's doc keeps only the constraint a future editor must not break. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DaleSeo
approved these changes
Oct 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1338.
A caller that drops
call_tool(atokio::time::timeout, a lostselect!branch) abandoned the request locally while the server kept working on it. This sends a best-effortnotifications/cancelledfor the request in flight when that happens.How. Option 1 from the issue, with the constraints from the discussion there: a small drop guard, modelled on
PendingSubscriptionRequest, that holds the request id and the peer. It is armed while the response is awaited and disarmed as soon as the response arrives, so dropping the future while the client handler fulfils input requests cancels nothing.One difference from what I wrote in the issue. The guard sits in
call_tool_once/get_prompt_once/read_resource_oncerather than in the MRTR loops, becauseread_resource_oncewraps the send in cache handling that I didn't want to duplicate. The loops get it through those calls. It also means a dropped*_oncefuture is cancelled too. Say if you'd rather keep it to the loops.Tests (
tests/test_abandoned_request_cancel.rs): abandoned tool call; abandoned retry round (the retry's id is the one cancelled); prompt and resource; drop during local input handling sends nothing; completed call sends nothing. The first three fail without the change.No public API change.
cargo test --all-features, the non-localfeature set, clippy andcargo +nightly fmtpass.AI disclosure: written with Claude Code; I reviewed the change and ran the checks above.
🤖 Generated with Claude Code