From e42c4bdc3175809af4a0a745da0e5837828269c5 Mon Sep 17 00:00:00 2001 From: David Savage Date: Wed, 28 May 2025 06:30:29 +0000 Subject: [PATCH 01/17] work in progress on extending protocol to support tasks that can be run in the background and reconnected to later --- schema/draft/schema.json | 80 ++++++++++++++++++++++++++++++++++++++++ schema/draft/schema.ts | 35 ++++++++++++++++++ 2 files changed, 115 insertions(+) diff --git a/schema/draft/schema.json b/schema/draft/schema.json index 62bfb110d..1221e9ed0 100644 --- a/schema/draft/schema.json +++ b/schema/draft/schema.json @@ -1943,6 +1943,59 @@ ], "type": "object" }, + "StartTaskRequest": { + "description": "Used by the client to start a task provided by the server.", + "properties": { + "method": { + "const": "tasks/start", + "type": "string" + }, + "params": { + "properties": { + "arguments": { + "additionalProperties": {}, + "type": "object" + }, + "name": { + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, + "StartTaskResult": { + "description": "Used by the by the server to provide a reference to the client for a task.", + "properties": { + "channels": { + "items": { + "$ref": "#/definitions/TaskUpdateChannel" + }, + "type": "array" + }, + "method": { + "const": "tasks/result", + "type": "string" + }, + "token": { + "$ref": "#/definitions/TaskToken" + } + }, + "required": [ + "channels", + "method", + "token" + ], + "type": "object" + }, "SubscribeRequest": { "description": "Sent from the client to request resources/updated notifications from the server whenever a particular resource changes.", "properties": { @@ -1970,6 +2023,33 @@ ], "type": "object" }, + "TaskToken": { + "description": "Used by the client to receive updates, join or cancel tasks on the server.", + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + "TaskUpdateChannel": { + "description": "Used by the server to provide update notifications to the client on progress of a task.", + "properties": { + "description": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, "TextContent": { "description": "Text provided to or from an LLM.", "properties": { diff --git a/schema/draft/schema.ts b/schema/draft/schema.ts index 2f359e0d6..6e6f8a23c 100644 --- a/schema/draft/schema.ts +++ b/schema/draft/schema.ts @@ -222,6 +222,41 @@ export interface ClientCapabilities { sampling?: object; } +/** + * Used by the client to start a task provided by the server. + */ +export interface StartTaskRequest { + method: "tasks/start"; + params: { + name: string; + arguments?: { [key: string]: unknown }; + }; +} + +/** + * Used by the client to receive updates, join or cancel tasks on the server. + */ +export interface TaskToken { + id: string +} + +/** + * Used by the server to provide update notifications to the client on progress of a task. + */ +export interface TaskUpdateChannel { + name: string; + description?: string; +} + +/** + * Used by the by the server to provide a reference to the client for a task. + */ +export interface StartTaskResult { + method: "tasks/result"; + token: TaskToken, + channels: TaskUpdateChannel[]; +} + /** * Capabilities that a server may support. Known capabilities are defined here, in this schema, but this is not a closed set: any server can define its own, additional capabilities. */ From 36687ff726c6e5319bc60131f2ea6354baaef8ce Mon Sep 17 00:00:00 2001 From: David Savage Date: Sat, 31 May 2025 09:45:30 +0000 Subject: [PATCH 02/17] update to use call tool semantics rather than start task --- schema/draft/schema.json | 178 +++++++++++++++++++++------------------ schema/draft/schema.ts | 38 +++++---- 2 files changed, 121 insertions(+), 95 deletions(-) diff --git a/schema/draft/schema.json b/schema/draft/schema.json index 1221e9ed0..da7273f75 100644 --- a/schema/draft/schema.json +++ b/schema/draft/schema.json @@ -20,6 +20,25 @@ }, "type": "object" }, + "AsyncToken": { + "description": "Used by the client to reference a previous async tool call submitted to the server.", + "type": [ + "string", + "integer" + ] + }, + "AsyncToolResult": { + "description": "Used by the by the server to provide a reference to the client for a previously submitted async tool call.", + "properties": { + "token": { + "$ref": "#/definitions/AsyncToken" + } + }, + "required": [ + "token" + ], + "type": "object" + }, "AudioContent": { "description": "Audio provided to or from an LLM.", "properties": { @@ -71,6 +90,35 @@ ], "type": "object" }, + "CallToolAsyncRequest": { + "description": "Used by the client to call a tool provided by the server and return a token to get the result or cancel the invocation at a later stage.", + "properties": { + "method": { + "const": "tools/call/async", + "type": "string" + }, + "params": { + "properties": { + "arguments": { + "additionalProperties": {}, + "type": "object" + }, + "name": { + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, "CallToolRequest": { "description": "Used by the client to invoke a tool provided by the server.", "properties": { @@ -143,6 +191,31 @@ ], "type": "object" }, + "CancelToolAsyncRequest": { + "description": "Used by the client to cancel a previous async tool call.", + "properties": { + "method": { + "const": "tools/cancel/async", + "type": "string" + }, + "params": { + "properties": { + "token": { + "$ref": "#/definitions/AsyncToken" + } + }, + "required": [ + "token" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, "CancelledNotification": { "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.", "properties": { @@ -561,6 +634,31 @@ ], "type": "object" }, + "GetToolAsyncResult": { + "description": "Used by the client to retrieve the result of an async tool call running on a server.", + "properties": { + "method": { + "const": "tools/get/async", + "type": "string" + }, + "params": { + "properties": { + "token": { + "$ref": "#/definitions/AsyncToken" + } + }, + "required": [ + "token" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, "ImageContent": { "description": "An image provided to or from an LLM.", "properties": { @@ -1943,59 +2041,6 @@ ], "type": "object" }, - "StartTaskRequest": { - "description": "Used by the client to start a task provided by the server.", - "properties": { - "method": { - "const": "tasks/start", - "type": "string" - }, - "params": { - "properties": { - "arguments": { - "additionalProperties": {}, - "type": "object" - }, - "name": { - "type": "string" - } - }, - "required": [ - "name" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" - }, - "StartTaskResult": { - "description": "Used by the by the server to provide a reference to the client for a task.", - "properties": { - "channels": { - "items": { - "$ref": "#/definitions/TaskUpdateChannel" - }, - "type": "array" - }, - "method": { - "const": "tasks/result", - "type": "string" - }, - "token": { - "$ref": "#/definitions/TaskToken" - } - }, - "required": [ - "channels", - "method", - "token" - ], - "type": "object" - }, "SubscribeRequest": { "description": "Sent from the client to request resources/updated notifications from the server whenever a particular resource changes.", "properties": { @@ -2023,33 +2068,6 @@ ], "type": "object" }, - "TaskToken": { - "description": "Used by the client to receive updates, join or cancel tasks on the server.", - "properties": { - "id": { - "type": "string" - } - }, - "required": [ - "id" - ], - "type": "object" - }, - "TaskUpdateChannel": { - "description": "Used by the server to provide update notifications to the client on progress of a task.", - "properties": { - "description": { - "type": "string" - }, - "name": { - "type": "string" - } - }, - "required": [ - "name" - ], - "type": "object" - }, "TextContent": { "description": "Text provided to or from an LLM.", "properties": { diff --git a/schema/draft/schema.ts b/schema/draft/schema.ts index 6e6f8a23c..50e5d66c7 100644 --- a/schema/draft/schema.ts +++ b/schema/draft/schema.ts @@ -223,10 +223,10 @@ export interface ClientCapabilities { } /** - * Used by the client to start a task provided by the server. + * Used by the client to call a tool provided by the server and return a token to get the result or cancel the invocation at a later stage. */ -export interface StartTaskRequest { - method: "tasks/start"; +export interface CallToolAsyncRequest { + method: "tools/call/async"; params: { name: string; arguments?: { [key: string]: unknown }; @@ -234,27 +234,35 @@ export interface StartTaskRequest { } /** - * Used by the client to receive updates, join or cancel tasks on the server. + * Used by the client to cancel a previous async tool call. */ -export interface TaskToken { - id: string +export interface CancelToolAsyncRequest { + method: "tools/cancel/async"; + params: { + token: AsyncToken + }; } /** - * Used by the server to provide update notifications to the client on progress of a task. + * Used by the client to retrieve the result of an async tool call running on a server. */ -export interface TaskUpdateChannel { - name: string; - description?: string; +export interface GetToolAsyncResult { + method: "tools/get/async"; + params: { + token: AsyncToken + }; } /** - * Used by the by the server to provide a reference to the client for a task. + * Used by the client to reference a previous async tool call submitted to the server. + */ +export type AsyncToken = string | number; + +/** + * Used by the by the server to provide a reference to the client for a previously submitted async tool call. */ -export interface StartTaskResult { - method: "tasks/result"; - token: TaskToken, - channels: TaskUpdateChannel[]; +export interface AsyncToolResult { + token: AsyncToken, } /** From a9972e69e6ef3de5f1636ed97d2a8f455da079a4 Mon Sep 17 00:00:00 2001 From: David Savage Date: Sat, 31 May 2025 12:40:15 +0000 Subject: [PATCH 03/17] Add a page to the specification text with mermaid diagrams and update protocol elements --- docs/specification/draft/basic/async.mdx | 98 +++++++++++++++++ schema/draft/schema.json | 132 +++++++++++++++++++---- schema/draft/schema.ts | 130 ++++++++++++++++++++-- 3 files changed, 334 insertions(+), 26 deletions(-) create mode 100644 docs/specification/draft/basic/async.mdx diff --git a/docs/specification/draft/basic/async.mdx b/docs/specification/draft/basic/async.mdx new file mode 100644 index 000000000..9931d1e6b --- /dev/null +++ b/docs/specification/draft/basic/async.mdx @@ -0,0 +1,98 @@ +--- +title: Async Tool Calls +--- + +**Protocol Revision**: draft + +The Model Context Protocol (MCP) defines a rigorous protocol for client-server +async call tool requests. + +## 1. Introduction + +### 1.1 Purpose and Scope + +The Model Context Protocol provides asyncronouse capabilities at the transport level, +enabling MCP clients to make asyncronous requests to call tools. + +### 1.2 Protocol Requirements + +Asynchronous tool calls is **OPTIONAL** for MCP implementations. When supported: + +- Implementations **MUST** follow established security best practices for their protocol. + +### 1.3 Standards Compliance + +- TODO note any relevant standards in this space + +### 2.1 Overview + +#### 2.1.1 Basic usage + +The following diagram shows a basic sequence of request, responses and notifications + +```mermaid +sequenceDiagram + actor Client + actor Server + Client->>Server: CallToolAsyncRequest + Server->>Client: CallToolAsyncResult + Server--)Client: ProgressNotification (1/2) + Server--)Client: ProgressNotification (2/2) + Client->>Server: GetAsyncResultRequest + Server->>Client: CallToolResult +``` + +#### 2.1.1 Join usage + +A client may join an existing tool call request. A server MAY send all or partial progress notifications to the client +```mermaid +sequenceDiagram + actor Client + actor Server + Client->>Server: JoinCallToolAsyncRequest + Server->>Client: CallToolAsyncResult + Server--)Client: ProgressNotification (3/4) + Server--)Client: ProgressNotification (4/4) + Client->>Server: GetAsyncResultRequest + Server->>Client: CallToolResult +``` + +Multiple clients MAY join an existing tool call request. +```mermaid +sequenceDiagram + actor Client1 + actor Client2 + actor Server + Client1->>Server: CallToolAsyncRequest + Server-->>Client1: CallToolAsyncResult + Server--)Client1: ProgressNotification (1/2) + Client2->>Server: JoinCallToolAsyncRequest + Server-->>Client2: CallToolAsyncResult + Server--)Client1: ProgressNotification (2/2) + Client1->>Server: GetAsyncResultRequest + Server-->>Client1: CallToolResult + Server--)Client2: ProgressNotification (2/2) + Client2->>Server: GetAsyncResultRequest + Server-->>Client2: CallToolResult +``` + +#### 2.1.1 Cancel usage + +A client may cancel any in progress call tool requests. +The server SHOULD notify all clients that have called or joined the async tool call of the cancellation +```mermaid +sequenceDiagram + actor Client1 + actor Client2 + actor Server + Client1->>Server: CallToolAsyncRequest + Server->>Client1: CallToolAsyncResult + Server--)Client1: ProgressNotification (1/3) + Client2->>Server: JoinCallToolAsyncRequest + Server->>Client2: CallToolAsyncResult + Server--)Client1: ProgressNotification (2/3) + Server--)Client2: ProgressNotification (2/3) + Client2--)Server: CancelToolAsyncNotification + Server--)Client1: CancelNotification + Server--)Client2: CancelNotification +``` \ No newline at end of file diff --git a/schema/draft/schema.json b/schema/draft/schema.json index da7273f75..03dd52991 100644 --- a/schema/draft/schema.json +++ b/schema/draft/schema.json @@ -27,18 +27,6 @@ "integer" ] }, - "AsyncToolResult": { - "description": "Used by the by the server to provide a reference to the client for a previously submitted async tool call.", - "properties": { - "token": { - "$ref": "#/definitions/AsyncToken" - } - }, - "required": [ - "token" - ], - "type": "object" - }, "AudioContent": { "description": "Audio provided to or from an LLM.", "properties": { @@ -91,7 +79,7 @@ "type": "object" }, "CallToolAsyncRequest": { - "description": "Used by the client to call a tool provided by the server and return a token to get the result or cancel the invocation at a later stage.", + "description": "Used by the client to call a tool provided by the server and return a token to get the result or cancel the call at a later stage.\n\nThe server SHOULD retain the result of the tool call for the number of seconds specified by keep_alive to enable the client to retrieve the result.\n\nThe server MUST respond with an CallToolAsyncResult.\n\nThe server MAY reduce the number of seconds specified by keep_alive if so the server MUST inform the client of the new keep_alive time in the CallToolAsyncResult", "properties": { "method": { "const": "tools/call/async", @@ -103,6 +91,9 @@ "additionalProperties": {}, "type": "object" }, + "keepAlive": { + "type": "integer" + }, "name": { "type": "string" } @@ -119,6 +110,26 @@ ], "type": "object" }, + "CallToolAsyncResult": { + "description": "Used by the server to provide a reference to the client for a previously submitted async tool call.\n\nThis MUST only be sent as the result of EITHER CallToolAsyncRequest or JoinToolAsyncResultRequest \n\nThe token SHOULD be valid to get or cancel the result for keepAalive seconds from the received time on the server.\n\nThe server SHOULD report the received time in unix time, e.g. seconds elapsed since 00:00:00 UTC on 1 January 1970\n\nThe is NOT", + "properties": { + "keepAlive": { + "type": "integer" + }, + "received": { + "type": "integer" + }, + "token": { + "$ref": "#/definitions/AsyncToken" + } + }, + "required": [ + "keepAlive", + "received", + "token" + ], + "type": "object" + }, "CallToolRequest": { "description": "Used by the client to invoke a tool provided by the server.", "properties": { @@ -192,7 +203,7 @@ "type": "object" }, "CancelToolAsyncRequest": { - "description": "Used by the client to cancel a previous async tool call.", + "description": "Used by the client to cancel a previous async tool call request.\n\nThe request SHOULD still be in-flight, but due to communication latency, it is always possible that this request MAY arrive after the request has already finished.\n\nThis request indicates that the result will be unused, so any associated processing SHOULD cease.\n\nThe server SHOULD send a CancelNotification to any clients that have.", "properties": { "method": { "const": "tools/cancel/async", @@ -298,6 +309,12 @@ { "$ref": "#/definitions/InitializeRequest" }, + { + "$ref": "#/definitions/CallToolAsyncRequest" + }, + { + "$ref": "#/definitions/JoinCallToolAsyncRequest" + }, { "$ref": "#/definitions/PingRequest" }, @@ -634,8 +651,8 @@ ], "type": "object" }, - "GetToolAsyncResult": { - "description": "Used by the client to retrieve the result of an async tool call running on a server.", + "GetToolAsyncResultRequest": { + "description": "Used by the client to request the result of an async async tool call running on a server to be sent to this client.\n\nThe request SHOULD still be in-flight, but due to communication latency, it is always possible that this request MAY arrive after the request has already finished.\n\nThe server MUST return a CallToolResult either with the result an error status if the result cannot be retrieved or a pending status if the call tool is still in progress.\n\nThe client SHOULD use ProgressNotification results to determin when to call this method.\n\nThe client MAY call this periodically if regular progress notifications are not recieved.\n\nThe server MAY cancel any in progress tool calls if the client submits too many GetToolAsyncResultRequest.\n\nThe server SHOULD send a CancelNotification to the client if the request has been cancelled with a reason.", "properties": { "method": { "const": "tools/get/async", @@ -987,6 +1004,35 @@ ], "type": "object" }, + "JoinCallToolAsyncRequest": { + "description": "Used by the client to reconnect to or extend the keep alive of a call tool request running on the server.\n\nThe server SHOULD allow multiple clients to join a previous call tool request.\n\nThe request SHOULD still be in-flight, but due to communication latency, it is always possible that this request MAY arrive after the request has already finished.\n\nThe client MAY request a new keepAlive time on the server to extend the amount of time the client has to retrieve the result.\n\nThe async token MUST not be relied on for authorisation and servers SHOULD validate clients have permission to join this tool call via other means.", + "properties": { + "method": { + "const": "tools/join/async", + "type": "string" + }, + "params": { + "properties": { + "keepAlive": { + "description": "The number of seconds to keep results and resources generated by this call tool available on the server", + "type": "integer" + }, + "token": { + "$ref": "#/definitions/AsyncToken" + } + }, + "required": [ + "token" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, "ListPromptsRequest": { "description": "Sent from the client to request a list of prompts and prompt templates the server has.", "properties": { @@ -1034,7 +1080,7 @@ "type": "object" }, "ListResourceTemplatesRequest": { - "description": "Sent from the client to request a list of resource templates the server has.", + "description": "Sent from the client to request a list of resource templates the server has.\n\nThis SHOULD NOT include any templates to return in progress resources sent as part of a ProgressNotification", "properties": { "method": { "const": "resources/templates/list", @@ -1102,7 +1148,7 @@ "type": "object" }, "ListResourcesResult": { - "description": "The server's response to a resources/list request from the client.", + "description": "The server's response to a resources/list request from the client.\n\nThis SHOULD NOT include any in progress resources sent as part of a ProgressNotification", "properties": { "_meta": { "additionalProperties": {}, @@ -1413,6 +1459,10 @@ "$ref": "#/definitions/ProgressToken", "description": "The progress token which was given in the initial request, used to associate this notification with the request that is proceeding." }, + "resourceId": { + "description": "An optional resource id associated with the current progress.\n\nServers SHOULD retain these resources for a reasonable period of time to enable the client\nto retrieve the resource.\n\nIn the case of CallToolAsyncRequest initiated requests this SHOULD be at least the keepAlive\ntime of the tool call.\n\nIn synchronous scenarios the server SHOULD retain these resources at least as long as the\nrequest is in progress and MAY be longer.\n\nIf a request is cancelled the server MAY discard these resources at any time after the cancellation.\n\nThe server SHOULD NOT list in-progress resources in the response to a ListResourcesRequest.", + "type": "string" + }, "total": { "description": "Total number of items to process (or total progress required), if known.", "type": "number" @@ -1885,6 +1935,16 @@ "ServerCapabilities": { "description": "Capabilities that a server may support. Known capabilities are defined here, in this schema, but this is not a closed set: any server can define its own, additional capabilities.", "properties": { + "async": { + "description": "Present if the server offers async tool calls.", + "properties": { + "maxKeepAliveTime": { + "description": "The maximum keep alive time in seconds that the server will support for async tool calls.", + "type": "integer" + } + }, + "type": "object" + }, "completions": { "additionalProperties": true, "description": "Present if the server supports argument autocompletion suggestions.", @@ -1954,6 +2014,9 @@ { "$ref": "#/definitions/ResourceListChangedNotification" }, + { + "$ref": "#/definitions/UnsupportedFeatureNotification" + }, { "$ref": "#/definitions/ResourceUpdatedNotification" }, @@ -1989,6 +2052,9 @@ { "$ref": "#/definitions/InitializeResult" }, + { + "$ref": "#/definitions/CallToolAsyncResult" + }, { "$ref": "#/definitions/ListResourcesResult" }, @@ -2203,6 +2269,10 @@ "description": "If true, this tool may interact with an \"open world\" of external\nentities. If false, the tool's domain of interaction is closed.\nFor example, the world of a web search tool is open, whereas that\nof a memory tool is not.\n\nDefault: true", "type": "boolean" }, + "preferAsync": { + "description": "If true, should ideally be called using the async protocol\nas requests are expected to be long running.\n\nDefault: false", + "type": "boolean" + }, "readOnlyHint": { "description": "If true, the tool does not modify its environment.\n\nDefault: false", "type": "boolean" @@ -2264,6 +2334,32 @@ "params" ], "type": "object" + }, + "UnsupportedFeatureNotification": { + "description": "A notification from the server to the client, informing it that a resource has changed and may need to be read again. This should only be sent if the client previously sent a resources/subscribe request.", + "properties": { + "method": { + "const": "notifications/feature/unsupported", + "type": "string" + }, + "params": { + "properties": { + "message": { + "description": "A message describing an optional feature that the client has called that is not supported by the server.", + "type": "string" + } + }, + "required": [ + "message" + ], + "type": "object" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" } } } diff --git a/schema/draft/schema.ts b/schema/draft/schema.ts index 50e5d66c7..07e3c9faa 100644 --- a/schema/draft/schema.ts +++ b/schema/draft/schema.ts @@ -223,20 +223,55 @@ export interface ClientCapabilities { } /** - * Used by the client to call a tool provided by the server and return a token to get the result or cancel the invocation at a later stage. + * Used by the client to call a tool provided by the server and return a token to get the result or cancel the call at a later stage. + * + * The server SHOULD retain the result of the tool call for the number of seconds specified by keep_alive to enable the client to retrieve the result. + * + * The server MUST respond with an CallToolAsyncResult. + * + * The server MAY reduce the number of seconds specified by keep_alive if so the server MUST inform the client of the new keep_alive time in the CallToolAsyncResult */ export interface CallToolAsyncRequest { method: "tools/call/async"; params: { name: string; arguments?: { [key: string]: unknown }; + keepAlive?: number }; } /** - * Used by the client to cancel a previous async tool call. + * Used by the client to reconnect to or extend the keep alive of a call tool request running on the server. + * + * The server SHOULD allow multiple clients to join a previous call tool request. + * + * The request SHOULD still be in-flight, but due to communication latency, it is always possible that this request MAY arrive after the request has already finished. + * + * The client MAY request a new keepAlive time on the server to extend the amount of time the client has to retrieve the result. + * + * The async token MUST not be relied on for authorisation and servers SHOULD validate clients have permission to join this tool call via other means. */ -export interface CancelToolAsyncRequest { +export interface JoinCallToolAsyncRequest { + method: "tools/join/async"; + params: { + token: AsyncToken + /** + * The number of seconds to keep results and resources generated by this call tool available on the server + */ + keepAlive?: number + }; +} + +/** + * Used by the client to cancel a previous async tool call request. + * + * The request SHOULD still be in-flight, but due to communication latency, it is always possible that this request MAY arrive after the request has already finished. + * + * This request indicates that the result will be unused, so any associated processing SHOULD cease. + * + * The server SHOULD send a CancelNotification to any clients that have. + */ +export interface CancelToolAsyncNotification { method: "tools/cancel/async"; params: { token: AsyncToken @@ -244,9 +279,21 @@ export interface CancelToolAsyncRequest { } /** - * Used by the client to retrieve the result of an async tool call running on a server. + * Used by the client to request the result of an async async tool call running on a server to be sent to this client. + * + * The request SHOULD still be in-flight, but due to communication latency, it is always possible that this request MAY arrive after the request has already finished. + * + * The server MUST return a CallToolResult either with the result an error status if the result cannot be retrieved or a pending status if the call tool is still in progress. + +* The client SHOULD use ProgressNotification results to determin when to call this method. + * + * The client MAY call this periodically if regular progress notifications are not recieved. + * + * The server MAY cancel any in progress tool calls if the client submits too many GetToolAsyncResultRequest. + * + * The server SHOULD send a CancelNotification to the client if the request has been cancelled with a reason. */ -export interface GetToolAsyncResult { +export interface GetToolAsyncResultRequest { method: "tools/get/async"; params: { token: AsyncToken @@ -259,10 +306,20 @@ export interface GetToolAsyncResult { export type AsyncToken = string | number; /** - * Used by the by the server to provide a reference to the client for a previously submitted async tool call. + * Used by the server to provide a reference to the client for a previously submitted async tool call. + * + * This MUST only be sent as the result of EITHER CallToolAsyncRequest or JoinToolAsyncResultRequest + * + * The token SHOULD be valid to get or cancel the result for keepAalive seconds from the received time on the server. + * + * The server SHOULD report the received time in unix time, e.g. seconds elapsed since 00:00:00 UTC on 1 January 1970 + * + * The is NOT */ -export interface AsyncToolResult { +export interface CallToolAsyncResult { token: AsyncToken, + received: number + keepAlive: number } /** @@ -312,6 +369,16 @@ export interface ServerCapabilities { */ listChanged?: boolean; }; + + /** + * Present if the server offers async tool calls. + */ + async?: { + /** + * The maximum keep alive time in seconds that the server will support for async tool calls. + */ + maxKeepAliveTime?: number + } } /** @@ -357,6 +424,24 @@ export interface ProgressNotification extends Notification { * An optional message describing the current progress. */ message?: string; + + /** + * An optional resource id associated with the current progress. + * + * Servers SHOULD retain these resources for a reasonable period of time to enable the client + * to retrieve the resource. + * + * In the case of CallToolAsyncRequest initiated requests this SHOULD be at least the keepAlive + * time of the tool call. + * + * In synchronous scenarios the server SHOULD retain these resources at least as long as the + * request is in progress and MAY be longer. + * + * If a request is cancelled the server MAY discard these resources at any time after the cancellation. + * + * The server SHOULD NOT list in-progress resources in the response to a ListResourcesRequest. + */ + resourceId?: string }; } @@ -389,6 +474,8 @@ export interface ListResourcesRequest extends PaginatedRequest { /** * The server's response to a resources/list request from the client. + * + * This SHOULD NOT include any in progress resources sent as part of a ProgressNotification */ export interface ListResourcesResult extends PaginatedResult { resources: Resource[]; @@ -396,6 +483,8 @@ export interface ListResourcesResult extends PaginatedResult { /** * Sent from the client to request a list of resource templates the server has. + * + * This SHOULD NOT include any templates to return in progress resources sent as part of a ProgressNotification */ export interface ListResourceTemplatesRequest extends PaginatedRequest { method: "resources/templates/list"; @@ -467,6 +556,19 @@ export interface UnsubscribeRequest extends Request { }; } +/** + * A notification from the server to the client, informing it that a resource has changed and may need to be read again. This should only be sent if the client previously sent a resources/subscribe request. + */ +export interface UnsupportedFeatureNotification extends Notification { + method: "notifications/feature/unsupported"; + params: { + /** + * A message describing an optional feature that the client has called that is not supported by the server. + */ + message: string; + }; +} + /** * A notification from the server to the client, informing it that a resource has changed and may need to be read again. This should only be sent if the client previously sent a resources/subscribe request. */ @@ -827,6 +929,14 @@ export interface ToolAnnotations { * Default: true */ openWorldHint?: boolean; + + /** + * If true, should ideally be called using the async protocol + * as requests are expected to be long running. + * + * Default: false + */ + preferAsync?: boolean } /** @@ -1283,6 +1393,8 @@ export type ClientRequest = | SubscribeRequest | UnsubscribeRequest | CallToolRequest + | CallToolAsyncRequest + | JoinCallToolAsyncRequest | ListToolsRequest; export type ClientNotification = @@ -1306,7 +1418,8 @@ export type ServerNotification = | ResourceUpdatedNotification | ResourceListChangedNotification | ToolListChangedNotification - | PromptListChangedNotification; + | PromptListChangedNotification + | UnsupportedFeatureNotification; export type ServerResult = | EmptyResult @@ -1318,4 +1431,5 @@ export type ServerResult = | ListResourcesResult | ReadResourceResult | CallToolResult + | CallToolAsyncResult | ListToolsResult; From 7da905cbc110d3be846e65ca0a30d67c61f44fa3 Mon Sep 17 00:00:00 2001 From: David Savage Date: Sat, 31 May 2025 13:18:02 +0000 Subject: [PATCH 04/17] generate:json --- schema/draft/schema.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/schema/draft/schema.json b/schema/draft/schema.json index 84aca433a..d73b5800d 100644 --- a/schema/draft/schema.json +++ b/schema/draft/schema.json @@ -96,7 +96,8 @@ }, "required": [ "type" - ] + ], + "type": "object" }, "CallToolAsyncRequest": { "description": "Used by the client to call a tool provided by the server and return a token to get the result or cancel the call at a later stage.\n\nThe server SHOULD retain the result of the tool call for the number of seconds specified by keep_alive to enable the client to retrieve the result.\n\nThe server MUST respond with an CallToolAsyncResult.\n\nThe server MAY reduce the number of seconds specified by keep_alive if so the server MUST inform the client of the new keep_alive time in the CallToolAsyncResult", @@ -222,7 +223,7 @@ ], "type": "object" }, - "CancelToolAsyncRequest": { + "CancelToolAsyncNotification": { "description": "Used by the client to cancel a previous async tool call request.\n\nThe request SHOULD still be in-flight, but due to communication latency, it is always possible that this request MAY arrive after the request has already finished.\n\nThis request indicates that the result will be unused, so any associated processing SHOULD cease.\n\nThe server SHOULD send a CancelNotification to any clients that have.", "properties": { "method": { From 1222b62f89404c7a88caec3b4df1598b5d6e97a6 Mon Sep 17 00:00:00 2001 From: David Savage Date: Sat, 31 May 2025 13:19:43 +0000 Subject: [PATCH 05/17] fixed typo --- docs/specification/draft/basic/async.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/specification/draft/basic/async.mdx b/docs/specification/draft/basic/async.mdx index 9931d1e6b..e3146cb03 100644 --- a/docs/specification/draft/basic/async.mdx +++ b/docs/specification/draft/basic/async.mdx @@ -11,7 +11,7 @@ async call tool requests. ### 1.1 Purpose and Scope -The Model Context Protocol provides asyncronouse capabilities at the transport level, +The Model Context Protocol provides asynchronous capabilities at the transport level, enabling MCP clients to make asyncronous requests to call tools. ### 1.2 Protocol Requirements From e1614f18c6a28c663007b364bc58e90431fd1fd3 Mon Sep 17 00:00:00 2001 From: David Savage Date: Sat, 31 May 2025 15:40:19 +0000 Subject: [PATCH 06/17] fixed some types and clarified position wrt to failed async call setup --- docs/specification/draft/basic/async.mdx | 2 +- schema/draft/schema.ts | 51 ++++++++++++++++++------ 2 files changed, 40 insertions(+), 13 deletions(-) diff --git a/docs/specification/draft/basic/async.mdx b/docs/specification/draft/basic/async.mdx index e3146cb03..e017b2c23 100644 --- a/docs/specification/draft/basic/async.mdx +++ b/docs/specification/draft/basic/async.mdx @@ -12,7 +12,7 @@ async call tool requests. ### 1.1 Purpose and Scope The Model Context Protocol provides asynchronous capabilities at the transport level, -enabling MCP clients to make asyncronous requests to call tools. +enabling MCP clients to make asynchronous requests to call tools. ### 1.2 Protocol Requirements diff --git a/schema/draft/schema.ts b/schema/draft/schema.ts index 3b7152eb5..d82e92da8 100644 --- a/schema/draft/schema.ts +++ b/schema/draft/schema.ts @@ -229,14 +229,14 @@ export interface ClientCapabilities { /** * Used by the client to call a tool provided by the server and return a token to get the result or cancel the call at a later stage. * - * The server SHOULD retain the result of the tool call for the number of seconds specified by keep_alive to enable the client to retrieve the result. + * The server SHOULD retain the result of the tool call for the number of seconds specified by keepAlive to enable the client to retrieve the result. * * The server MUST respond with an CallToolAsyncResult. * - * The server MAY reduce the number of seconds specified by keep_alive if so the server MUST inform the client of the new keep_alive time in the CallToolAsyncResult + * The server MAY reduce the number of seconds specified by keepAlive if so the server MUST inform the client of the new keepAlive time in the CallToolAsyncResult. */ export interface CallToolAsyncRequest { - method: "tools/call/async"; + method: "tools/async/call"; params: { name: string; arguments?: { [key: string]: unknown }; @@ -256,7 +256,7 @@ export interface CallToolAsyncRequest { * The async token MUST not be relied on for authorisation and servers SHOULD validate clients have permission to join this tool call via other means. */ export interface JoinCallToolAsyncRequest { - method: "tools/join/async"; + method: "tools/async/join"; params: { token: AsyncToken /** @@ -273,10 +273,10 @@ export interface JoinCallToolAsyncRequest { * * This request indicates that the result will be unused, so any associated processing SHOULD cease. * - * The server SHOULD send a CancelNotification to any clients that have. + * The server SHOULD send a CancelNotification to any clients that have called or joined the associated async tool call. */ export interface CancelToolAsyncNotification { - method: "tools/cancel/async"; + method: "tools/async/cancel"; params: { token: AsyncToken }; @@ -298,7 +298,7 @@ export interface CancelToolAsyncNotification { * The server SHOULD send a CancelNotification to the client if the request has been cancelled with a reason. */ export interface GetToolAsyncResultRequest { - method: "tools/get/async"; + method: "tools/async/get"; params: { token: AsyncToken }; @@ -306,6 +306,8 @@ export interface GetToolAsyncResultRequest { /** * Used by the client to reference a previous async tool call submitted to the server. + * + * The server SHOULD NOT rely on this for authentication or authorisation purposes */ export type AsyncToken = string | number; @@ -317,13 +319,38 @@ export type AsyncToken = string | number; * The token SHOULD be valid to get or cancel the result for keepAalive seconds from the received time on the server. * * The server SHOULD report the received time in unix time, e.g. seconds elapsed since 00:00:00 UTC on 1 January 1970 - * - * The is NOT */ export interface CallToolAsyncResult { - token: AsyncToken, - received: number - keepAlive: number + /** + * The token the client can use to retrieve results, cancel the invocation or rejoin the call in the case of disconnection + * + * MUST be set if accepted is true + * + * MAY be set if accepted is false in order for the client to retrieve the error result + */ + token?: AsyncToken, + /** + * The unix time on the server the async call was received + * + * MUST be set if accepted is true + * + * MAY be set if accepted is false in order for the client to retrieve the error result + */ + received?: number + /** + * The number of seconds from the received time that the client has to retrieve the result + * + * MUST be set if accepted is true + * + * MAY be set if accepted is false in order for the client to retrieve the error result + */ + keepAlive?: number + /** + * Whether the async tool call was accepted for execution on the server. + * + * If accepted is false the server MAY still respond to GetToolAsyncResultRequest with details of the reason for rejection if so the token MUST be set. + */ + accepted: boolean } /** From 0c82a6017abb05b3fa703a542b963e609fbcf234 Mon Sep 17 00:00:00 2001 From: David Savage Date: Sat, 31 May 2025 15:42:27 +0000 Subject: [PATCH 07/17] run generate:json --- schema/draft/schema.json | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/schema/draft/schema.json b/schema/draft/schema.json index d73b5800d..a2751f464 100644 --- a/schema/draft/schema.json +++ b/schema/draft/schema.json @@ -21,7 +21,7 @@ "type": "object" }, "AsyncToken": { - "description": "Used by the client to reference a previous async tool call submitted to the server.", + "description": "Used by the client to reference a previous async tool call submitted to the server.\n\nThe server SHOULD NOT rely on this for authentication or authorisation purposes", "type": [ "string", "integer" @@ -100,10 +100,10 @@ "type": "object" }, "CallToolAsyncRequest": { - "description": "Used by the client to call a tool provided by the server and return a token to get the result or cancel the call at a later stage.\n\nThe server SHOULD retain the result of the tool call for the number of seconds specified by keep_alive to enable the client to retrieve the result.\n\nThe server MUST respond with an CallToolAsyncResult.\n\nThe server MAY reduce the number of seconds specified by keep_alive if so the server MUST inform the client of the new keep_alive time in the CallToolAsyncResult", + "description": "Used by the client to call a tool provided by the server and return a token to get the result or cancel the call at a later stage.\n\nThe server SHOULD retain the result of the tool call for the number of seconds specified by keepAlive to enable the client to retrieve the result.\n\nThe server MUST respond with an CallToolAsyncResult.\n\nThe server MAY reduce the number of seconds specified by keepAlive if so the server MUST inform the client of the new keepAlive time in the CallToolAsyncResult.", "properties": { "method": { - "const": "tools/call/async", + "const": "tools/async/call", "type": "string" }, "params": { @@ -132,22 +132,27 @@ "type": "object" }, "CallToolAsyncResult": { - "description": "Used by the server to provide a reference to the client for a previously submitted async tool call.\n\nThis MUST only be sent as the result of EITHER CallToolAsyncRequest or JoinToolAsyncResultRequest \n\nThe token SHOULD be valid to get or cancel the result for keepAalive seconds from the received time on the server.\n\nThe server SHOULD report the received time in unix time, e.g. seconds elapsed since 00:00:00 UTC on 1 January 1970\n\nThe is NOT", + "description": "Used by the server to provide a reference to the client for a previously submitted async tool call.\n\nThis MUST only be sent as the result of EITHER CallToolAsyncRequest or JoinToolAsyncResultRequest \n\nThe token SHOULD be valid to get or cancel the result for keepAalive seconds from the received time on the server.\n\nThe server SHOULD report the received time in unix time, e.g. seconds elapsed since 00:00:00 UTC on 1 January 1970", "properties": { + "accepted": { + "description": "Whether the async tool call was accepted for execution on the server.\n\nIf accepted is false the server MAY still respond to GetToolAsyncResultRequest with details of the reason for rejection if so the token MUST be set.", + "type": "boolean" + }, "keepAlive": { + "description": "The number of seconds from the received time that the client has to retrieve the result\n\nMUST be set if accepted is true\n\nMAY be set if accepted is false in order for the client to retrieve the error result", "type": "integer" }, "received": { + "description": "The unix time on the server the async call was received\n\nMUST be set if accepted is true\n\nMAY be set if accepted is false in order for the client to retrieve the error result", "type": "integer" }, "token": { - "$ref": "#/definitions/AsyncToken" + "$ref": "#/definitions/AsyncToken", + "description": "The token the client can use to retrieve results, cancel the invocation or rejoin the call in the case of disconnection\n\nMUST be set if accepted is true\n\nMAY be set if accepted is false in order for the client to retrieve the error result" } }, "required": [ - "keepAlive", - "received", - "token" + "accepted" ], "type": "object" }, @@ -224,10 +229,10 @@ "type": "object" }, "CancelToolAsyncNotification": { - "description": "Used by the client to cancel a previous async tool call request.\n\nThe request SHOULD still be in-flight, but due to communication latency, it is always possible that this request MAY arrive after the request has already finished.\n\nThis request indicates that the result will be unused, so any associated processing SHOULD cease.\n\nThe server SHOULD send a CancelNotification to any clients that have.", + "description": "Used by the client to cancel a previous async tool call request.\n\nThe request SHOULD still be in-flight, but due to communication latency, it is always possible that this request MAY arrive after the request has already finished.\n\nThis request indicates that the result will be unused, so any associated processing SHOULD cease.\n\nThe server SHOULD send a CancelNotification to any clients that have called or joined the associated async tool call.", "properties": { "method": { - "const": "tools/cancel/async", + "const": "tools/async/cancel", "type": "string" }, "params": { @@ -797,7 +802,7 @@ "description": "Used by the client to request the result of an async async tool call running on a server to be sent to this client.\n\nThe request SHOULD still be in-flight, but due to communication latency, it is always possible that this request MAY arrive after the request has already finished.\n\nThe server MUST return a CallToolResult either with the result an error status if the result cannot be retrieved or a pending status if the call tool is still in progress.\n\nThe client SHOULD use ProgressNotification results to determin when to call this method.\n\nThe client MAY call this periodically if regular progress notifications are not recieved.\n\nThe server MAY cancel any in progress tool calls if the client submits too many GetToolAsyncResultRequest.\n\nThe server SHOULD send a CancelNotification to the client if the request has been cancelled with a reason.", "properties": { "method": { - "const": "tools/get/async", + "const": "tools/async/get", "type": "string" }, "params": { @@ -1150,7 +1155,7 @@ "description": "Used by the client to reconnect to or extend the keep alive of a call tool request running on the server.\n\nThe server SHOULD allow multiple clients to join a previous call tool request.\n\nThe request SHOULD still be in-flight, but due to communication latency, it is always possible that this request MAY arrive after the request has already finished.\n\nThe client MAY request a new keepAlive time on the server to extend the amount of time the client has to retrieve the result.\n\nThe async token MUST not be relied on for authorisation and servers SHOULD validate clients have permission to join this tool call via other means.", "properties": { "method": { - "const": "tools/join/async", + "const": "tools/async/join", "type": "string" }, "params": { From 058e33152f1350e51c4a85600a0b21e15a399af6 Mon Sep 17 00:00:00 2001 From: David Savage Date: Sat, 31 May 2025 15:47:21 +0000 Subject: [PATCH 08/17] add isPending status to CallToolResult --- schema/draft/schema.json | 4 ++++ schema/draft/schema.ts | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/schema/draft/schema.json b/schema/draft/schema.json index a2751f464..6cfbc812f 100644 --- a/schema/draft/schema.json +++ b/schema/draft/schema.json @@ -217,6 +217,10 @@ "description": "Whether the tool call ended in an error.\n\nIf not set, this is assumed to be false (the call was successful).\n\nAny errors that originate from the tool SHOULD be reported inside the result\nobject, with `isError` set to true, _not_ as an MCP protocol-level error\nresponse. Otherwise, the LLM would not be able to see that an error occurred\nand self-correct.\n\nHowever, any errors in _finding_ the tool, an error indicating that the\nserver does not support tool calls, or any other exceptional conditions,\nshould be reported as an MCP error response.", "type": "boolean" }, + "isPending": { + "description": "Whether the tool call is pending a result.\n\nIf not set, this is assumed to be false (the call was successful).", + "type": "boolean" + }, "structuredContent": { "additionalProperties": {}, "description": "An optional JSON object that represents the structured result of the tool call.", diff --git a/schema/draft/schema.ts b/schema/draft/schema.ts index d82e92da8..e257dd421 100644 --- a/schema/draft/schema.ts +++ b/schema/draft/schema.ts @@ -888,6 +888,13 @@ export interface CallToolResult extends Result { * should be reported as an MCP error response. */ isError?: boolean; + + /** + * Whether the tool call is pending a result. + * + * If not set, this is assumed to be false (the call was successful). + */ + isPending?: boolean; } /** From 71e46664e5b9d83a139b2cc01c45fc26e100262d Mon Sep 17 00:00:00 2001 From: David Savage Date: Sat, 31 May 2025 15:58:34 +0000 Subject: [PATCH 09/17] More clarifications on get result process --- schema/draft/schema.json | 2 +- schema/draft/schema.ts | 16 ++++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/schema/draft/schema.json b/schema/draft/schema.json index 6cfbc812f..4bdb2af00 100644 --- a/schema/draft/schema.json +++ b/schema/draft/schema.json @@ -803,7 +803,7 @@ "type": "object" }, "GetToolAsyncResultRequest": { - "description": "Used by the client to request the result of an async async tool call running on a server to be sent to this client.\n\nThe request SHOULD still be in-flight, but due to communication latency, it is always possible that this request MAY arrive after the request has already finished.\n\nThe server MUST return a CallToolResult either with the result an error status if the result cannot be retrieved or a pending status if the call tool is still in progress.\n\nThe client SHOULD use ProgressNotification results to determin when to call this method.\n\nThe client MAY call this periodically if regular progress notifications are not recieved.\n\nThe server MAY cancel any in progress tool calls if the client submits too many GetToolAsyncResultRequest.\n\nThe server SHOULD send a CancelNotification to the client if the request has been cancelled with a reason.", + "description": "Used by the client to request the result of an async tool call running on a server to be sent to this client.\n\nThe request SHOULD be complete and this request SHOULD be recieved within the keepAlive window, but due to communication latency, it is always possible that this request MAY arrive after the request has already been discarded.\n\nThe server MUST return a CallToolResult with either: the result if the call was successful; an error status if the result cannot be retrieved; or a pending status if the call tool is still in progress.\n\nThe client SHOULD use ProgressNotification progress/total values to determin when to call this method.\n\nThe client MAY call this periodically if regular progress notifications are not recieved.\n\nThe client SHOULD NOT rely solely on polling to retrieve the result.\n\nThe server MAY cancel any in progress tool calls if the client submits too many GetToolAsyncResultRequest to the server.\n\nThe determination of what constitutes too many requests MAY be defined by the server at runtime.\n\nThe server SHOULD send a CancelNotification to the client if the request has been cancelled with a reason.", "properties": { "method": { "const": "tools/async/get", diff --git a/schema/draft/schema.ts b/schema/draft/schema.ts index e257dd421..92f6fd06c 100644 --- a/schema/draft/schema.ts +++ b/schema/draft/schema.ts @@ -283,17 +283,21 @@ export interface CancelToolAsyncNotification { } /** - * Used by the client to request the result of an async async tool call running on a server to be sent to this client. + * Used by the client to request the result of an async tool call running on a server to be sent to this client. * - * The request SHOULD still be in-flight, but due to communication latency, it is always possible that this request MAY arrive after the request has already finished. + * The request SHOULD be complete and this request SHOULD be recieved within the keepAlive window, but due to communication latency, it is always possible that this request MAY arrive after the request has already been discarded. * - * The server MUST return a CallToolResult either with the result an error status if the result cannot be retrieved or a pending status if the call tool is still in progress. - -* The client SHOULD use ProgressNotification results to determin when to call this method. + * The server MUST return a CallToolResult with either: the result if the call was successful; an error status if the result cannot be retrieved; or a pending status if the call tool is still in progress. + * + * The client SHOULD use ProgressNotification progress/total values to determin when to call this method. * * The client MAY call this periodically if regular progress notifications are not recieved. * - * The server MAY cancel any in progress tool calls if the client submits too many GetToolAsyncResultRequest. + * The client SHOULD NOT rely solely on polling to retrieve the result. + * + * The server MAY cancel any in progress tool calls if the client submits too many GetToolAsyncResultRequest to the server. + * + * The determination of what constitutes too many requests MAY be defined by the server at runtime. * * The server SHOULD send a CancelNotification to the client if the request has been cancelled with a reason. */ From 779b77703b8a46ed63ad70862afb25b743e48f51 Mon Sep 17 00:00:00 2001 From: David Savage Date: Sat, 31 May 2025 16:03:57 +0000 Subject: [PATCH 10/17] Fixed doc after copy paste --- schema/draft/schema.json | 2 +- schema/draft/schema.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/schema/draft/schema.json b/schema/draft/schema.json index 4bdb2af00..5b1745770 100644 --- a/schema/draft/schema.json +++ b/schema/draft/schema.json @@ -2567,7 +2567,7 @@ "type": "object" }, "UnsupportedFeatureNotification": { - "description": "A notification from the server to the client, informing it that a resource has changed and may need to be read again. This should only be sent if the client previously sent a resources/subscribe request.", + "description": "A notification from the server to the client, informing it that a feature that the client has requested is not supported by this server.", "properties": { "method": { "const": "notifications/feature/unsupported", diff --git a/schema/draft/schema.ts b/schema/draft/schema.ts index 92f6fd06c..e3547604d 100644 --- a/schema/draft/schema.ts +++ b/schema/draft/schema.ts @@ -592,7 +592,7 @@ export interface UnsubscribeRequest extends Request { } /** - * A notification from the server to the client, informing it that a resource has changed and may need to be read again. This should only be sent if the client previously sent a resources/subscribe request. + * A notification from the server to the client, informing it that a feature that the client has requested is not supported by this server. */ export interface UnsupportedFeatureNotification extends Notification { method: "notifications/feature/unsupported"; From e6e18eef583a5ed274123b5e82f91376c87015e4 Mon Sep 17 00:00:00 2001 From: David Savage Date: Sat, 31 May 2025 16:42:36 +0000 Subject: [PATCH 11/17] remove unsupported feature, just use error instead --- schema/draft/schema.json | 29 ----------------------------- schema/draft/schema.ts | 14 -------------- 2 files changed, 43 deletions(-) diff --git a/schema/draft/schema.json b/schema/draft/schema.json index 5b1745770..81e1a98ae 100644 --- a/schema/draft/schema.json +++ b/schema/draft/schema.json @@ -2209,9 +2209,6 @@ { "$ref": "#/definitions/ResourceListChangedNotification" }, - { - "$ref": "#/definitions/UnsupportedFeatureNotification" - }, { "$ref": "#/definitions/ResourceUpdatedNotification" }, @@ -2565,32 +2562,6 @@ "params" ], "type": "object" - }, - "UnsupportedFeatureNotification": { - "description": "A notification from the server to the client, informing it that a feature that the client has requested is not supported by this server.", - "properties": { - "method": { - "const": "notifications/feature/unsupported", - "type": "string" - }, - "params": { - "properties": { - "message": { - "description": "A message describing an optional feature that the client has called that is not supported by the server.", - "type": "string" - } - }, - "required": [ - "message" - ], - "type": "object" - } - }, - "required": [ - "method", - "params" - ], - "type": "object" } } } diff --git a/schema/draft/schema.ts b/schema/draft/schema.ts index e3547604d..60728a1a6 100644 --- a/schema/draft/schema.ts +++ b/schema/draft/schema.ts @@ -591,19 +591,6 @@ export interface UnsubscribeRequest extends Request { }; } -/** - * A notification from the server to the client, informing it that a feature that the client has requested is not supported by this server. - */ -export interface UnsupportedFeatureNotification extends Notification { - method: "notifications/feature/unsupported"; - params: { - /** - * A message describing an optional feature that the client has called that is not supported by the server. - */ - message: string; - }; -} - /** * A notification from the server to the client, informing it that a resource has changed and may need to be read again. This should only be sent if the client previously sent a resources/subscribe request. */ @@ -1547,7 +1534,6 @@ export type ServerNotification = | ResourceListChangedNotification | ToolListChangedNotification | PromptListChangedNotification - | UnsupportedFeatureNotification; export type ServerResult = | EmptyResult From cc1b370de9db8afae936beb38ea057d9cb43f019 Mon Sep 17 00:00:00 2001 From: David Savage Date: Sat, 31 May 2025 17:06:34 +0000 Subject: [PATCH 12/17] add clarifying point about security to protocol requirements, also tweeked line style for mermaid diagrams for legibility --- docs/specification/draft/basic/async.mdx | 35 ++++++++++++------------ 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/docs/specification/draft/basic/async.mdx b/docs/specification/draft/basic/async.mdx index e017b2c23..20f83c101 100644 --- a/docs/specification/draft/basic/async.mdx +++ b/docs/specification/draft/basic/async.mdx @@ -19,6 +19,7 @@ enabling MCP clients to make asynchronous requests to call tools. Asynchronous tool calls is **OPTIONAL** for MCP implementations. When supported: - Implementations **MUST** follow established security best practices for their protocol. +- In particular implementations **MUST NOT** use the AsyncToken as a substitute for authorisation or authentication checks. ### 1.3 Standards Compliance @@ -35,11 +36,11 @@ sequenceDiagram actor Client actor Server Client->>Server: CallToolAsyncRequest - Server->>Client: CallToolAsyncResult - Server--)Client: ProgressNotification (1/2) - Server--)Client: ProgressNotification (2/2) + Server-->>Client: CallToolAsyncResult + Server-)Client: ProgressNotification (1/2) + Server-)Client: ProgressNotification (2/2) Client->>Server: GetAsyncResultRequest - Server->>Client: CallToolResult + Server-->>Client: CallToolResult ``` #### 2.1.1 Join usage @@ -50,11 +51,11 @@ sequenceDiagram actor Client actor Server Client->>Server: JoinCallToolAsyncRequest - Server->>Client: CallToolAsyncResult - Server--)Client: ProgressNotification (3/4) - Server--)Client: ProgressNotification (4/4) + Server-->>Client: CallToolAsyncResult + Server-)Client: ProgressNotification (3/4) + Server-)Client: ProgressNotification (4/4) Client->>Server: GetAsyncResultRequest - Server->>Client: CallToolResult + Server-->>Client: CallToolResult ``` Multiple clients MAY join an existing tool call request. @@ -65,13 +66,13 @@ sequenceDiagram actor Server Client1->>Server: CallToolAsyncRequest Server-->>Client1: CallToolAsyncResult - Server--)Client1: ProgressNotification (1/2) + Server-)Client1: ProgressNotification (1/2) Client2->>Server: JoinCallToolAsyncRequest Server-->>Client2: CallToolAsyncResult - Server--)Client1: ProgressNotification (2/2) + Server-)Client1: ProgressNotification (2/2) Client1->>Server: GetAsyncResultRequest Server-->>Client1: CallToolResult - Server--)Client2: ProgressNotification (2/2) + Server-)Client2: ProgressNotification (2/2) Client2->>Server: GetAsyncResultRequest Server-->>Client2: CallToolResult ``` @@ -86,13 +87,13 @@ sequenceDiagram actor Client2 actor Server Client1->>Server: CallToolAsyncRequest - Server->>Client1: CallToolAsyncResult - Server--)Client1: ProgressNotification (1/3) + Server-->>Client1: CallToolAsyncResult + Server-)Client1: ProgressNotification (1/3) Client2->>Server: JoinCallToolAsyncRequest - Server->>Client2: CallToolAsyncResult - Server--)Client1: ProgressNotification (2/3) - Server--)Client2: ProgressNotification (2/3) - Client2--)Server: CancelToolAsyncNotification + Server-->>Client2: CallToolAsyncResult + Server-)Client1: ProgressNotification (2/3) + Server-)Client2: ProgressNotification (2/3) + Client2-)Server: CancelToolAsyncNotification Server--)Client1: CancelNotification Server--)Client2: CancelNotification ``` \ No newline at end of file From 20dccf6ef186867ba4533c59dd33a0975b520c28 Mon Sep 17 00:00:00 2001 From: David Savage Date: Sat, 31 May 2025 18:34:39 +0000 Subject: [PATCH 13/17] change resourceId to resource uri --- schema/draft/schema.json | 5 +++-- schema/draft/schema.ts | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/schema/draft/schema.json b/schema/draft/schema.json index 81e1a98ae..e549a7dff 100644 --- a/schema/draft/schema.json +++ b/schema/draft/schema.json @@ -1654,8 +1654,9 @@ "$ref": "#/definitions/ProgressToken", "description": "The progress token which was given in the initial request, used to associate this notification with the request that is proceeding." }, - "resourceId": { - "description": "An optional resource id associated with the current progress.\n\nServers SHOULD retain these resources for a reasonable period of time to enable the client\nto retrieve the resource.\n\nIn the case of CallToolAsyncRequest initiated requests this SHOULD be at least the keepAlive\ntime of the tool call.\n\nIn synchronous scenarios the server SHOULD retain these resources at least as long as the\nrequest is in progress and MAY be longer.\n\nIf a request is cancelled the server MAY discard these resources at any time after the cancellation.\n\nThe server SHOULD NOT list in-progress resources in the response to a ListResourcesRequest.", + "resourceUri": { + "description": "An optional resource uri associated with the current progress.\n\nServers SHOULD retain these resources for a reasonable period of time to enable the client\nto retrieve the resource.\n\nIn the case of CallToolAsyncRequest initiated requests this SHOULD be at least the keepAlive\ntime of the tool call.\n\nIn synchronous scenarios the server SHOULD retain these resources at least as long as the\nrequest is in progress and MAY be longer.\n\nIf a request is cancelled the server MAY discard these resources at any time after the cancellation.\n\nThe server SHOULD NOT list in-progress resources in the response to a ListResourcesRequest.", + "format": "uri", "type": "string" }, "total": { diff --git a/schema/draft/schema.ts b/schema/draft/schema.ts index 60728a1a6..f96971a30 100644 --- a/schema/draft/schema.ts +++ b/schema/draft/schema.ts @@ -461,7 +461,7 @@ export interface ProgressNotification extends Notification { message?: string; /** - * An optional resource id associated with the current progress. + * An optional resource uri associated with the current progress. * * Servers SHOULD retain these resources for a reasonable period of time to enable the client * to retrieve the resource. @@ -475,8 +475,10 @@ export interface ProgressNotification extends Notification { * If a request is cancelled the server MAY discard these resources at any time after the cancellation. * * The server SHOULD NOT list in-progress resources in the response to a ListResourcesRequest. + * + * @format uri */ - resourceId?: string + resourceUri?: string }; } From 20bc045a478101ab3676ba08c71629ca2ad5f03d Mon Sep 17 00:00:00 2001 From: David Savage Date: Sat, 31 May 2025 18:41:44 +0000 Subject: [PATCH 14/17] Add CancelToolAsyncNotification to client notifications fix trailing ; --- schema/draft/schema.json | 3 +++ schema/draft/schema.ts | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/schema/draft/schema.json b/schema/draft/schema.json index e549a7dff..5bdaee267 100644 --- a/schema/draft/schema.json +++ b/schema/draft/schema.json @@ -332,6 +332,9 @@ { "$ref": "#/definitions/InitializedNotification" }, + { + "$ref": "#/definitions/CancelToolAsyncNotification" + }, { "$ref": "#/definitions/ProgressNotification" }, diff --git a/schema/draft/schema.ts b/schema/draft/schema.ts index f96971a30..0a25ac2cb 100644 --- a/schema/draft/schema.ts +++ b/schema/draft/schema.ts @@ -1517,6 +1517,7 @@ export type ClientNotification = | CancelledNotification | ProgressNotification | InitializedNotification + | CancelToolAsyncNotification | RootsListChangedNotification; export type ClientResult = EmptyResult | CreateMessageResult | ListRootsResult | ElicitResult; @@ -1535,7 +1536,7 @@ export type ServerNotification = | ResourceUpdatedNotification | ResourceListChangedNotification | ToolListChangedNotification - | PromptListChangedNotification + | PromptListChangedNotification; export type ServerResult = | EmptyResult From de4ed52c51860cb85f69778327b96a961344611d Mon Sep 17 00:00:00 2001 From: David Savage Date: Sun, 1 Jun 2025 05:57:02 +0000 Subject: [PATCH 15/17] Add missing Get Async Result --- schema/draft/schema.json | 3 +++ schema/draft/schema.ts | 1 + 2 files changed, 4 insertions(+) diff --git a/schema/draft/schema.json b/schema/draft/schema.json index 5bdaee267..50292a514 100644 --- a/schema/draft/schema.json +++ b/schema/draft/schema.json @@ -354,6 +354,9 @@ { "$ref": "#/definitions/JoinCallToolAsyncRequest" }, + { + "$ref": "#/definitions/GetToolAsyncResultRequest" + }, { "$ref": "#/definitions/PingRequest" }, diff --git a/schema/draft/schema.ts b/schema/draft/schema.ts index 0a25ac2cb..c40d7b512 100644 --- a/schema/draft/schema.ts +++ b/schema/draft/schema.ts @@ -1511,6 +1511,7 @@ export type ClientRequest = | CallToolRequest | CallToolAsyncRequest | JoinCallToolAsyncRequest + | GetToolAsyncResultRequest | ListToolsRequest; export type ClientNotification = From c147adc177abcfb287e630014034f694578c490b Mon Sep 17 00:00:00 2001 From: David Savage Date: Wed, 4 Jun 2025 07:30:57 +0000 Subject: [PATCH 16/17] remove received value and clarified meaning of keepAlive now this is no longer provided --- schema/draft/schema.json | 6 +----- schema/draft/schema.ts | 13 +++---------- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/schema/draft/schema.json b/schema/draft/schema.json index 50292a514..1b9df05b9 100644 --- a/schema/draft/schema.json +++ b/schema/draft/schema.json @@ -139,11 +139,7 @@ "type": "boolean" }, "keepAlive": { - "description": "The number of seconds from the received time that the client has to retrieve the result\n\nMUST be set if accepted is true\n\nMAY be set if accepted is false in order for the client to retrieve the error result", - "type": "integer" - }, - "received": { - "description": "The unix time on the server the async call was received\n\nMUST be set if accepted is true\n\nMAY be set if accepted is false in order for the client to retrieve the error result", + "description": "The number of seconds that the server should retain results after all sessions \nhave disconnected\n\nMUST be set if accepted is true\n\nMAY be set if accepted is false in order for the client to retrieve an error result", "type": "integer" }, "token": { diff --git a/schema/draft/schema.ts b/schema/draft/schema.ts index c40d7b512..9ea5047f8 100644 --- a/schema/draft/schema.ts +++ b/schema/draft/schema.ts @@ -334,19 +334,12 @@ export interface CallToolAsyncResult { */ token?: AsyncToken, /** - * The unix time on the server the async call was received + * The number of seconds that the server should retain results after all sessions + * have disconnected * * MUST be set if accepted is true * - * MAY be set if accepted is false in order for the client to retrieve the error result - */ - received?: number - /** - * The number of seconds from the received time that the client has to retrieve the result - * - * MUST be set if accepted is true - * - * MAY be set if accepted is false in order for the client to retrieve the error result + * MAY be set if accepted is false in order for the client to retrieve an error result */ keepAlive?: number /** From 45301ddccea3b99c387a97a1642847e1ba0e794c Mon Sep 17 00:00:00 2001 From: David Savage Date: Thu, 5 Jun 2025 05:39:09 +0000 Subject: [PATCH 17/17] Add timeout to GetToolAsyncResultRequest to enable polling as a backup to progressNotifications --- schema/draft/schema.json | 6 +++++- schema/draft/schema.ts | 12 +++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/schema/draft/schema.json b/schema/draft/schema.json index 1b9df05b9..82d5a73b9 100644 --- a/schema/draft/schema.json +++ b/schema/draft/schema.json @@ -214,7 +214,7 @@ "type": "boolean" }, "isPending": { - "description": "Whether the tool call is pending a result.\n\nIf not set, this is assumed to be false (the call was successful).", + "description": "Whether the tool call is pending a result.\n\nThis MUST only be set in response to a GetToolAsyncResultRequest where timeout is set\n\nIf not set, this is assumed to be false (the call was successful).", "type": "boolean" }, "structuredContent": { @@ -813,6 +813,10 @@ }, "params": { "properties": { + "timeout": { + "description": "The timeout in seconds for the call tool request timeout.\n\nIf not set the server SHOULD wait indefinitely for a result or until the client session disconnects.\n\nThe server MAY return with an error if the client submits too many requests.", + "type": "integer" + }, "token": { "$ref": "#/definitions/AsyncToken" } diff --git a/schema/draft/schema.ts b/schema/draft/schema.ts index 9ea5047f8..966368608 100644 --- a/schema/draft/schema.ts +++ b/schema/draft/schema.ts @@ -304,7 +304,15 @@ export interface CancelToolAsyncNotification { export interface GetToolAsyncResultRequest { method: "tools/async/get"; params: { - token: AsyncToken + token: AsyncToken, + /** + * The timeout in seconds for the call tool request timeout. + * + * If not set the server SHOULD wait indefinitely for a result or until the client session disconnects. + * + * The server MAY return with an error if the client submits too many requests. + */ + timeout?: number }; } @@ -877,6 +885,8 @@ export interface CallToolResult extends Result { /** * Whether the tool call is pending a result. + * + * This MUST only be set in response to a GetToolAsyncResultRequest where timeout is set * * If not set, this is assumed to be false (the call was successful). */