From 386692dff839d06a3a8344cc6ee9dd7d91b45311 Mon Sep 17 00:00:00 2001
From: Tomas Pilar <tomas.pilar@apoco.com>
Date: Mon, 16 Jun 2025 08:38:35 +0200
Subject: [PATCH] feat: partial results

Signed-off-by: Tomas Pilar <thomas7pilar@gmail.com>
Co-authored-by: Matous Havlena <havlenma@gmail.com>
---
 docs/specification/draft/basic/index.mdx      |  11 ++
 docs/specification/draft/basic/transports.mdx |   8 +-
 docs/specification/draft/server/tools.mdx     |  23 ++++
 schema/draft/schema.json                      | 100 ++++++++++++++++++
 schema/draft/schema.ts                        |  12 ++-
 5 files changed, 149 insertions(+), 5 deletions(-)

diff --git a/docs/specification/draft/basic/index.mdx b/docs/specification/draft/basic/index.mdx
index fc3cf00aa..3f8b3146c 100644
--- a/docs/specification/draft/basic/index.mdx
+++ b/docs/specification/draft/basic/index.mdx
@@ -38,6 +38,10 @@ Requests are sent from the client to the server or vice versa, to initiate an op
   method: string;
   params?: {
     [key: string]: unknown;
+    _meta?: {
+      [key: string]: unknown;
+      allowPartial?: boolean;
+    }
   };
 }
 ```
@@ -46,6 +50,7 @@ Requests are sent from the client to the server or vice versa, to initiate an op
 - Unlike base JSON-RPC, the ID **MUST NOT** be `null`.
 - The request ID **MUST NOT** have been previously used by the requestor within the same
   session.
+- Requests **MAY** allow partial results by setting the `allowPartial` flag.
 
 ### Responses
 
@@ -57,6 +62,10 @@ Responses are sent in reply to requests, containing the result or error of the o
   id: string | number;
   result?: {
     [key: string]: unknown;
+    _meta?: {
+      [key: string]: unknown;
+      hasMore?: boolean;
+    }
   }
   error?: {
     code: number;
@@ -70,6 +79,8 @@ Responses are sent in reply to requests, containing the result or error of the o
 - **Responses** are further sub-categorized as either **successful results** or
   **errors**. Either a `result` or an `error` **MUST** be set. A response **MUST NOT**
   set both.
+- Results **MAY** set `hasMore` flag only if the corresponding request set the `allowPartial` flag.
+  The flag indicates that more responses with the same ID will follow.
 - Results **MAY** follow any JSON object structure, while errors **MUST** include an
   error code and message at minimum.
 - Error codes **MUST** be integers.
diff --git a/docs/specification/draft/basic/transports.mdx b/docs/specification/draft/basic/transports.mdx
index 3a79a9ec8..e7528753f 100644
--- a/docs/specification/draft/basic/transports.mdx
+++ b/docs/specification/draft/basic/transports.mdx
@@ -101,15 +101,15 @@ MCP endpoint.
    `Content-Type: application/json`, to return one JSON object. The client **MUST**
    support both these cases.
 6. If the server initiates an SSE stream:
-   - The SSE stream **SHOULD** eventually include JSON-RPC _response_ for the
+   - The SSE stream **SHOULD** eventually include JSON-RPC _response(s)_ for the
      JSON-RPC _request_ sent in the POST body.
    - The server **MAY** send JSON-RPC _requests_ and _notifications_ before sending the
-     JSON-RPC _response_. These messages **SHOULD** relate to the originating client
+     final JSON-RPC _response_. These messages **SHOULD** relate to the originating client
      _request_.
-   - The server **SHOULD NOT** close the SSE stream before sending the JSON-RPC _response_
+   - The server **SHOULD NOT** close the SSE stream before sending the JSON-RPC _response(s)_
      for the received JSON-RPC _request_, unless the [session](#session-management)
      expires.
-   - After the JSON-RPC _response_ has been sent, the server **SHOULD** close the SSE
+   - After the final JSON-RPC _response_ has been sent, the server **SHOULD** close the SSE
      stream.
    - Disconnection **MAY** occur at any time (e.g., due to network conditions).
      Therefore:
diff --git a/docs/specification/draft/server/tools.mdx b/docs/specification/draft/server/tools.mdx
index adf1760a5..780041d19 100644
--- a/docs/specification/draft/server/tools.mdx
+++ b/docs/specification/draft/server/tools.mdx
@@ -347,6 +347,29 @@ Providing an output schema helps clients and LLMs understand and properly handle
 - Guiding clients and LLMs to properly parse and utilize the returned data
 - Supporting better documentation and developer experience
 
+## Content streaming
+
+Tools might provide result as a stream of partial results:
+
+```mermaid
+sequenceDiagram
+    participant User
+    participant Client
+    participant Server
+
+    Note over Client,Server: Streaming
+    Client->>Server: tools/call
+    Server-->>Client: Tool partial result
+    Client-->>User: Content
+    Server-->>Client: Tool partial result
+    Client-->>User: Content
+    Server-->>Client: Tool partial result
+    Client-->>User: Content
+```
+
+_Unstructured_ content is incremental and the complete result can be acquired by concatenation.
+_Structured_ content is not incremental unless the tool's output schema states otherwise.
+
 ## Error Handling
 
 Tools use two error reporting mechanisms:
diff --git a/schema/draft/schema.json b/schema/draft/schema.json
index f1139948d..24dc7194f 100644
--- a/schema/draft/schema.json
+++ b/schema/draft/schema.json
@@ -131,6 +131,12 @@
                 "_meta": {
                     "additionalProperties": {},
                     "description": "This result property is reserved by the protocol to allow clients and servers to attach additional metadata to their responses.",
+                    "properties": {
+                        "hasMore": {
+                            "description": "If true, more results will follow.",
+                            "type": "boolean"
+                        }
+                    },
                     "type": "object"
                 },
                 "content": {
@@ -367,6 +373,12 @@
                 "_meta": {
                     "additionalProperties": {},
                     "description": "This result property is reserved by the protocol to allow clients and servers to attach additional metadata to their responses.",
+                    "properties": {
+                        "hasMore": {
+                            "description": "If true, more results will follow.",
+                            "type": "boolean"
+                        }
+                    },
                     "type": "object"
                 },
                 "completion": {
@@ -488,6 +500,12 @@
                 "_meta": {
                     "additionalProperties": {},
                     "description": "This result property is reserved by the protocol to allow clients and servers to attach additional metadata to their responses.",
+                    "properties": {
+                        "hasMore": {
+                            "description": "If true, more results will follow.",
+                            "type": "boolean"
+                        }
+                    },
                     "type": "object"
                 },
                 "content": {
@@ -585,6 +603,12 @@
                 "_meta": {
                     "additionalProperties": {},
                     "description": "This result property is reserved by the protocol to allow clients and servers to attach additional metadata to their responses.",
+                    "properties": {
+                        "hasMore": {
+                            "description": "If true, more results will follow.",
+                            "type": "boolean"
+                        }
+                    },
                     "type": "object"
                 },
                 "action": {
@@ -714,6 +738,12 @@
                 "_meta": {
                     "additionalProperties": {},
                     "description": "This result property is reserved by the protocol to allow clients and servers to attach additional metadata to their responses.",
+                    "properties": {
+                        "hasMore": {
+                            "description": "If true, more results will follow.",
+                            "type": "boolean"
+                        }
+                    },
                     "type": "object"
                 },
                 "description": {
@@ -816,6 +846,12 @@
                 "_meta": {
                     "additionalProperties": {},
                     "description": "This result property is reserved by the protocol to allow clients and servers to attach additional metadata to their responses.",
+                    "properties": {
+                        "hasMore": {
+                            "description": "If true, more results will follow.",
+                            "type": "boolean"
+                        }
+                    },
                     "type": "object"
                 },
                 "capabilities": {
@@ -965,6 +1001,10 @@
                     "properties": {
                         "_meta": {
                             "properties": {
+                                "allowPartial": {
+                                    "description": "If specified, the caller is requesting partial results for this request. The receiver is not obligated to return partial results.",
+                                    "type": "boolean"
+                                },
                                 "progressToken": {
                                     "$ref": "#/definitions/ProgressToken",
                                     "description": "If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications."
@@ -1032,6 +1072,12 @@
                 "_meta": {
                     "additionalProperties": {},
                     "description": "This result property is reserved by the protocol to allow clients and servers to attach additional metadata to their responses.",
+                    "properties": {
+                        "hasMore": {
+                            "description": "If true, more results will follow.",
+                            "type": "boolean"
+                        }
+                    },
                     "type": "object"
                 },
                 "nextCursor": {
@@ -1078,6 +1124,12 @@
                 "_meta": {
                     "additionalProperties": {},
                     "description": "This result property is reserved by the protocol to allow clients and servers to attach additional metadata to their responses.",
+                    "properties": {
+                        "hasMore": {
+                            "description": "If true, more results will follow.",
+                            "type": "boolean"
+                        }
+                    },
                     "type": "object"
                 },
                 "nextCursor": {
@@ -1124,6 +1176,12 @@
                 "_meta": {
                     "additionalProperties": {},
                     "description": "This result property is reserved by the protocol to allow clients and servers to attach additional metadata to their responses.",
+                    "properties": {
+                        "hasMore": {
+                            "description": "If true, more results will follow.",
+                            "type": "boolean"
+                        }
+                    },
                     "type": "object"
                 },
                 "nextCursor": {
@@ -1154,6 +1212,10 @@
                     "properties": {
                         "_meta": {
                             "properties": {
+                                "allowPartial": {
+                                    "description": "If specified, the caller is requesting partial results for this request. The receiver is not obligated to return partial results.",
+                                    "type": "boolean"
+                                },
                                 "progressToken": {
                                     "$ref": "#/definitions/ProgressToken",
                                     "description": "If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications."
@@ -1176,6 +1238,12 @@
                 "_meta": {
                     "additionalProperties": {},
                     "description": "This result property is reserved by the protocol to allow clients and servers to attach additional metadata to their responses.",
+                    "properties": {
+                        "hasMore": {
+                            "description": "If true, more results will follow.",
+                            "type": "boolean"
+                        }
+                    },
                     "type": "object"
                 },
                 "roots": {
@@ -1218,6 +1286,12 @@
                 "_meta": {
                     "additionalProperties": {},
                     "description": "This result property is reserved by the protocol to allow clients and servers to attach additional metadata to their responses.",
+                    "properties": {
+                        "hasMore": {
+                            "description": "If true, more results will follow.",
+                            "type": "boolean"
+                        }
+                    },
                     "type": "object"
                 },
                 "nextCursor": {
@@ -1399,6 +1473,12 @@
                 "_meta": {
                     "additionalProperties": {},
                     "description": "This result property is reserved by the protocol to allow clients and servers to attach additional metadata to their responses.",
+                    "properties": {
+                        "hasMore": {
+                            "description": "If true, more results will follow.",
+                            "type": "boolean"
+                        }
+                    },
                     "type": "object"
                 },
                 "nextCursor": {
@@ -1420,6 +1500,10 @@
                     "properties": {
                         "_meta": {
                             "properties": {
+                                "allowPartial": {
+                                    "description": "If specified, the caller is requesting partial results for this request. The receiver is not obligated to return partial results.",
+                                    "type": "boolean"
+                                },
                                 "progressToken": {
                                     "$ref": "#/definitions/ProgressToken",
                                     "description": "If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications."
@@ -1635,6 +1719,12 @@
                 "_meta": {
                     "additionalProperties": {},
                     "description": "This result property is reserved by the protocol to allow clients and servers to attach additional metadata to their responses.",
+                    "properties": {
+                        "hasMore": {
+                            "description": "If true, more results will follow.",
+                            "type": "boolean"
+                        }
+                    },
                     "type": "object"
                 },
                 "contents": {
@@ -1666,6 +1756,10 @@
                     "properties": {
                         "_meta": {
                             "properties": {
+                                "allowPartial": {
+                                    "description": "If specified, the caller is requesting partial results for this request. The receiver is not obligated to return partial results.",
+                                    "type": "boolean"
+                                },
                                 "progressToken": {
                                     "$ref": "#/definitions/ProgressToken",
                                     "description": "If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications."
@@ -1889,6 +1983,12 @@
                 "_meta": {
                     "additionalProperties": {},
                     "description": "This result property is reserved by the protocol to allow clients and servers to attach additional metadata to their responses.",
+                    "properties": {
+                        "hasMore": {
+                            "description": "If true, more results will follow.",
+                            "type": "boolean"
+                        }
+                    },
                     "type": "object"
                 }
             },
diff --git a/schema/draft/schema.ts b/schema/draft/schema.ts
index fdbb02894..58b0cdbc5 100644
--- a/schema/draft/schema.ts
+++ b/schema/draft/schema.ts
@@ -30,6 +30,10 @@ export interface Request {
        * If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications.
        */
       progressToken?: ProgressToken;
+      /**
+       * If specified, the caller is requesting partial results for this request. The receiver is not obligated to return partial results.
+       */
+      allowPartial?: boolean;
     };
     [key: string]: unknown;
   };
@@ -50,7 +54,13 @@ export interface Result {
   /**
    * This result property is reserved by the protocol to allow clients and servers to attach additional metadata to their responses.
    */
-  _meta?: { [key: string]: unknown };
+  _meta?: {
+    /**
+     * If true, more results will follow.
+     */
+    hasMore?: boolean;
+    [key: string]: unknown;
+  };
   [key: string]: unknown;
 }
 
