Skip to content

Commit bff7f69

Browse files
committed
Add draft cancellation docs
1 parent f421176 commit bff7f69

File tree

8 files changed

+138
-54
lines changed

8 files changed

+138
-54
lines changed

docs/docs.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,12 @@
7676
"protocol/transports",
7777
"protocol/schema",
7878
{
79-
"group": "Unstable",
79+
"group": "Draft: In Progress and May Change",
8080
"hidden": true,
81-
"pages": ["protocol/schema.unstable"]
81+
"pages": [
82+
"protocol/draft/cancellation",
83+
"protocol/draft/schema"
84+
]
8285
}
8386
]
8487
},
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
---
2+
title: "Cancellation"
3+
description: "Mechanisms for request cancellation"
4+
---
5+
6+
ACP uses JSON-RPC 2.0 for making requests and getting responses.
7+
8+
The JSON-RPC specification doesn't define any standard mechanism for request cancellation and keeps it up to the implementation.
9+
10+
## `request/cancel` Notification
11+
12+
In order to provide a consistent approach to cancellation, ACP defines a `request/cancel` notification that can be sent to cancel requests if the recipient declared support.
13+
14+
- A party **MAY** declare `{ cancellationCapabilities: {} }` in their capabilities to handle `request/cancel` notifications for their requests.
15+
- If an implementation declares support, it **MUST** handle `request/cancel` notifications for its requests.
16+
- The other party **MAY** send `request/cancel` to cancel requests if the recipient declared support.
17+
- If an implementation does not declare support, `request/cancel` notifications **SHOULD** be ignored.
18+
- Either party **MAY** send `request/cancel` regardless of if they declare support themselves, as long as the recipient supports cancellation.
19+
- Until capabilities are exchanged, requests **MUST NOT** be cancelled. The `initialize` request cannot be cancelled as it occurs before capability exchange.
20+
21+
Cancellation will remain optional as it might not be implementable in all clients or servers. For example if the implementation uses a single threaded synchronous programming language then there is little it can do to react to a `request/cancel` notification.
22+
23+
When a `request/cancel` notification is received by a supporting implementation, the implementation:
24+
25+
- **MUST** cancel the corresponding request activity and all nested activities
26+
- **MAY** choose how quickly to honor cancellation requests. For example, they may finish sending any pending notifications before responding
27+
- **MUST** send one of these responses for the original request:
28+
- A valid response with appropriate data (such as partial results or cancellation marker)
29+
- An error response with code [`-32800` (Request Cancelled)](./schema#errorcode)
30+
31+
The calling side **MAY** implement graceful cancellation processing by waiting for the response from the remote side.
32+
33+
Cancellation **MAY** also be done explicitly on a per-feature basis within the protocol to cover specific scenarios (e.g., cancellation of a [prompt turn](./prompt-turn#cancellation))
34+
35+
## Internal Cancellation
36+
37+
Requests can also be cancelled internally by the executing party without receiving `request/cancel`:
38+
39+
- **Client-side examples**: User closes IDE, switches to different project, file becomes unavailable
40+
- **Agent-side examples**: LLM context limit reached, internal timeout, resource constraints
41+
42+
When internal cancellation occurs, the executing party **MUST**:
43+
44+
- Send the same `-32800` (Cancelled) error response as if `request/cancel` was received
45+
- Ensure consistent behavior regardless of cancellation source
46+
47+
## Example: Cascading Cancellation Flow
48+
49+
```mermaid
50+
sequenceDiagram
51+
participant Client
52+
participant Agent
53+
54+
Note over Client,Agent: 1. Session prompt in progress
55+
Client->>Agent: session/prompt (id=1, "Analyze file X")
56+
Agent-->>Client: session/update (agent started processing)
57+
58+
Note over Client,Agent: 2. Agent makes concurrent requests
59+
Agent->>Client: terminal/create (id=2, "grep pattern file.txt")
60+
Agent->>Client: session/request_permission (id=3, "read sensitive file")
61+
62+
Note over Client,Agent: 3. Client cancels the prompt turn
63+
Client->>Agent: session/cancel (sessionId)
64+
65+
Note over Client,Agent: 4. Agent cascades cancellation internally
66+
Agent->>Client: request/cancel (id=2) [terminal request]
67+
Agent->>Client: request/cancel (id=3) [permission request]
68+
69+
Note over Client,Agent: 5. Client confirms individual cancellations
70+
Client->>Agent: response to id=2 (error -32800 "Cancelled")
71+
Client->>Agent: response to id=3 (error -32800 "Cancelled")
72+
73+
Note over Client,Agent: 6. Agent completes prompt cancellation
74+
Agent->>Client: response to id=1 (stopReason: "cancelled")
75+
```
Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -167,9 +167,9 @@ This capability is not part of the spec yet, and may be removed or changed at an
167167

168168
Cancels an ongoing request.
169169

170-
This is a notification sent by the client to cancel any ongoing request.
170+
This is a notification sent by the agent to cancel any ongoing request.
171171

172-
Upon receiving this notification, the Agent:
172+
Upon receiving this notification, the Client:
173173

174174
1. MUST cancel the corresponding request activity and all nested activities
175175
2. MAY send any pending notifications.
@@ -876,9 +876,9 @@ This capability is not part of the spec yet, and may be removed or changed at an
876876

877877
Cancels an ongoing request.
878878

879-
This is a notification sent by the client to cancel any ongoing request.
879+
This is a notification sent by the agent to cancel any ongoing request.
880880

881-
Upon receiving this notification, the Agent:
881+
Upon receiving this notification, the Client:
882882

883883
1. MUST cancel the corresponding request activity and all nested activities
884884
2. MAY send any pending notifications.
@@ -2034,7 +2034,8 @@ and use the reserved range (-32000 to -32099) for protocol-specific errors.
20342034

20352035
This capability is not part of the spec yet, and may be removed or changed at any point.
20362036

2037-
Execution of the method was aborted due to a cancellation request from the caller.
2037+
Execution of the method was aborted either due to a cancellation request from the caller or
2038+
because of resource constraints or shutdown.
20382039

20392040
</ResponseField>
20402041

docs/rfds/request-cancellation.mdx

Lines changed: 45 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ This creates the following inconveniences:
2020

2121
## What we propose to do about it
2222

23-
Implement an **optional** `$/cancelRequest` notification method (inspired by the Language Server Protocol) that uses JSON-RPC 2.0 notification format, allowing either party (client or agent) to cancel any outstanding request by its ID.
23+
Implement an **optional** `request/cancel` notification method (inspired by the Language Server Protocol) to both Agent and Client that uses JSON-RPC 2.0 notification format, allowing either party (client or agent) to cancel any outstanding request by its ID.
2424

2525
The mechanism will be:
2626

@@ -39,10 +39,10 @@ Once implemented, this enables:
3939
- **SDK integration layer**: Default mechanism that ACP SDKs can automatically wire to native language cancellation (C# CancellationToken, Kotlin Job, Go context.Context, JavaScript AbortController, etc.)
4040
- Individual JSON-RPC request cancellation without affecting other concurrent requests
4141
- Universal fallback for any request when feature-specific cancellation methods don't exist
42-
- Consistent cancellation behavior from both external `$/cancelRequest` and internal cancellation triggers
42+
- Consistent cancellation behavior from both external `request/cancel` and internal cancellation triggers
4343
- Standard error response (`-32800`) or partial results when requests are cancelled regardless of cancellation source
4444

45-
The mechanism complements existing `session/cancel` for prompt turns while providing granular per-request control.
45+
In a future version, we could potentially deprecate the `session/cancel` notification in favor of the more general approach, as it would still provide the same functionality but with more flexibility and consistency.
4646

4747
## Implementation details and plan
4848

@@ -57,22 +57,24 @@ interface CancellationCapabilities {}
5757

5858
interface ClientCapabilities {
5959
// existing capabilities...
60-
cancellation?: CancellationCapabilities;
60+
cancellationCapabilities?: CancellationCapabilities;
6161
}
6262

6363
interface AgentCapabilities {
6464
// existing capabilities...
65-
cancellation?: CancellationCapabilities;
65+
cancellationCapabilities?: CancellationCapabilities;
6666
}
6767
```
6868

69+
Supplying `{}` will indicate that cancellation is supported, while following the same pattern elsewhere for allowing more granular capabilities in the future.
70+
6971
#### Cancellation Method
7072

71-
Add the `$/cancelRequest` notification method to the JSON-RPC protocol:
73+
Add the `request/cancel` notification method to the JSON-RPC protocol:
7274

7375
```typescript
7476
interface CancelNotification {
75-
method: "$/cancelRequest";
77+
method: "request/cancel";
7678
params: {
7779
requestId: string | number; // ID of request to cancel
7880
};
@@ -83,62 +85,63 @@ interface CancelNotification {
8385

8486
#### Prerequisites
8587

86-
- A party **MUST** declare `{ cancellation: {} }` in their capabilities to handle `$/cancelRequest` notifications for their requests
87-
- The other party can send `$/cancelRequest` to cancel requests if the recipient declared support
88+
- A party **MAY** declare `{ cancellationCapabilities: {} }` in their capabilities to handle `request/cancel` notifications for their requests
89+
- The other party can send `request/cancel` to cancel requests if the recipient declared support
8890
- Until capabilities are exchanged, **NO** requests can be cancelled
8991
- The `initialize` request **CANNOT** be cancelled as it occurs before capability exchange
9092
- Any requests after `initialize` (which performs capability negotiation) **CAN** be cancelled if the recipient declared support
9193

9294
#### Responsibility for Support Checking
9395

94-
The **calling party** (sender of `$/cancelRequest`) **MUST**:
96+
The **calling party** (sender of `request/cancel`) **MUST**:
9597

96-
- Check the recipient's capabilities before sending `$/cancelRequest`
97-
- Only send `$/cancelRequest` if the recipient declared `cancellation: {}`
98-
- Not send `$/cancelRequest` notifications to parties that don't support them
98+
- Check the recipient's capabilities before sending `request/cancel`
99+
- Only send `request/cancel` if the recipient declared `cancellationCapabilities: {}`
100+
- Not send `request/cancel` notifications to parties that don't support them
99101

100102
The **receiving party** is **NOT** required to:
101103

102104
- Perform special handling for unsupported cancellation requests
103-
- Return custom errors for unsupported `$/cancelRequest` notifications
105+
- Return custom errors for unsupported `request/cancel` notifications
104106

105107
#### Cancellation Processing
106108

107-
When a `$/cancelRequest` notification is received by a supporting implementation:
109+
When a `request/cancel` notification is received by a supporting implementation:
110+
111+
1. MUST cancel the corresponding request activity and all nested activities
112+
2. MAY send any pending notifications.
113+
3. MUST send one of these responses for the original request:
108114

109-
1. **MUST** cancel the corresponding request activity and all nested activities
110-
2. **MUST** send one of these responses for the original request:
111-
- Error response with code `-32800` (Cancelled)
112-
- Valid response with appropriate data (partial results or cancellation marker)
115+
- Valid response with appropriate data (partial results or cancellation marker)
116+
- Error response with code `-32800` (Cancelled)
113117

114118
#### Internal Cancellation
115119

116-
Requests can also be cancelled internally by the executing party without receiving `$/cancelRequest`:
120+
Requests can also be cancelled internally by the executing party without receiving `request/cancel`:
117121

118122
- **Client-side examples**: User closes IDE, switches to different project, file becomes unavailable
119123
- **Agent-side examples**: LLM context limit reached, internal timeout, resource constraints
120124

121125
When internal cancellation occurs, the executing party **SHOULD**:
122126

123-
1. Stop the request processing immediately
124-
2. Send the same `-32800` (Cancelled) error response as if `$/cancelRequest` was received
125-
3. Ensure consistent behavior regardless of cancellation source
127+
1. Send the same `-32800` (Cancelled) error response as if `request/cancel` was received
128+
2. Ensure consistent behavior regardless of cancellation source
126129

127130
### Error Code
128131

129132
Add standard JSON-RPC error code `-32800` for cancelled requests:
130133

131134
- Code: `-32800`
132-
- Message: "Cancelled"
133-
- Meaning: The request was cancelled
135+
- Message: "Request cancelled"
136+
- Meaning: Execution of the method was aborted either due to a cancellation request from the caller or because of resource constraints or shutdown.
134137

135138
## Frequently asked questions
136139

137140
### What alternative approaches did you consider, and why did you settle on this one?
138141

139142
The core need is to add **granular cancellation** as a general mechanism for individual JSON-RPC requests, while **feature-specific cancellation methods** (like `session/cancel`) remain useful for cases requiring additional domain semantics.
140143

141-
We selected the **LSP-style `$/cancelRequest`** approach because:
144+
We selected the **LSP-style `request/cancel`** approach because:
142145

143146
- Serves as a **default cancellation layer** that SDK implementations can easily map to native language cancellation mechanisms
144147
- Proven pattern familiar to developers from LSP ecosystem
@@ -148,9 +151,9 @@ We selected the **LSP-style `$/cancelRequest`** approach because:
148151

149152
### How does this relate to existing cancellation mechanisms like `session/cancel`?
150153

151-
The `$/cancelRequest` mechanism is complementary to feature-specific cancellation:
154+
The `request/cancel` mechanism is complementary to feature-specific cancellation:
152155

153-
- `$/cancelRequest`: Generic cancellation for any JSON-RPC request by ID
156+
- `request/cancel`: Generic cancellation for any JSON-RPC request by ID
154157
- `session/cancel`: Feature-specific cancellation with additional semantics (e.g., cancels entire prompt turn context, triggers specific cleanup logic)
155158

156159
Both mechanisms serve different purposes:
@@ -161,16 +164,16 @@ Both mechanisms serve different purposes:
161164
- Structured cleanup for complex operations
162165
- Context-aware cancellation logic
163166

164-
**Generic `$/cancelRequest`** provides:
167+
**Generic `request/cancel`** provides:
165168

166169
- Default cancellation layer that bridges programming language cancellation mechanisms (C# CancellationToken, Kotlin Job, Go context.Context, etc.) with ACP
167170
- Universal fallback for any request when no feature-specific method exists
168171
- Simple ID-based targeting for SDK convenience
169172
- Standardized error responses
170173

171-
Implementations can use both: feature-specific methods for rich semantics, and `$/cancelRequest` for simple per-request cancellation.
174+
Implementations can use both: feature-specific methods for rich semantics, and `request/cancel` for simple per-request cancellation.
172175

173-
Note: it is possible that `session/cancel` could be replaced by the more generic `$/cancelRequest` in future versions of the protocol.
176+
Note: it is possible that `session/cancel` could be replaced by the more generic `request/cancel` in future versions of the protocol.
174177

175178
#### Example: Cascading Cancellation Flow
176179

@@ -191,8 +194,8 @@ sequenceDiagram
191194
Client->>Agent: session/cancel (sessionId)
192195
193196
Note over Client,Agent: 4. Agent cascades cancellation internally
194-
Agent->>Client: $/cancelRequest (id=2) [terminal request]
195-
Agent->>Client: $/cancelRequest (id=3) [permission request]
197+
Agent->>Client: request/cancel (id=2) [terminal request]
198+
Agent->>Client: request/cancel (id=3) [permission request]
196199
197200
Note over Client,Agent: 5. Client confirms individual cancellations
198201
Client->>Agent: response to id=2 (error -32800 "Cancelled")
@@ -226,10 +229,10 @@ This ensures complete cleanup and prevents resource leaks.
226229

227230
Cancellation support is **optional** in the current protocol version and declared through capabilities:
228231

229-
- Implementations **MAY** support handling cancellation by declaring `supportsRequestCancellation: true`
230-
- If an implementation declares support, it **MUST** handle `$/cancelRequest` notifications for its requests
231-
- If an implementation does not declare support, `$/cancelRequest` notifications should be ignored
232-
- The other party can send `$/cancelRequest` regardless of their own declared support
232+
- Implementations **MAY** support handling cancellation by declaring `cancellationCapabilities: {}`
233+
- If an implementation declares support, it **MUST** handle `request/cancel` notifications for its requests
234+
- If an implementation does not declare support, `request/cancel` notifications should be ignored
235+
- The other party can send `request/cancel` regardless of their own declared support
233236
- The `initialize` request **CANNOT** be cancelled regardless of capability support
234237
- Any requests after `initialize` **CAN** be cancelled if the recipient declared support
235238

@@ -239,20 +242,21 @@ When cancellation is supported:
239242
- Implementations **MAY** choose how quickly to honor cancellation requests
240243
- Implementations **MAY** return partial results instead of errors when appropriate
241244

242-
**Future consideration**: There is a proposal to make basic `$/cancelRequest` support **mandatory** in the next major protocol version to ensure consistent cancellation behavior across all ACP implementations and improve SDK developer experience.
245+
Cancellation will remain optional as it might not be implementable in all clients or servers. For example if the implementation uses a single threaded synchronous programming language then there is little it can do to react to a `request/cancel` notification.
243246

244247
### Example scenarios:
245248

246249
1. **Client-to-Agent cancellation only**: Client declares support=false, Agent declares support=true
247-
- Client can send `$/cancelRequest` to cancel agent's requests
248-
- Agent cannot send `$/cancelRequest` to client (client will ignore)
250+
- Client can send `request/cancel` to cancel agent's requests
251+
- Agent cannot send `request/cancel` to client (client will ignore)
249252

250253
2. **Bidirectional cancellation**: Both declare support=true
251254
- Both parties can cancel each other's requests
252255

253256
3. **No cancellation**: Both declare support=false (or omit the capability)
254-
- No `$/cancelRequest` notifications are processed
257+
- No `request/cancel` notifications are processed
255258

256259
## Revision history
257260

258261
- 2025-11-13: Initial version converted from PR #183
262+
- 2025-12-05: Updated with current implementation.

0 commit comments

Comments
 (0)