Skip to main content
  • 状态(Status): Final
  • 类型(Type): Extensions Track
  • 创建时间(Created): 2026-04-27
  • 作者(Author(s)): Luca Chang (@LucaButBoring), Caitie McCaffrey (@CaitieM20);代表 Agents Working Group
  • 发起人(Sponsor): Caitie McCaffrey (@CaitieM20)
  • 扩展标识符(Extension Identifier): io.modelcontextprotocol/tasks
  • PR: https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2663

摘要(Abstract)

本 SEP 定义一个扩展,允许服务器以异步的 任务句柄(task handle) 而非最终结果来响应 tools/call 请求,使客户端可以通过轮询取回最终结果。该扩展引入三个方法:tasks/gettasks/updatetasks/cancel;一个多态结果判别器(resultType: "task");以及一个携带任务状态、进行中的服务器到客户端请求、最终结果或错误的 Task 形态。任务创建由服务器主导:客户端通过在其按请求能力中包含该扩展来表示支持,而服务器按请求逐一决定是否物化一个任务。 任务将成为 MCP 的基础构件,并预计在未来的协议版本中得到支持。2025-11-25 规范中的实验性 tasks 特性在协议的扩展机制可用之前充当了权宜之计。既然 扩展 已被正式化,将任务移至一个官方扩展能让该特性有时间孵化并基于更多真实世界的实现反馈演进,而不受核心规范发布节奏的约束。一旦该扩展稳定并获得广泛采用,即计划将其提升进核心协议。 本提案将 2025-11-25 发布中规定的 tasks 版本从核心协议中 移除,并移至一个扩展。它还提议基于该发布以来的实现反馈、以及 2026-06-30 规范中对基础协议的若干变更,对任务进行更新:

动机(Motivation)

实验性 tasks 特性充当了工具调用、征询与采样的一种替代执行模式,让接收方可以返回一个轮询句柄,而非阻塞直到最终结果就绪。实现经验暴露了若干挑战:
  1. 握手脆弱。 如今任务暴露方法级能力(tasks.requests.tools.call 声明 tools/call 可以(MAY) 被任务增强),同时有一个工具级的 execution.taskSupport 字段声明某个特定工具是否接受该增强。客户端通过在其请求上传递 task 参数来表达自己对任务的支持,但若方法/工具不支持任务则 必须不(MUST NOT) 包含它。因此想要选择加入任务的客户端必须先用一次 tools/list 调用来给其状态预热,之后才能发出任何任务增强请求,并且不能盲目地给每个请求附加 task 参数来同构地处理工具。这令人困惑、隐晦且容易出错。
  2. tasks/result 是一个阻塞陷阱。 在当前流程中,观察到 input_required 的客户端被要求过早地调用 tasks/result,以便服务器有一条 SSE 流可在其上旁路发送征询或采样请求。tasks/result 随后会阻塞直到整个操作完成。这迫使产生许多客户端和服务器并不想实现的长连接,并且它与 SEP-2260 冲突——后者彻底禁止未经请求的服务器到客户端请求。在 SEP-2260 之下,为阻塞行为辩护的 SSE 语义已不再适用。
  3. tasks/list 的作用域无法定义。 为避免客户端取消或取回它们本不应访问的任务的结果,所有任务都应绑定到某种 “授权上下文”,其实现留给各服务器按其既有的定制权限模型处理。然而在许多情况下无法进行这种绑定,此时任务 ID 成为防止污染的唯一防线。在此情形下,服务器根本就不安全去支持 tasks/list。虽然任务原本可以改为绑定到一个会话,但 SEP-2567 从协议中移除了会话。没有其他服务器可以单方面定义的自然作用域——任务 ID 可以是不可猜测的句柄,服务器可以逐个识别,但服务器无法在没有额外状态的情况下可靠地将两个无关句柄关联到同一调用方。
除实现挑战外,任务还面临另一个结构性问题:客户端托管的任务不再可表达。 SEP-1686 允许客户端为征询和采样托管任务,部分原因是为了避免将任务耦合到工具调用。SEP-2260 使任何未经请求的服务器到客户端请求无效;客户端托管任务下的每个服务器到客户端轮询请求按定义都将是未经请求的。 本提案意在通过重新设计该特性的某些方面并将任务移出为一个官方扩展来解决上述问题。将任务重新定义为官方扩展,让该特性有更多时间独立于核心规范孵化与演进,促进采用。作为重新设计的一部分,本提案将轮询生命周期整合进 tasks/get 和一个新的 tasks/update,以移除阻塞的 tasks/result 方法。该重新设计允许服务器未经请求地返回任务(响应普通的、未打 task 标记的请求),从而消除按请求的选择加入和 tools/list 预热,转而依赖扩展能力作为单一握手点。最后,本提案为遵循 SEP-2260 移除了客户端托管的征询和采样任务。

规范(Specification)

MCP 任务扩展允许某些请求被 任务(tasks) 增强。任务是持久的状态机,携带关于它们所增强请求底层执行状态的信息,旨在用于客户端轮询和延迟的结果取回。每个任务由服务器生成的 任务 ID(task ID) 唯一标识。 任务对于表示昂贵计算和批处理请求很有用,并能自然映射到外部作业 API 上。

扩展标识符(Extension Identifier)

此扩展标识为:io.modelcontextprotocol/tasks

能力协商(Capability Negotiation)

客户端与服务器在各自的 capabilities 对象中声明对任务扩展的支持(使用 SEP-2575:让 MCP 无状态 的更新形式):
当前未定义任何扩展专属设置;空对象即表示支持。 已协商此扩展的服务器 可以(MAY) 自行裁量、按请求逐一,在响应任何受支持的请求时返回 CreateTaskResult 来代替标准结果(例如 CallToolResult)。服务器是唯一决策方;客户端不在请求本身上表达任务偏好。客户端声明扩展能力并不意味着它要求对该请求返回 CreateTaskResult 服务器 必须不(MUST NOT) 向在其请求上未包含扩展能力的客户端返回 CreateTaskResult,无论此前有何声明。已协商此扩展的客户端 必须(MUST) 准备好处理对其发出的任何受支持请求返回的 CallToolResultCreateTaskResult。客户端若收到对不受支持请求类型返回的 CreateTaskResult必须(MUST) 将其解释为对该请求的无效响应。 如果服务器无法在不返回 CreateTaskResult 的情况下为未声明此扩展能力的客户端提供服务,服务器 必须(MUST) 返回错误码 -32021(Missing Required Client Capability)的错误,并在错误响应中指明所需扩展:

受支持的方法(Supported Methods)

以下方法当前支持任务增强执行:
  • tools/call
本规范未来可能扩展以支持对其他请求类型使用任务;实现 应当(SHOULD) 被设计为能在本规范未来的修订中容纳更多请求类型。

多态结果(Polymorphic Results)

有资格接受任务增强的请求可能返回两种不同的结果形态之一——请求的标准结果,或一个 CreateTaskResult。判别器是结果对象上的 resultType 字段,由 SEP-2322 引入:
服务器在返回 CreateTaskResult必须(MUST)resultType 设为 "task",以便客户端能将其与标准结果区分开。服务器 必须不(MUST NOT)CreateTaskResult 以外的结果类型上将 resultType 设为 "task" 提醒客户端实现者:返回固定形态的既有代码(例如返回 CallToolResulttools/call 方法)无需改变其公共契约——它们可以在内部透明地驱动轮询流程,只对外呈现最终的、已完成的结果。新的实现面 可以(MAY) 直接暴露任务生命周期,供有能力利用它的应用使用。

任务(Tasks)

Task 携带关于进行中工作的操作性元数据。

任务状态(Task Status)

任务可以处于以下状态之一:
  • working:请求当前正在被处理。
  • input_required:服务器需要来自客户端的输入才能使任务继续。tasks/get 响应将在 inputRequests 字段中包含未决请求。客户端 必须(MUST) 检查此字段,并 应当(SHOULD) 在后续的 tasks/update 请求中经由 inputResponses 字段提供响应。
  • completed:请求成功完成,结果在 result 字段中可用。这包括返回了 isError: true 结果的工具调用。
  • failed:请求因执行期间的一个 JSON-RPC 错误而失败。任务将包含带 JSON-RPC 错误详情的 error 字段。此状态 必须不(MUST NOT) 用于非 JSON-RPC 错误。
  • cancelled:请求在完成前被取消。
Task 的派生形态内联状态专属的载荷字段,用于 tasks/get 响应和 notifications/tasks 通知:

任务创建(Task Creation)

服务器对一个请求返回 CreateTaskResult 来代替标准结果形态,以指示该请求将被异步处理。
示例请求(CallToolRequest):
示例响应(CreateTaskResult):
内嵌的 task 是任务的初始种子状态,通常(但不必然)为 status: "working"。客户端在所有后续的 tasks/gettasks/updatetasks/cancel 调用中使用 task.taskId 服务器 必须不(MUST NOT) 在任务被持久创建之前返回 CreateTaskResult——即在针对返回的 taskIdtasks/get 能够解析之前。在最终一致性的环境中,服务器 必须(MUST) 等待一致性达成后再响应。此要求消除了客户端为任务创建推测性轮询的需要。 将多轮往返请求与任务创建结合使用的服务器实现(例如一个在创建任务前需要经由 InputRequiredResult 进行征询的工具)应当(SHOULD) 在以 CreateTaskResult 响应之前 同步地 解决所有 MRTR 交换。

任务轮询(Task Polling)

客户端通过发送 tasks/get 请求来轮询任务完成。 客户端在确定轮询频率时 应当(SHOULD) 尊重响应中提供的 pollIntervalMspollIntervalMs 可以(MAY) 在任务生命周期内改变。服务器 可以(MAY) 对轮询频率高于所记录 pollIntervalMs 的客户端进行限流。 客户端 应当(SHOULD) 持续轮询,直到任务达到终态或直到调用 tasks/cancel。客户端 应当(SHOULD) 将任务 ID 持久化到持久存储,以便在崩溃或重启后恢复轮询。

请求

响应

收到 tasks/get 请求后,服务器 必须(MUST) 检查任务状态并相应响应:
  1. 若状态为 working,服务器 必须(MUST) 返回一个 status 为 workingTask 对象。
  2. 若状态为 input_required,服务器 必须(MUST) 返回一个 status 为 input_required 且带有 多轮往返请求 中定义的 inputRequests 字段的 Task 对象。inputRequests 字段 必须(MUST) 包含服务器到客户端的所有、需要在任务继续前被满足的未决请求。
  3. 若状态为 completed,服务器 必须(MUST) 返回一个 status 为 completed 且带有含任务最终结果的 result 字段的 Task 对象。
  4. 若状态为 cancelled,服务器 必须(MUST) 返回一个 status 为 cancelledTask 对象。
  5. 若状态为 failed,服务器 必须(MUST) 返回一个 status 为 failed 且带有执行期间发生错误的 Task 对象。
响应携带与任务当前状态对应的恰当响应变体(见 任务状态)。此对象上的 resultType 字段 必须(MUST) 设为 "complete",因为它是 tasks/get 请求的标准结果形态。 若任务具有非 null 的 ttlMs,客户端 可以(MAY) 将 TTL 视为兜底:若在 createdAtttlMs 过去后任务的可观测状态仍未反映更新,客户端 可以(MAY) 认为该任务不再可用。反之,服务器 可以(MAY) 在 TTL 过去后的任意时刻将任务标记为 failed,并随后在任意时刻删除它。ttlMs 的值 可以(MAY) 在任务生命周期内改变。

任务更新请求(Task Update Requests)

当任务需要来自客户端的输入(由 input_required 状态指示)时,服务器在 tasks/get 响应的 inputRequests 字段中包含未决请求(见 多轮往返请求)。客户端在一个或多个后续的 tasks/update 请求中经由 inputResponses 字段提供响应。 当客户端观察到带 status: "input_required"tasks/get 响应(或 notifications/tasks 通知)时,客户端 应当(SHOULD) 通过发送一个或多个带相应 inputResponsestasks/update 请求来满足 inputRequests 中的未决请求。发送 tasks/update 后,客户端 应当(SHOULD) 继续经由轮询(tasks/get)或通知(notifications/tasks)观察任务状态,直到它达到终态。 客户端 必须(MUST)inputRequests 中的每个条目视同等价的独立服务器到客户端请求——例如,经由 inputRequests 呈现的征询请求与直接的 elicitation/create 请求受同样的信任模型与面向用户行为约束。客户端 应当(SHOULD) 在连续轮询间对 inputRequests 键去重,以避免向用户或模型多次呈现同一请求。 inputRequests 中的每个请求键在单个任务的生命周期内 必须(MUST) 唯一。服务器 必须不(MUST NOT) 在某个键的响应已被投递后,为后续的服务器到客户端请求重用该键,并 必须不(MUST NOT) 在一个任务的生命周期内用同一键指代两个不同的请求。这保证了以同一标识符为键的 inputResponses 总是指向客户端所期待的请求,消除了客户端跨轮询去重的歧义,并让服务器可以忽略针对未知或已满足请求的 inputResponses

请求

响应

成功时,服务器 必须(MUST) 以空结果确认该请求。该确认是 最终一致的:服务器 可以(MAY) 接受响应并在任务的可观测状态(经由 tasks/getnotifications/tasks)反映它们之前返回确认。若 taskId 不对应已知任务,服务器 应当(SHOULD) 返回 JSON-RPC 错误。客户端 应当(SHOULD) 跟踪 inputRequests 键以避免对请求响应多于一次。 服务器 应当(SHOULD) 忽略任何映射到当前对该任务非未决的键的 inputResponses 响应——包括从未发出的键、已被回答的键,以及其对应请求已被取代的键。服务器 可以(MAY) 接受一组部分响应(当前未决键的一个严格子集); UpdateTaskResult 上的 resultType 字段 必须(MUST) 设为 "complete",因为它是 tasks/update 请求的标准结果形态。

任务取消(Task Cancellation)

客户端发送 tasks/cancel 请求来表示其取消进行中任务的意图。notifications/cancelled 通知 必须不(MUST NOT) 用于任务取消。

请求

响应

服务器 必须(MUST) 以空结果确认该请求。若 taskId 不对应已知任务,服务器 应当(SHOULD) 返回 JSON-RPC 错误。取消处理是 最终一致的——任务的可观测状态 可以(MAY) 在确认后仍为 working(或某个其他非终态),并 可以(MAY) 在工作于取消生效之前已完成时最终达到 cancelled 以外的终态。 取消是 协作式的(cooperative):请求表示意图,而服务器决定是否以及何时予以尊重。服务器没有义务真正停止工作;它只有义务确认该请求。并不保证最终会转换到 cancelled 客户端 可以(MAY) 在发送取消后立即删除与该任务关联的所有状态(例如,它不再需要保留已响应过的 inputRequests 键列表)。客户端无需再次轮询 tasks/get 来等待任务达到 cancelled 状态。 CancelTaskResult 上的 resultType 字段 必须(MUST) 设为 "complete",因为它是 tasks/cancel 请求的标准结果形态。

任务状态通知(Task Status Notifications)

除服务于客户端轮询外,服务器 可以(MAY) 经由 notifications/tasks 通知推送状态更新:
为开始监听任务状态通知,客户端向服务器发送一个 subscriptions/listen 请求,包含客户端感兴趣的任务 ID 列表(见 SEP-2575):
在其确认通知中,服务器包含它已同意为之发送任务状态通知的任务 ID 列表(如果有的话):
若客户端请求任务状态通知但未声明 io.modelcontextprotocol/tasks 扩展能力,服务器 必须(MUST) 返回指明缺失能力的 JSON-RPC 错误:
每条通知携带当前状态的完整 DetailedTask,与 tasks/get 在那一刻会返回的内容相同。 通知:
该通知包含完整的任务对象,允许客户端无需轮询 tasks/get 方法即可访问完整的任务状态与最终结果。客户端 可以(MAY) 在订阅任务状态通知之外继续轮询 tasks/get,但无需这么做。 notifications/progressnotifications/message 通知 必须不(MUST NOT) 在某任务的 subscriptions/listen 流上发送,在本规范中总体上也不在任务上受支持。

Streamable HTTP:路由头部

tasks/gettasks/updatetasks/cancel 经由 Streamable HTTP 传输发送时,客户端 必须(MUST)Mcp-Name 头部(由 SEP-2243 定义)设为 params.taskId 的值。这允许传输中间件和负载均衡器将同一任务的后续请求路由到持有其状态的服务器实例,而这通常是正确性所必需的。Mcp-Method 头部按 SEP-2243 设为 JSON-RPC 方法名。

示例消息流程

设想一个简单的工具调用 hello_world,它需要一次征询让用户提供姓名。该工具本身不接受参数。 为调用此工具,客户端发起一个 CallToolRequest 如下:
服务器(经由定制逻辑)判定它想要创建一个任务来表示这项工作,于是立即返回一个 CreateTaskResult
客户端收到 CreateTaskResult 后,即开始轮询 tasks/get
在任务处于 "working" 状态期间的每个请求上,服务器返回一个常规任务响应:
最终,服务器到达需要向用户发送征询的点。它将任务状态设为 "input_required" 以发出此信号。在客户端的下一个 tasks/get 请求上,服务器经由 inputRequests 字段发送征询载荷。请注意,虽然任务 inputRequestsSEP-2322 多轮往返请求有结构上的相似性,但它们是一个不同的机制:任务 inputRequests 经由 tasks/get 呈现、经由 tasks/update 满足,而非经由原始方法的重试。需要在返回 CreateTaskResult 之前 获取客户端输入(例如为决定是否继续)的服务器,在原始请求上使用多轮往返请求流程;需要在任务执行 期间 获取客户端输入的服务器,使用此处描述的 inputRequests/inputResponses 机制。
为求周全,我们考虑一种情形:客户端恰好在用户满足征询请求 之前 再次轮询 tasks/get。由于 inputRequests 实际上是任务关联的所有未决服务器到客户端请求的时间点快照,服务器会再次包含同一请求,尽管客户端已经见过这一信息(建议客户端出于 UX 目的对同一键的 inputRequests 去重):
用户输入其姓名,客户端携带所满足的信息发起一个 tasks/update 请求:
服务器确认该请求:
异步地,服务器处理它并将任务移回 working 状态:
最终,服务器完成请求,于是它存储最终的 CallToolResult 并将任务移入 "completed" 状态。在下一个 tasks/get 请求上,服务器发送内联进任务对象中的最终工具结果:

错误处理(Error Handling)

任务使用两种错误报告机制:
  1. 协议错误(Protocol Errors):用于协议级问题的标准 JSON-RPC 错误
  2. 任务执行错误(Task Execution Errors):底层请求执行中的错误,通过任务状态报告

协议错误

服务器 必须(MUST) 为以下协议错误情形返回标准 JSON-RPC 错误:
  • 无效或不存在的 taskId-32602(Invalid params)
    • 服务器 必须(MUST)tasks/get 返回此错误。
    • 服务器 应当(SHOULD)tasks/updatetasks/cancel 返回此错误。
  • 内部错误:-32603(Internal error)
  • 缺少必需的客户端能力:-32021(Missing Required Client Capability)
    • 服务器 必须(MUST) 为在 subscriptions/listen 上请求任务通知的未声明客户端返回此错误。
    • 服务器 必须(MUST) 为发出 tasks/gettasks/updatetasks/cancel 请求的未声明客户端返回此错误。
服务器 应当(SHOULD) 提供信息丰富的错误消息来描述错误原因。 示例:任务未找到
示例:任务已过期
服务器无需无限期保留任务。若服务器已清除一个过期任务,它返回一个声明找不到该任务的错误是符合规范的行为。

任务执行错误

当底层请求在执行期间遇到 JSON-RPC 协议错误时,任务移入 failed 状态。tasks/get 响应 应当(SHOULD) 包含带关于失败的诊断信息的 statusMessage 字段,并 必须(MUST) 包含带 JSON-RPC 错误的 error 字段。 failed 状态 必须不(MUST NOT) 用于表示非 JSON-RPC 错误,例如以 isError: true 完成的工具结果。协议方法结果上下文内的错误 必须(MUST) 使用 completed 状态,并在 result 字段中附带错误详情。这在协议级故障(使用 failed 状态)与其他故障之间保持了强分离。 示例:带 JSON-RPC 执行错误的任务
示例:以工具错误(isError: true)完成的工具调用 对于在协议级成功完成但返回工具级错误(由工具结果中的 isError: true 指示)的工具调用,任务达到 completed 状态,工具结果在 result 字段中:
tasks/get 端点恰好返回底层请求本会返回的内容:
  • 若底层请求导致了 JSON-RPC 错误,任务使用 failed 状态,且 error 字段 必须(MUST) 包含该 JSON-RPC 错误。
  • 若请求以结果完成(即使工具结果为 isError: true),任务使用 completed 状态,且 result 字段 必须(MUST) 包含该结果。

保留项(Reservations)

  • tasks/ 方法前缀和 notifications/tasks/ 通知前缀为本扩展保留。
  • resultType 的结果判别值 "task" 为本扩展保留。
  • 标签 io.modelcontextprotocol/tasks 为本扩展保留。

理由(Rationale)

未经请求的任务 vs. 即时结果

一份替代提案本会单独处理即时结果情形,且前提条件略有不同: 支持任务, 客户端支持即时任务结果, 服务器可以对一个任务增强请求返回一个常规结果。那一版的即时结果在当时看来是更好的选择,因为它意味着在初始任务规范之上没有破坏性变更。 然而,随着我们着眼于摆脱有状态的协议交互,并鉴于任务总体上当前的实验性状态,此时提出一个稍微更激进、能降低整体规范复杂度并使任务对 MCP 更 “原生” 的变更似乎是值得的。特别地,允许未经请求的任务(在即时结果 之外)这一选择,意味着将任务提升为一个面向所有持久操作的一等概念,而非一个并行且略显特化的概念。 这恰好与所提议的 SEP-2322 对齐,但两者彼此并不耦合。

分离读(tasks/get)与写(tasks/update

此次重新设计的早期草案让 tasks/get 携带 inputResponses,以便单次往返既提交响应又观察结果状态。这种混淆有代价:它使读路径非幂等(重试的 tasks/get 可能重复提交响应),它迫使读路径共享写的最终一致性模型,并且它使想要缓存或去重读操作的中间件复杂化。分离方法使 tasks/get 成为一个纯粹、幂等的读,任何层都能安全地缓存或重放,并将写语义——包括其最终一致性窗口——限定在 tasks/update tasks/update 的仅确认响应形态源自同一分离:没有服务器需要返回、而客户端无法从后续 tasks/get 获取的读数据,且强行将一个内嵌 Task 塞进响应会重新引入我们正试图避免的非幂等性。代价是每轮输入多一次往返——仅在任务实际需要客户端请求时才付出。

任务创建一致性

引入以下新要求:
服务器 必须不(MUST NOT) 在任务被持久创建之前返回 CreateTaskResult——即在针对返回的 taskIdtasks/get 能够解析之前。在最终一致性的环境中,服务器 必须(MUST) 等待一致性达成后再响应。此要求消除了客户端为任务创建推测性轮询的需要。
tasks/updatetasks/cancel 不同,任务创建是强一致的。这是必需的,以避免请求方发出推测性 tasks/get 请求——否则它们无从知晓某任务是被悄然丢弃还是仅仅尚未被创建。反之,tasks/updatetasks/cancel 中的最终一致性之所以可行,是因为客户端行为并不取决于这些操作的结果(客户端无论如何都可以继续轮询)。虽然一致的任务创建确实会在本来不这么行事的分布式系统中增加时延成本,但显式引入此要求简化了客户端实现,并消除了一个未定义行为的来源。 这也与长时运行操作 API 总体对齐,后者通常要求一旦某操作被确认,它就必须可经由轮询端点找到。

仅确认的取消

在任务的 2025-11-25 设计中,tasks/cancel 返回一个描述取消尝试后任务状态的任务。那种返回形态意味着一次同步读——服务器必须查询任务状态来填充它——但取消在许多应用中本质上是异步的(一个单独的 worker 决定是否以及何时予以尊重),因此返回的任务对象在许多情况下只会重复下一次 tasks/get 会显示的内容。将 tasks/cancel 简化为一个确认符合该操作的实际语义:该请求是一个信号,而非一次状态查询。想要知道取消后状态的客户端经由 tasks/get、在它们用于所有其他状态观察的同一代码路径上这么做。 确认上的最终一致性与 tasks/update 是同样的分离:服务器可以记录取消请求并在 worker 实际转换任务之前响应,同时不允许客户端将该确认解释为强一致。 虽然 tasks/updatetasks/cancel 出于上述原因使用仅确认的响应形态,服务器 应当(SHOULD) 仍为明显无效的请求返回错误——例如未知的 taskId。仅确认设计是为了在成功路径中避免对任务状态的同步读,而非为了抑制服务器在请求时即可检测到的错误。为无效输入返回错误能给客户端一个更快的出错信号,而非迫使它们经由后续 tasks/get 轮询间接发现问题。

与多轮往返请求的组合

引入以下新要求:
将多轮往返请求与任务创建结合使用的服务器实现(例如一个在创建任务前需要经由 InputRequiredResult 进行征询的工具)应当(SHOULD) 在以 CreateTaskResult 响应之前 同步地 解决所有 MRTR 交换。
同时支持 MRTR(SEP-2322)和本扩展的 tools/call 可以按顺序使用它们:先发送一个或多个 InputRequiredResult 交换以同步收集输入,随后以一个 CreateTaskResult 交接给异步执行。这种组合是 resultType 判别器的结果——每个响应独立定型,客户端根据它收到的值切换行为,无需 在两种模式之间维护任何状态。禁止这一点将需要强加一个在协议级无机制可强制执行的人为约束,因为客户端事先并不知道服务器将创建一个任务。 这两个流程尽管共享字段名,仍维护各自独立的状态。MRTR 阶段在服务器返回任何非 "input_required"resultType 时结束,此时其 inputRequests 键被消耗。任务阶段以 CreateTaskResult 开始,并独立维护 其自身的 inputRequests 键。任务 inputRequests 的键唯一性被限定在任务的生命周期内,不延伸到前面 MRTR 阶段的键。客户端无需跨这两个流程去重。

向后兼容性(Backward Compatibility)

2025-11-25 发布中的实验性任务特性与本扩展 在线格式上不兼容。需要与两个面互操作的实现可以在 SDK 层打补丁,方法是并行实现实验性流程与扩展流程,并根据所协商的协议版本和对端声明的客户端能力进行分派。下表总结了每种排列的预期行为: 返回标准 CallToolResult 形态——即从不选择创建任务——的服务器在本扩展下仍完全符合规范。已协商该扩展的客户端 必须(MUST) 为任何增强请求处理两种结果形态。

安全影响(Security Implications)

  • 任务 ID 的不可猜测性。 服务器 可以(MAY) 将任务 ID 用作其存储状态的 bearer token。服务器 必须(MUST) 以足够的熵生成它们,使第三方无法枚举或猜测它们。
  • 授权绑定。 服务器 必须(MUST) 对每个与任务相关的请求执行身份认证与授权检查,以确保客户端有权限访问某任务。
  • 跨调用方关联。 由于没有 tasks/list,服务器不会无意中把一个调用方任务的存在泄露给另一个。这相较于 2025-11-25 任务规范是一项改进——在后者中,一个作用域欠佳的列表可能暴露无关的任务 ID。
  • 输入请求信任模型。 inputRequests 携带从服务器经客户端到用户或模型的征询和采样载荷。宿主 必须(MUST) 对这些载荷应用与标准征询/采样请求相同的信任模型。任务并非更高信任的通道。

参考实现(Reference Implementation)

已在 mcpkit 中实现(见使用示例)。