What it does
The requestor sends a `tasks/cancel` request carrying a single `taskId` parameter to the receiver that owns the task. The receiver looks up the task, checks its current status, and — if the task is still in a non-terminal state (`working` or `input_required`) — transitions it to `cancelled` and returns the updated Task object in the response. The whole round-trip follows the standard JSON-RPC 2.0 request/response pattern: the requestor waits for the response before treating the cancellation as acknowledged. Both sides must have negotiated the tasks capability during the initialize handshake before any tasks methods are valid. The client must declare `tasks: {}` in ClientCapabilities and the server must echo `tasks: {}` in ServerCapabilities (returned in InitializeResult). Sending `tasks/cancel` to a server that did not advertise the tasks capability results in a `-32601 Method not found` error; receivers MUST reject the call if their own advertised capability does not cover tasks. Cancellation is best-effort and asynchronous at the work level. The response confirms the receiver has registered the intent, not that work has actually stopped. Expect the returned `task.status` to show `cancelled` immediately in the response when the receiver can honour the request synchronously, but the underlying worker thread may still be running for a brief window after the response is sent. Two error codes are significant: `-32602` is returned when the `taskId` refers to a task that is already in a terminal state (`completed`, `failed`, or `cancelled`), and `-32602` is also returned if `taskId` is unknown or has been purged past its TTL. There is no dedicated `-32603` path for cancellation — internal failures use standard JSON-RPC error codes.
When to use
When the user aborts a long-running operation.
When NOT to use
On every short request — use notifications/cancelled for those.
Notes
Capability gate is mandatory
Both the client's ClientCapabilities and the server's ServerCapabilities must include the `tasks` object before any tasks/* method is valid. Check the negotiated capabilities after initialize completes and short-circuit gracefully rather than sending tasks/cancel to a server that may return -32601.
Best-effort, not guaranteed stop
The SHOULD in the spec means some receivers may not stop work immediately — or at all, if the underlying operation has passed a point of no return (e.g., an irreversible external API call). After receiving the response, poll tasks/get and confirm status is 'cancelled' before assuming resources have been freed.
Terminal-state error is -32602, not a new code
Cancelling a task that is already completed, failed, or cancelled returns a standard -32602 Invalid Params error with the message 'Cannot cancel: already in terminal status'. Your error handler should branch on this specific message (or a server-defined error code field) rather than treating every -32602 as a cancellation collision.
Race condition with automatic completion
A task can transition to 'completed' or 'failed' in the milliseconds between your last tasks/get poll and the moment your tasks/cancel arrives. Always guard against -32602 on the cancel response — it is not a bug, it is normal TOCTOU behavior. Log it and move on rather than surfacing it as a user-visible error.
No pagination or cursor on this method
Unlike tasks/list, the tasks/cancel request takes only a single taskId and returns a single Task. There is no cursor, no batch cancel API, and no partial-success response. To cancel multiple tasks, issue individual tasks/cancel requests sequentially; MCP does not define a bulk cancel primitive.
Request parameters
| Name | Type | Purpose |
|---|---|---|
| taskId | string | Task to cancel. |
Response fields
| Name | Type | Purpose |
|---|---|---|
| task | Task | Updated state. |
Examples
Cancel
{ "method": "tasks/cancel", "params": { "taskId": "abc" } }
Common mistakes
❌ Expecting immediate stop
✅ Cancellation is best-effort — wait for status:'cancelled'.