From f75722a2527c4533cf26a90ae5c32df895a14f06 Mon Sep 17 00:00:00 2001 From: Caitie McCaffrey Date: Mon, 26 Jan 2026 20:52:05 -0800 Subject: [PATCH 1/6] Message Structure Proposal to align MRTR & Tasks Updated pros and cons for MRTR & Tasks message structure options. --- ...TR-And-Tasks-Message-Structure-Proposal.md | 259 ++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100644 proposals/MRTR-And-Tasks-Message-Structure-Proposal.md diff --git a/proposals/MRTR-And-Tasks-Message-Structure-Proposal.md b/proposals/MRTR-And-Tasks-Message-Structure-Proposal.md new file mode 100644 index 0000000..91b02e2 --- /dev/null +++ b/proposals/MRTR-And-Tasks-Message-Structure-Proposal.md @@ -0,0 +1,259 @@ +# MRTR And Tasks Message Structure Proposal + +## Overview +This document provides an overview of how the [Multi-Round-Trip Request Proposal](https://github.com/modelcontextprotocol/transports-wg/pull/7/changes#diff-c42674696a4c91ccc0d2daf8425dbcb52201ec1ef75921ae1e4865b5b911018d) (MRTR) fits with the Tasks by walking thorugh an example with Elicitations. + +It also raises open questions and proposed solutions for discussion on what the return type should be for Tool Requests that require Elicitation or Sampling to complete. + +## Tasks Background +The [Tasks](https://modelcontextprotocol.io/specification/draft/basic/utilities/tasks) Utilities allows requests to be augmented with a Promise like mechanism. `Tasks` have a Status Lifecycle including `Working`, `Input Requried`, `Completed`, `Failed`, and `Cancelled`. + +The `Input Required` status allows a Tool or Capability to indicate that additional input is required from the user to complete the task. This is where an Elicitation or Sampling request can be made by the server. + +When the client encounters an `Input Required` status it SHOULD call `tasks/result`. This allows the server to then return an `Elicitation` or `Sampling` request to the client. This fits well with the proposal to eliminate unsolicited `Elicitation` and `Sampling` requests from the server to the client. + +### Example Flow with Elicitations +The below example uses an Echo Tool with an optional input parameter, when missing Elicitation is used to request the input from the user before completing the request. + +1. Client Request to invoke EchoTool. +```json +{ + "jsonrpc": "2.0", + "id": 1, + "method": "tools/call", + "params": { + "name": "echo", + "task": { + "ttl": 60000 + } + } +} +``` + +2. Server Response with a `Task` +```json +{ + "id": 1, + "jsonrpc": "2.0", + "result": { + "task": { + "taskId":"echo_dc792e24-01b5-4c0a-abcb-0559848ca3c5", + "status": "Working", + "statusMessage": "Task has been created for echo tool invocation.", + "createdAt": "2026-01-27T03:32:48.3148180Z", + "ttl": 60000, + "pollInterval": 100 + } + } +} +``` + +3. Client Request periodically checks the status of the `Task` using `tasks/get`. +```json +{ + "jsonrpc": "2.0", + "id": 2, + "method": "tasks/get", + "params": { + "taskId": "echo_dc792e24-01b5-4c0a-abcb-0559848ca3c5" + } +} +``` + +4. Server Response with Task status `InputRequired` +```json +{ + "id": 2, + "jsonrpc": "2.0", + "result": + { + "taskId": "echo_dc792e24-01b5-4c0a-abcb-0559848ca3c5", + "status": "InputRequired", + "statusMessage": "Input Required to Proceed call get/result", + "createdAt": "2026-01-27T03:38:07.7534643Z", + "ttl": 60000, + "pollInterval": 100 + }, +} +``` + +5. Client Request sends message `tasks/result` to discover what input is required to proceed. +```json +{ + "jsonrpc": "2.0", + "id": 3, + "method": "tasks/result", + "params": { + "taskId": "echo_dc792e24-01b5-4c0a-abcb-0559848ca3c5" + } +} +``` + +6. Server Response returns `elicitation/create` to request additional input +```json +{ + "id": 3, + "jsonrpc": "2.0", + "method": "elicitation/create", + "params": { + "mode": "form", + "message": "Please provide the input string to echo back", + "requestedSchema": + { + "type": "object", + "properties": + { + "input": {"type": "string"} + }, + "required": ["input"] + } + }, + "_meta": + { + "io.modelcontextprotocol/related-task": + { + "taskId": "echo_dc792e24-01b5-4c0a-abcb-0559848ca3c5" + } + } +} +``` + +7. Client Request presents the Elicitation to the user and collects the input, then sends message to the server. +```json +{ + "jsonrpc": "2.0", + "id": 4, + "result": { + "action": "accept", + "content": { + "input": "Hello World!" + }, + "_meta": { + "io.modelcontextprotocol/related-task": { + "taskId": "echo_dc792e24-01b5-4c0a-abcb-0559848ca3c5" + } + } + } +} +``` + +8. Server Response .Currently there is no required response to this message, but the server can now proceed to complete the `Task` using the provided input, and the `Task` status changes to `Working` + +9. Client Request continues to poll the input status using `tasks/get` until server responds with Task Status of `Completed` +Client Request +```json +{ + "jsonrpc": "2.0", + "id": 5, + "method": "tasks/get", + "params": { + "taskId": "echo_dc792e24-01b5-4c0a-abcb-0559848ca3c5" + } +} +``` +10. Server Response with Task status `Completed` +```json +{ + "id": 5, + "jsonrpc": "2.0", + "result": + { + "taskId": "echo_dc792e24-01b5-4c0a-abcb-0559848ca3c5", + "status": "Completed", + "statusMessage": "Task has been completed successfully, call get/result", + "createdAt": "2026-01-27T03:38:07.7534643Z", + "ttl": 60000, + "pollInterval": 100 + }, +} +``` + +11. Client Request calls `tasks/result` to get the final result of the `Task` from the server. +Client Message +```json +{ + "id": 6, + "jsonrpc": "2.0", + "method": "tasks/result", + "params": + { + "taskId": "echo_dc792e24-01b5-4c0a-abcb-0559848ca3c5" + }, +} +``` +12. Server Response with the final result of the `Task` +```json +{ + "id": 6, + "jsonrpc": "2.0", + "result": + { + "isError": false, + "content": + [{ + "type": "text", + "text": "Echo: Hello World!" + }], + "_meta": + { + "io.modelcontextprotocol/related-task": + { + "taskId": "echo_dc792e24-01b5-4c0a-abcb-0559848ca3c5" + } + } + }, +} +``` + +## Discussion Points +Both Tool Calls and Task Results should follow the same pattern when requesting additional input via Elicitation or Sampling. Having different mechanisms and messaging pattern leads to complexity in implementation and confusion. + +In both implementations a request for more information is treated as a special result. This can be viewed as a recoverable error case. In Tasks the request for more input is retrieved via the `tasks/result` message, while in Tool Calls it is returned directly as the result of the `tools/call` message. + +Given the below options for response types should be considered. + +### Option One - MRTR & Tasks return existing Elicitaiton or Sampling Messagees. +Today this is what Tasks does. The response to a `tasks/result` call when additional input is required is to return an `elicitation/create` or `sampling/createMessage` message. + +Pros: Smaller changes to existing implementations. + +Cons: Message structure does not align with the `result` message structure used by Completed Results and Error Messages. + +### Option Two - MRTR & Tasks return a Result Wrapper around Elicitation & Sampling +This would involve defining a new [`ToolResult`](https://modelcontextprotocol.io/specification/draft/server/tools#tool-result) Content type that wraps an Elicitation or Sampling request. + +This could look like, and would replace the response in step 6 above: +```json +{ + "id": 3, + "jsonrpc": "2.0", + "result":{ + "content": [ + { + "type": "elicitation", + "mode": "form", + "message": "Please provide the input string to echo back", + "requestedSchema": + { + "type": "object", + "properties": + { + "input": {"type": "string"} + }, + "required": ["input"] + } + }], + "isError": false, + } +} +``` + +Pros: +- Consistent message structure for all Result types from Tasks & Tools which simplifies the SDK implementations. +- Consistent handling of isError or other future result metadata fields. +- With Tasks this structure would allow for partial results & elicitation & sampling requests to be part of the same message. +- Supports multiple Elicitation & Sampling requests at the same time. Notably with Tasks this structure would allow for partial results to also be retured as part of the 'result' message in addition to Elicitation or Sampling requests. + +Cons: Larger change to existing implementations. +- Requires deprecating the existing `Elicitation` and `Sampling` messages returned by Tasks since out of band messages are no longer needed. +- Need to `ToolResult` Content schema as a `Utilities` `Results` schema to indicate it's not just for `Tools` since they are already being used by `Tasks` and will now be extended further. From fb1bc81469603f8fd4d19f298ca8ad515fd3dff6 Mon Sep 17 00:00:00 2001 From: Caitie McCaffrey Date: Mon, 26 Jan 2026 20:59:22 -0800 Subject: [PATCH 2/6] Fixing typos --- proposals/MRTR-And-Tasks-Message-Structure-Proposal.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/MRTR-And-Tasks-Message-Structure-Proposal.md b/proposals/MRTR-And-Tasks-Message-Structure-Proposal.md index 91b02e2..66e8823 100644 --- a/proposals/MRTR-And-Tasks-Message-Structure-Proposal.md +++ b/proposals/MRTR-And-Tasks-Message-Structure-Proposal.md @@ -251,8 +251,8 @@ This could look like, and would replace the response in step 6 above: Pros: - Consistent message structure for all Result types from Tasks & Tools which simplifies the SDK implementations. - Consistent handling of isError or other future result metadata fields. -- With Tasks this structure would allow for partial results & elicitation & sampling requests to be part of the same message. -- Supports multiple Elicitation & Sampling requests at the same time. Notably with Tasks this structure would allow for partial results to also be retured as part of the 'result' message in addition to Elicitation or Sampling requests. +- With Tasks this structure would allow for partial results & additional input to requested on the same get/results +- Supports multiple Elicitation & Sampling requests at the same time. Cons: Larger change to existing implementations. - Requires deprecating the existing `Elicitation` and `Sampling` messages returned by Tasks since out of band messages are no longer needed. From 3f4067072b04b121792fc40ae2701feb4b947980 Mon Sep 17 00:00:00 2001 From: Caitie McCaffrey Date: Wed, 28 Jan 2026 08:49:55 -0800 Subject: [PATCH 3/6] Apply suggestion from @halter73 Co-authored-by: Stephen Halter --- proposals/MRTR-And-Tasks-Message-Structure-Proposal.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/MRTR-And-Tasks-Message-Structure-Proposal.md b/proposals/MRTR-And-Tasks-Message-Structure-Proposal.md index 66e8823..eb83147 100644 --- a/proposals/MRTR-And-Tasks-Message-Structure-Proposal.md +++ b/proposals/MRTR-And-Tasks-Message-Structure-Proposal.md @@ -68,8 +68,8 @@ The below example uses an Echo Tool with an optional input parameter, when missi "result": { "taskId": "echo_dc792e24-01b5-4c0a-abcb-0559848ca3c5", - "status": "InputRequired", - "statusMessage": "Input Required to Proceed call get/result", + "status": "input_require", + "statusMessage": "Input Required to Proceed call tasks/result", "createdAt": "2026-01-27T03:38:07.7534643Z", "ttl": 60000, "pollInterval": 100 From cf18c585a273e66f5b6d646edb4c4b7c6948a743 Mon Sep 17 00:00:00 2001 From: Caitie McCaffrey Date: Wed, 28 Jan 2026 09:14:37 -0800 Subject: [PATCH 4/6] Adding additional Overview Adding additional overview to capture Discord discussion --- ...MRTR-And-Tasks-Message-Structure-Proposal.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/proposals/MRTR-And-Tasks-Message-Structure-Proposal.md b/proposals/MRTR-And-Tasks-Message-Structure-Proposal.md index eb83147..4631a9b 100644 --- a/proposals/MRTR-And-Tasks-Message-Structure-Proposal.md +++ b/proposals/MRTR-And-Tasks-Message-Structure-Proposal.md @@ -3,6 +3,23 @@ ## Overview This document provides an overview of how the [Multi-Round-Trip Request Proposal](https://github.com/modelcontextprotocol/transports-wg/pull/7/changes#diff-c42674696a4c91ccc0d2daf8425dbcb52201ec1ef75921ae1e4865b5b911018d) (MRTR) fits with the Tasks by walking thorugh an example with Elicitations. +A few notes the Goals of MRTR are to: +1. Ensure the protocol itself is stateless while allowing for stateful application semantics +2. Eliminating the need for the SSE stream, this is marked as Optional in the transport today so should only be used for optimization/performance not required. +3. Remove the ability for sampling/elicitation to be sent out of band (i.e. without a client request). + +In Tasks this means: +1. TaskId is used to represent the application state +2. Remove the guidance around using SSE stream, and model the call flow as request/response semantics. + +Tasks & Tool Calls provide mecahnisms for implementing two different kinds of messaging patterns. +1. Tool Calls: short running/sync - i.e. return within ms/seconds, low cost to compute/answer +2. Tasks: long running/async - i.e. can run for minutes to hours, higher cost to compute. + +Given the above these patterns will manifest MRTR in two different ways. +1. Tool Calls: Server sends a Result with an elicitation/sampling request. The server stops processing the request at this point, and the client must retry +2. Tasks: Server sets status to input_required. The client will make a request for the Result which should contain the elicitation/sampling request. The server can pause processing, while the client gathers the info, and then updates the server. Once the necessary info has been retrieved the server can resume processing. + It also raises open questions and proposed solutions for discussion on what the return type should be for Tool Requests that require Elicitation or Sampling to complete. ## Tasks Background From 0e8ae24aa30a30018ea536446dff7e08e7309d62 Mon Sep 17 00:00:00 2001 From: Caitie McCaffrey Date: Fri, 30 Jan 2026 08:25:47 -0800 Subject: [PATCH 5/6] update with comments from transport-wg discussion --- proposals/MRTR-And-Tasks-Message-Structure-Proposal.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/MRTR-And-Tasks-Message-Structure-Proposal.md b/proposals/MRTR-And-Tasks-Message-Structure-Proposal.md index 4631a9b..66594ba 100644 --- a/proposals/MRTR-And-Tasks-Message-Structure-Proposal.md +++ b/proposals/MRTR-And-Tasks-Message-Structure-Proposal.md @@ -5,16 +5,16 @@ This document provides an overview of how the [Multi-Round-Trip Request Proposal A few notes the Goals of MRTR are to: 1. Ensure the protocol itself is stateless while allowing for stateful application semantics -2. Eliminating the need for the SSE stream, this is marked as Optional in the transport today so should only be used for optimization/performance not required. +2. Making the GET SSE stream truly optional, i.e. not required for core functionality 3. Remove the ability for sampling/elicitation to be sent out of band (i.e. without a client request). In Tasks this means: 1. TaskId is used to represent the application state -2. Remove the guidance around using SSE stream, and model the call flow as request/response semantics. +2. Remove the guidance around using SSE stream, and model the call flow as request/response semantics only. Tasks & Tool Calls provide mecahnisms for implementing two different kinds of messaging patterns. -1. Tool Calls: short running/sync - i.e. return within ms/seconds, low cost to compute/answer -2. Tasks: long running/async - i.e. can run for minutes to hours, higher cost to compute. +1. Tool Calls: short running/sync/stateless - i.e. return within ms/seconds, low cost to compute/answer. +2. Tasks: long running/async/stateful - i.e. can run for minutes to hours, higher cost to compute. Server may be storing state between calls related to TaskId. Given the above these patterns will manifest MRTR in two different ways. 1. Tool Calls: Server sends a Result with an elicitation/sampling request. The server stops processing the request at this point, and the client must retry @@ -85,7 +85,7 @@ The below example uses an Echo Tool with an optional input parameter, when missi "result": { "taskId": "echo_dc792e24-01b5-4c0a-abcb-0559848ca3c5", - "status": "input_require", + "status": "input_required", "statusMessage": "Input Required to Proceed call tasks/result", "createdAt": "2026-01-27T03:38:07.7534643Z", "ttl": 60000, From 63c7694a5d19b93e76309c6917b7247be118dbb6 Mon Sep 17 00:00:00 2001 From: Caitie McCaffrey Date: Fri, 30 Jan 2026 08:30:00 -0800 Subject: [PATCH 6/6] Apply suggestion from @evalstate Co-authored-by: shaun smith <1936278+evalstate@users.noreply.github.com> --- proposals/MRTR-And-Tasks-Message-Structure-Proposal.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/MRTR-And-Tasks-Message-Structure-Proposal.md b/proposals/MRTR-And-Tasks-Message-Structure-Proposal.md index 66594ba..0a8b079 100644 --- a/proposals/MRTR-And-Tasks-Message-Structure-Proposal.md +++ b/proposals/MRTR-And-Tasks-Message-Structure-Proposal.md @@ -1,7 +1,7 @@ # MRTR And Tasks Message Structure Proposal ## Overview -This document provides an overview of how the [Multi-Round-Trip Request Proposal](https://github.com/modelcontextprotocol/transports-wg/pull/7/changes#diff-c42674696a4c91ccc0d2daf8425dbcb52201ec1ef75921ae1e4865b5b911018d) (MRTR) fits with the Tasks by walking thorugh an example with Elicitations. +This document provides an overview of how the [Multi-Round-Trip Request Proposal](https://github.com/modelcontextprotocol/transports-wg/pull/7/changes#diff-c42674696a4c91ccc0d2daf8425dbcb52201ec1ef75921ae1e4865b5b911018d) (MRTR) fits with the Tasks by walking through an example with Elicitations. A few notes the Goals of MRTR are to: 1. Ensure the protocol itself is stateless while allowing for stateful application semantics