Skip to content

Commit 795ecef

Browse files
Merge pull request modelcontextprotocol#47 from modelcontextprotocol/justin/timeouts
Notification to cancel a previous request
2 parents 3178139 + b9d1451 commit 795ecef

File tree

3 files changed

+137
-3
lines changed

3 files changed

+137
-3
lines changed

docs/spec/cancellation.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
---
2+
title: Cancellation
3+
type: docs
4+
weight: 5
5+
---
6+
7+
MCP supports request cancellation through a dedicated notification message. This allows either party to signal that they are no longer interested in the result of an in-flight request.
8+
9+
## Cancellation Protocol
10+
11+
When a party wants to cancel a request, they send a `cancelled` notification containing the ID of the request they wish to cancel:
12+
13+
For example, initiating a request:
14+
15+
```json
16+
{
17+
"jsonrpc": "2.0",
18+
"id": "abc123",
19+
"method": "resources/read",
20+
"params": {
21+
"uri": "example://large-file"
22+
}
23+
}
24+
```
25+
26+
… then some time later, cancelling that request:
27+
28+
```json
29+
{
30+
"jsonrpc": "2.0",
31+
"method": "cancelled",
32+
"params": {
33+
"requestId": "abc123",
34+
"reason": "User interrupted operation"
35+
}
36+
}
37+
```
38+
39+
## Handling Cancellation
40+
41+
Upon receiving a cancellation notification, the receiver SHOULD:
42+
43+
1. Stop any processing related to the cancelled request
44+
2. Free any resources associated with that request
45+
3. Not send a response for the cancelled request (even if already computed)
46+
47+
However, due to race conditions and message latency:
48+
49+
- The notification may arrive after processing is complete
50+
- A response may already be in transit when cancellation is received
51+
- Multiple cancellation notifications for the same request ID may be received
52+
53+
## Special Cases
54+
55+
### Initialize Request
56+
57+
The client MUST NOT attempt to cancel its `initialize` request. Cancelling initialization leaves the protocol in an undefined state.
58+
59+
### Long-Running Operations
60+
61+
For long-running operations that support progress notifications, progress notifications SHOULD stop being sent after receiving cancellation.
62+
63+
## Best Practices
64+
65+
1. Implement cancellation handlers for all long-running operations
66+
2. Free resources promptly when receiving cancellation
67+
3. Make cancellation handling idempotent
68+
4. Include meaningful reason strings to aid debugging
69+
5. Log cancellations appropriately for troubleshooting

schema/schema.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,36 @@
8787
],
8888
"type": "object"
8989
},
90+
"CancelledNotification": {
91+
"description": "This notification can be sent by either side to indicate that it is cancelling a previously-issued request.\n\nThe request SHOULD still be in-flight, but due to communication latency, it is always possible that this notification MAY arrive after the request has already finished.\n\nThis notification indicates that the result will be unused, so any associated processing SHOULD cease.\n\nA client MUST NOT attempt to cancel its `initialize` request.",
92+
"properties": {
93+
"method": {
94+
"const": "cancelled",
95+
"type": "string"
96+
},
97+
"params": {
98+
"properties": {
99+
"reason": {
100+
"description": "An optional string describing the reason for the cancellation. This MAY be logged or presented to the user.",
101+
"type": "string"
102+
},
103+
"requestId": {
104+
"$ref": "#/definitions/RequestId",
105+
"description": "The ID of the request to cancel.\n\nThis MUST correspond to the ID of a request previously issued in the same direction."
106+
}
107+
},
108+
"required": [
109+
"requestId"
110+
],
111+
"type": "object"
112+
}
113+
},
114+
"required": [
115+
"method",
116+
"params"
117+
],
118+
"type": "object"
119+
},
90120
"ClientCapabilities": {
91121
"description": "Capabilities a client may support. Known capabilities are defined here, in this schema, but this is not a closed set: any client can define its own, additional capabilities.",
92122
"properties": {
@@ -120,6 +150,9 @@
120150
},
121151
"ClientNotification": {
122152
"anyOf": [
153+
{
154+
"$ref": "#/definitions/CancelledNotification"
155+
},
123156
{
124157
"$ref": "#/definitions/InitializedNotification"
125158
},
@@ -1659,6 +1692,9 @@
16591692
},
16601693
"ServerNotification": {
16611694
"anyOf": [
1695+
{
1696+
"$ref": "#/definitions/CancelledNotification"
1697+
},
16621698
{
16631699
"$ref": "#/definitions/ProgressNotification"
16641700
},

schema/schema.ts

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,33 @@ export interface JSONRPCError {
114114
*/
115115
export type EmptyResult = Result;
116116

117+
/* Cancellation */
118+
/**
119+
* This notification can be sent by either side to indicate that it is cancelling a previously-issued request.
120+
*
121+
* The request SHOULD still be in-flight, but due to communication latency, it is always possible that this notification MAY arrive after the request has already finished.
122+
*
123+
* This notification indicates that the result will be unused, so any associated processing SHOULD cease.
124+
*
125+
* A client MUST NOT attempt to cancel its `initialize` request.
126+
*/
127+
export interface CancelledNotification extends Notification {
128+
method: "cancelled";
129+
params: {
130+
/**
131+
* The ID of the request to cancel.
132+
*
133+
* This MUST correspond to the ID of a request previously issued in the same direction.
134+
*/
135+
requestId: RequestId;
136+
137+
/**
138+
* An optional string describing the reason for the cancellation. This MAY be logged or presented to the user.
139+
*/
140+
reason?: string;
141+
};
142+
}
143+
117144
/* Initialization */
118145
/**
119146
* This request is sent from the client to the server when it first connects, asking it to begin initialization.
@@ -615,7 +642,7 @@ export interface CallToolResult extends Result {
615642

616643
/**
617644
* Whether the tool call ended in an error.
618-
*
645+
*
619646
* If not set, this is assumed to be false (the call was successful).
620647
*/
621648
isError?: boolean;
@@ -865,12 +892,12 @@ export interface ModelPreferences {
865892
export interface ModelHint {
866893
/**
867894
* A hint for a model name.
868-
*
895+
*
869896
* The client SHOULD treat this as a substring of a model name; for example:
870897
* - `claude-3-5-sonnet` should match `claude-3-5-sonnet-20241022`
871898
* - `sonnet` should match `claude-3-5-sonnet-20241022`, `claude-3-sonnet-20240229`, etc.
872899
* - `claude` should match any Claude model
873-
*
900+
*
874901
* The client MAY also map the string to a different provider's model name or a different model family, as long as it fills a similar niche; for example:
875902
* - `gemini-1.5-flash` could match `claude-3-haiku-20240307`
876903
*/
@@ -1013,6 +1040,7 @@ export type ClientRequest =
10131040
| ListToolsRequest;
10141041

10151042
export type ClientNotification =
1043+
| CancelledNotification
10161044
| ProgressNotification
10171045
| InitializedNotification
10181046
| RootsListChangedNotification;
@@ -1026,6 +1054,7 @@ export type ServerRequest =
10261054
| ListRootsRequest;
10271055

10281056
export type ServerNotification =
1057+
| CancelledNotification
10291058
| ProgressNotification
10301059
| LoggingMessageNotification
10311060
| ResourceUpdatedNotification

0 commit comments

Comments
 (0)