Tasks
社区译文;来源: MCP 80041ab0
ext-tasks 仓库包含 MCP Tasks 的完整规范和文档。
modelcontextprotocol/ext-tasks
MCP Tasks 的完整规范和文档。
并非每次工具调用都能立即返回。有些操作,例如 CI 流水线、批处理和人工审批,需要数秒、数分钟甚至更长时间。MCP Tasks 允许服务器返回持久句柄,而不是一直阻塞;客户端可以轮询进度、在需要时提供输入,并在重新连接后获取最终结果。
为什么不直接阻塞?
你可以一直保持连接,直到工作结束。但 Tasks 能解决阻塞方式无法解决的问题:
- 无需长连接。 阻塞会在整个操作期间占用连接。许多客户端和传输中间件都会设置超时,因此超过几秒后,这种方式往往不可行。
- 故障恢复能力。 任务 ID 是持久句柄。如果客户端断开连接或重新启动,可以使用同一个 ID 恢复轮询。
- 进度可见性。 Tasks 携带状态元数据(
working、input_required、completed、failed、cancelled)和可选状态消息,让客户端能够了解进度。 - 执行中交互。 当任务需要输入(例如通过 elicitation 请求用户确认)时,会进入
input_required状态并公开该请求。客户端通过tasks/update响应,无需建立第二条连接,也不需要服务器主动向客户端发送未经请求的消息。 - 由服务器决定。 服务器按请求决定是否创建任务。客户端只需通过扩展能力选择启用一次,然后处理实际返回的任一种结果形态,无需按工具预热,也无需为每个请求设置标志。
Tasks 如何工作
Tasks 扩展了标准请求流程。当服务器认定某个请求会长时间运行时,会返回任务句柄,而不是最终结果。客户端随后轮询任务是否完成。
能力协商。 客户端在每个请求的能力中包含
io.modelcontextprotocol/tasks。服务器在自己的server/discover能力中公布同一个扩展。创建任务。 服务器响应受支持的请求时,返回
CreateTaskResult(通过resultType: "task"标识),其中包含taskId、初始状态、TTL 和建议轮询间隔。发送响应之前,任务必须已经持久化创建。轮询。 客户端使用
taskId调用tasks/get。响应包含当前状态;如果任务已到达终态,还会包含最终结果或错误。执行中输入。 如果任务进入
input_required状态,tasks/get响应会包含一个inputRequests映射,其中是 elicitation 或其他服务器请求。客户端通过tasks/update满足这些请求。完成。 状态变为
completed后,result字段包含原请求同步执行时应返回的内容。如果状态是failed,error字段包含 JSON-RPC 错误。取消。 客户端可以随时发送
tasks/cancel。取消采用协作式机制:服务器确认取消意图,但没有义务停止工作。
sequenceDiagram
participant Client
participant Server
Client->>Server: tools/call (with tasks capability)
Server-->>Client: CreateTaskResult (taskId, status: working)
loop Poll until terminal
Client->>Server: tasks/get (taskId)
Server-->>Client: Task (status: working)
end
Note over Client,Server: Server needs user input
Client->>Server: tasks/get (taskId)
Server-->>Client: Task (status: input_required, inputRequests)
Client->>Server: tasks/update (taskId, inputResponses)
Server-->>Client: ack
loop Poll until terminal
Client->>Server: tasks/get (taskId)
Server-->>Client: Task (status: working)
end
Client->>Server: tasks/get (taskId)
Server-->>Client: Task (status: completed, result)
何时使用 Tasks
Tasks 适合以下使用场景:
长时间运行的操作。 需要数分钟或数小时的 CI 流水线、批量数据处理或模型训练作业。
人参与的工作流。 审批门、评审步骤或任何暂停并等待用户确认的操作。任务进入 input_required 状态,客户端向用户呈现请求。
外部作业系统。 如果服务器封装的 API 已经使用作业 ID,例如云部署、异步 API 或排队作业,则可以在创建作业时返回任务,并在作业完成时解析任务结果。
不可靠的连接。 移动客户端、间歇性网络或连接容易中断的环境。任务 ID 在断开连接后仍然有效。
批处理。 处理大量项目的操作,例如批量导入或大规模更新,其中部分进度也有意义。状态消息可以报告进度。
任务生命周期
| 状态 | 含义 |
|---|---|
working | 操作正在进行。 |
input_required | 服务器需要客户端输入才能继续。请参阅 inputRequests。 |
completed | 操作已完成。result 字段包含最终输出。 |
failed | 执行期间发生 JSON-RPC 错误。error 字段包含详细信息。 |
cancelled | 操作已取消,但取消请求不一定总会得到执行。 |
completed、failed 和 cancelled 都是终态;任务一旦进入终态,其状态就不会再改变。
通知
服务器可以通过 notifications/tasks 推送状态更新。客户端通过 subscriptions/listen 机制选择接收。每条通知都携带完整任务状态,因此无需额外执行一次 tasks/get 往返。
默认方式是轮询。如果服务器支持通知,客户端可以依赖通知而不再轮询。
实现指南
MCP 客户端
要使用带任务信息的响应,客户端必须执行以下步骤:
声明支持
在每个请求的能力中包含该扩展:
{
"jsonrpc": "2.0",
"id": 1,
"method": "...",
"params": {
// Other fields...
"_meta": {
// Other fields...
"io.modelcontextprotocol/clientCapabilities": {
"extensions": {
"io.modelcontextprotocol/tasks": {},
},
},
},
},
}处理多态结果
发出受支持的请求(例如 tools/call)时,应准备接收标准结果,或接收 resultType: "task" 的 CreateTaskResult。
轮询完成状态
使用返回的 taskId 调用 tasks/get,并遵循 pollIntervalMs 的值。持续轮询,直到任务进入终态(completed、failed 或 cancelled)。
处理输入请求
如果任务状态为 input_required,读取 inputRequests 映射,把请求呈现给用户或模型,再通过 tasks/update 提交响应。
持久保存任务 ID
持久保存任务 ID,以便客户端崩溃或重启后恢复轮询。
MCP 服务器
要从服务器返回任务,需要执行以下步骤:
公布支持
在 server/discover 能力中包含该扩展:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
// Other fields...
"capabilities": {
"extensions": {
"io.modelcontextprotocol/tasks": {},
},
},
},
}检查客户端能力
返回 CreateTaskResult 之前,验证客户端是否在每个请求的能力中包含该扩展。绝不能向未声明支持的客户端返回任务。
返回 CreateTaskResult
当请求将长时间运行时,返回 resultType: "task",并提供一个 Task 对象,其中包含唯一的 taskId、初始状态、ttlMs 和 pollIntervalMs。发送响应前,任务必须已经持久化创建。
提供 tasks/get
每次轮询都返回当前任务状态。如果任务进入终态,则包含 result(completed 时)或 error(failed 时)字段。
处理 tasks/update
接收按未完成 inputRequests 的键组织的 inputResponses。以空结果确认请求。对未知或已经满足的键忽略响应。
处理 tasks/cancel
以空结果确认取消请求。应尽可能执行取消,但取消是协作式的,任务仍可能进入 cancelled 以外的终态。
客户端支持
请查看客户端矩阵,了解各客户端对扩展的支持情况。客户端和服务器都必须明确选择启用,才能使用 Tasks 支持。
规范
Tasks 扩展在 ext-tasks 仓库中定义。它使用标准 MCP 扩展协商机制:客户端在每个请求 _meta 内的 io.modelcontextprotocol/clientCapabilities 的 extensions 字段中声明支持,服务器则在 server/discover 返回的能力中通过 extensions 字段公布支持。