Overview
Cancellation allows agents to abort long-running operations before they complete. This is essential for operations like:- PDF generation for large documents (5-60 seconds)
- AI operations like summarization (3-30 seconds)
- Image processing (2-20 seconds)
- Large file exports (10-120 seconds)
Why Cancellation Matters
Design Rationale
Without cancellation, agents face a dilemma:- Wait: User might cancel the request, but operation keeps running
- Disconnect: Loses the entire session, must reconnect for next operation
Design Decisions
| Decision | Choice | Alternatives Considered | Rationale |
|---|---|---|---|
| Cancellation identifier | Auto-generated callId | Progress token, manual IDs | Every call should be cancellable, not just those with progress tracking. Auto-generation reduces developer burden. |
| JavaScript API | AbortSignal support | Custom cancel methods, Promise extensions | AbortSignal is a web standard that developers already know from fetch(). Reusing familiar patterns reduces learning curve. |
| Partial results | Not supported | Include partial data on cancel | For browser operations (PDF, images, canvas), partial results are rarely useful. A half-generated PDF is not a valid PDF. |
| Capability flag | Not required | cancellable: boolean per capability | If an operation completes before cancel arrives, no harm done. Adding flags creates unnecessary complexity. |
| Cancel acknowledgment | Cancel returns confirmation | Fire-and-forget | Confirmation lets agents know if cancellation succeeded, enabling better error handling and UX. |
What We Explicitly Chose NOT to Do
- No partial results — Half-processed outputs are typically useless for browser operations
- No
cancellablecapability flag — Adds complexity without clear benefit - No progress-token-based cancellation — Ties cancellation to progress, but you might want to cancel without tracking progress
Protocol Specification
Call ID
Every capability call includes acallId for identification:
callId is not provided, the agent-side library MUST generate a unique identifier (e.g., UUID).
Cancel Request (Agent → App)
Cancel Response (App → Agent)
Modified Call Response
When a call is cancelled, its response includes acancelled flag:
Using AbortSignal (Recommended)
The recommended agent-side API uses the standardAbortSignal pattern:
Why AbortSignal?
Developers already use this pattern withfetch():
App-Side Implementation
Apps implement cancellation usingAbortController internally:
Example: Cancellable PDF Generation
Transport-Specific Handling
Puppeteer/Playwright
postMessage
WebSocket
Edge Cases
| Scenario | Behavior |
|---|---|
| Cancel non-existent callId | Return { cancelled: false, reason: 'Operation not found' } |
| Cancel already-completed call | Return { cancelled: false, reason: 'Operation already completed' } |
| Cancel already-cancelled call | Return { cancelled: true } (idempotent) |
| Multiple cancels for same callId | All return same result (idempotent) |
| Call completes while cancel in flight | Call result returned normally, cancel returns { cancelled: false } |
| Cancel without active session | Return error { code: 'NOT_INITIALIZED' } |
Timing Considerations
- Cancel arrives before operation starts: Operation is skipped, response has
cancelled: true - Cancel arrives during operation: Operation is aborted, response has
cancelled: true - Cancel arrives after completion: Cancel response has
cancelled: false, original result is returned
Relationship to Timeout
Thetimeout option in calls is complementary to cancellation:
| Mechanism | Who decides | When triggered |
|---|---|---|
timeout option | Agent, at call time | Automatic, after specified duration |
AbortSignal | Agent, any time | Manual, based on external events |
cancelled: true in the response. Apps cannot distinguish between timeout-triggered and signal-triggered cancellation (and SHOULD NOT need to).