Suppose an assistant is preparing a monthly report. It collects documents, asks a model to summarize them, saves the report, then emails a link. Halfway through, the person notices the wrong month and presses Cancel. The report disappears from the screen. A minute later, an email arrives.
A useful cancellation contract follows the job through its remaining work and records what already happened. For this report, that means preventing steps that have not started, asking running operations to stop, and accounting for a report or email already created. The screen needs enough of that history to explain the outcome.
Give the job an identity beyond the open page
The browser can stop receiving an answer. Its AbortController API can abort a fetch request and response consumption. A background job already accepted by an application still needs its own cancellation path. Closing the page does not define what that application should do with its queued report.
In this example, the application assigns a job ID before work begins and keeps its status on the server. Cancel names that job. After reopening the page, the person can see whether it is still running and what it produced. The same identity connects the request to the document lookup, model call and email attempt.
The server first records that cancellation was requested. The interface can then show “Stopping…” while the worker handles it. “Cancelled” needs a defined meaning: for this report, the worker has stopped or accounted for its running operations, and no remaining report steps will start. A lost connection while requesting cancellation leaves the outcome unconfirmed until the application reads the job status again.
Carry cancellation into the running work
A queued report is the straightforward case. The queue removes it or the worker checks its saved cancellation state before starting. Either design needs to prevent report work from starting after cancellation is accepted.
For a running report, the application stops starting later steps and passes a cancellation request to the model or tool operation already underway, using whatever cancellation mechanism that service provides. If that operation cannot be interrupted, the application may have to wait for it to return. Its result can be discarded while the later save and email steps remain blocked.
Cancellation can require cooperation even inside one process. In Python, Task.cancel() requests cancellation by arranging an exception in the coroutine. The coroutine can perform cleanup and can also suppress that exception. A successful call to the method therefore does not prove the task has stopped.
The report worker needs to preserve the cancellation request through cleanup. Treating it as a temporary failure and retrying the summarization would restart work the person asked to stop. Cleanup can release a resource or remove an incomplete temporary file without advancing to the email step.
Resolve the race with completion
Suppose the model returns just as Cancel reaches the server. The worker is ready to save the report; the cancellation handler is ready to stop it. Checking a flag and then saving in separate, unprotected steps leaves a gap in which both can proceed.
The application needs one enforced order for accepting cancellation and saving the report. A database transaction can check the cancellation state and save the report as one operation. If cancellation wins, the save is refused. If the save wins, the report exists and the status must account for it. The application can still stop an email step that has not started. A finished job stays finished when a late cancellation request arrives.
The Model Context Protocol’s cancellation specification makes signaling depend on transport. With Streamable HTTP, closing the response stream signals cancellation, and the server must treat the disconnect as request cancellation. With stdio, the client sends a cancellation notification. That notification may arrive after processing has completed, even after a response was sent; a receiver may also ignore it when the request cannot be cancelled. The application still needs to account for the work actually performed.
Keep track of the effects that remain
A database transaction in the report application cannot recall an email already accepted by another service. Cancellation and the email service’s acceptance can race across that boundary. If the email request times out, the application may initially be unable to tell whether it was accepted.
In this example, the status might say “Report saved; email not started” or “Report saved; email outcome unconfirmed.” Once acceptance is known, the application can say that the email service accepted the message, without claiming that it reached the recipient. The status should describe the stage the evidence establishes.
Deleting a saved report is a separate product decision. It might leave a broken link in an email already sent. A product can offer deletion, or a correction can follow an earlier message, but those actions need their own recorded outcomes. Cancellation alone does not reverse effects already committed.
Repeated requests also need a stable meaning. Pressing Cancel twice should target the same job without creating more work. Retrying an uncertain email call needs protection against a duplicate send. An idempotency key is an operation identifier that a service can use to recognize a retry and avoid repeating its effect. AWS’s explanation of safe retries describes this approach, including the need to retain the identifier. The email service’s actual retry and retention contract determines whether that protection is available. Without it, the application needs to establish the first attempt’s outcome before retrying, or leave the send unresolved.
Test where the outcome can change
A cancellation test becomes useful when it stops the report at a known point. The expected result should include the stored job status and the effects visible outside the application.
| Cancel arrives | Evidence to inspect |
|---|---|
| While the report is queued | No worker starts it after cancellation is accepted. |
| During summarization | The running call is stopped or accounted for; no save or email step follows. |
| Alongside the save | The recorded order explains whether a saved report remains. |
| After an email request times out | The outcome stays unconfirmed until reconciled; a retry cannot silently send a duplicate. |
Repeat the test with a second Cancel and with the page closed, then reopen the job. The cancellation contract should still explain the last committed step and what, if anything, is waiting to stop. For the person who selected the wrong month, that determines whether starting again will leave an unwanted report or a second email behind.