Mingyu's Library主页
AI 每日 · 深度学习 AI Daily · Deep Dive
2026-08-26 · Agent 工程 · MCP 扩展
2026-08-26 · Agent Engineering · MCP Extensions

MCP Tasks 扩展与异步长任务

MCP Tasks: Asynchronous Long-Running Operations

当一次工具调用要跑五分钟、要等人审批、要熬过一次客户端重启——阻塞连接就不再是答案。Tasks 用一个持久句柄换掉了长连接,把「等结果」变成一件可恢复、可取消、可中途追问的事。

When a single tool call takes five minutes, waits on a human approval, or has to survive a client restart, holding the connection open stops being an answer. Tasks trade the long-lived connection for a durable handle, turning "waiting for a result" into something resumable, cancellable, and interruptible.

阅读时间约 16 分钟 · 全部内容基于 2026-08-26 前公开的官方文档与规范
~16 min read · Based entirely on official docs and specs published before 2026-08-26
SEP-2663io.modelcontextprotocol/taskstasks/getinput_requiredsubscriptions/listenMRTR
SEP-2663io.modelcontextprotocol/taskstasks/getinput_requiredsubscriptions/listenMRTR

01为什么阻塞不够用Why blocking is not enough

最朴素的做法是:工具调用发出去,连接一直挂着,直到活干完再返回结果。对一个 200 毫秒的数据库查询,这完全没问题。问题出在那些「不是立刻返回」的操作上——CI 流水线、批量数据处理、人工审批。官方文档给出了四个阻塞解决不了的问题,每一个都对应一类真实故障:

The naive approach is simple: fire the tool call, hold the connection open, return the result when the work finishes. For a 200 ms database query that is entirely fine. The trouble starts with operations that do not return immediately — CI pipelines, batch processing, human approvals. The official docs list four problems blocking cannot solve, and each maps to a real class of failure:

① 不要长连接

阻塞会把一条连接占满整个操作时长。大量客户端和中间层(负载均衡、网关、WAF)都有超时,超过几秒就不现实了。

① No long-lived connections

Blocking ties up a connection for the whole operation. Many clients and transport intermediaries impose timeouts that make this impractical beyond a few seconds.

② 崩溃可恢复

task ID 是一个持久句柄。客户端断线或重启后,拿同一个 ID 继续轮询即可,活还在那儿。

② Crash resilience

A task ID is a durable handle. If the client disconnects or restarts, it resumes polling with the same ID — the work is still there.

③ 进度可见

任务自带状态元数据(workinginput_requiredcompletedfailedcancelled)和可选状态消息,客户端能看到进展。

③ Progress visibility

Tasks carry status metadata (working, input_required, completed, failed, cancelled) plus optional status messages, so clients can see progress.

④ 中途可交互

任务需要输入时(例如一次用户确认的 elicitation)转入 input_required 并把请求暴露出来,客户端用 tasks/update 回应——不需要第二条连接,也不需要服务端主动推消息

④ Mid-flight interaction

When a task needs input (an elicitation for user confirmation, say) it moves to input_required and surfaces the request. The client answers via tasks/updateno second connection and no unsolicited server-to-client messages required.

还有第五条,是设计上最容易被忽略但工程上最省事的一条:是否转任务由服务端逐请求决定。客户端只需要在能力里声明一次「我支持 Tasks」,然后准备好接收两种形状的结果即可——不需要给每个工具做预热,也不需要在每次请求上打标记。

There is a fifth property, easy to overlook in the design but the one that saves the most engineering effort: the server decides per request whether to create a task. The client opts in once through the extension capability and then handles whichever result shape arrives — no per-tool warmup, no per-request flag.

背景

Tasks 由 AWS 贡献,是 MCP 首批官方扩展之一。在 2026-07-28 规范中它从实验性核心正式移入 io.modelcontextprotocol/tasks 扩展(SEP-2663),同时引入基于轮询的 tasks/get 和新的 tasks/update

Background

Tasks was contributed by AWS and is one of MCP's first official extensions. In the 2026-07-28 specification it moved out of the experimental core and into the io.modelcontextprotocol/tasks extension (SEP-2663), together with a poll-based tasks/get and a new tasks/update.

02Tasks 是什么:六步机制What Tasks are: the six-step mechanism

Tasks 不是一套新的请求方法,而是对标准请求流程的一层扩展。当服务端判断某个请求会跑很久,它返回的不是最终结果,而是一个任务句柄;客户端随后轮询直到终态。整个过程分六步:

Tasks are not a new family of request methods; they are an extension of the standard request flow. When the server decides a request will be long-running, it returns a task handle instead of the final result, and the client polls until a terminal state. The whole process has six steps:

  1. 能力协商。客户端在每个请求的能力字段里带上 io.modelcontextprotocol/tasks;服务端在自己的 server/discover 能力里声明同一个扩展。
  2. 任务创建。服务端返回 CreateTaskResult(以 resultType: "task" 标识),内含 taskId、初始状态、TTL 和建议轮询间隔。任务必须在响应发出之前就已持久化创建。
  3. 轮询。客户端拿 taskIdtasks/get,响应携带当前状态;终态时携带最终结果或错误。
  4. 中途输入。任务转入 input_required 时,tasks/get 的响应里会带一个 inputRequests 映射(elicitation 或其它服务端请求),客户端通过 tasks/update 逐一回填。
  5. 完成。状态到 completed 时,result 字段里就是「如果同步执行本来会返回的东西」;状态是 failed 时,error 字段里是 JSON-RPC 错误。
  6. 取消。客户端随时可以发 tasks/cancel取消是协作式的——服务端确认收到意图,但没有义务真的停下来。
  1. Capability negotiation. The client includes io.modelcontextprotocol/tasks in its per-request capabilities; the server advertises the same extension in its own server/discover capabilities.
  2. Task creation. The server returns a CreateTaskResult (identified by resultType: "task") containing a taskId, an initial status, a TTL, and a suggested polling interval. The task must be durably created before the response is sent.
  3. Polling. The client calls tasks/get with the taskId. The response carries the current status and, for terminal states, the final result or error.
  4. Mid-flight input. If the task moves to input_required, the tasks/get response includes an inputRequests map with elicitations or other server requests. The client fulfills these via tasks/update.
  5. Completion. When the status reaches completed, the result field contains what the original request would have returned synchronously. If the status is failed, the error field carries the JSON-RPC error.
  6. Cancellation. The client can send tasks/cancel at any time. Cancellation is cooperative — the server acknowledges the intent but is not obligated to stop the work.
MCP 客户端 MCP 服务端 tools/call(带 tasks 能力声明) CreateTaskResult { taskId, status: working } 循环:按 pollIntervalMs 轮询,直到终态 tasks/get(taskId) Task { status: working } 服务端需要用户输入 Task { status: input_required, inputRequests } tasks/update(taskId, inputResponses) ack(继续轮询…) Task { status: completed, result }
图 1 · Tasks 的完整交互时序:创建句柄 → 轮询 → 中途回填输入 → 终态取结果
MCP Client MCP Server tools/call (with tasks capability) CreateTaskResult { taskId, status: working } loop: poll at pollIntervalMs until terminal tasks/get (taskId) Task { status: working } server needs user input Task { status: input_required, inputRequests } tasks/update (taskId, inputResponses) ack (keep polling…) Task { status: completed, result }
Figure 1 · The full Tasks exchange: mint a handle → poll → answer mid-flight input → collect the terminal result

来源:MCP Tasks · How Tasks work

Source: MCP Tasks · How Tasks work

03任务生命周期与状态机Task lifecycle and state machine

五个状态,三个是终态。终态的定义很硬:一旦到达,任务状态就不再改变。这条约束是客户端能安全停止轮询、安全落盘归档的基础。

Five statuses, three of them terminal. The definition of terminal is strict: once reached, the task's state does not change. That constraint is what lets a client safely stop polling and safely archive the outcome.

状态含义终态
working操作正在进行中。
input_required服务端需要客户端提供输入才能继续,详见 inputRequests
completed操作完成,result 字段是最终输出。
failed执行过程中发生 JSON-RPC 错误,error 字段含细节。
cancelled操作被取消(不保证一定被采纳)。
StatusMeaningTerminal
workingThe operation is in progress.No
input_requiredThe server needs client input before continuing. See inputRequests.No
completedThe operation finished. The result field contains the final output.Yes
failedA JSON-RPC error occurred during execution. The error field has details.Yes
cancelledThe operation was cancelled (not always honored).Yes
请求到达 tools/call working 正在执行 input_required 等待客户端回填 completed result 里是最终输出 failed error 里是 JSON-RPC 错误 cancelled 协作式,不保证生效 需要输入 tasks/update tasks/cancel 客户端在非终态时按 pollIntervalMs 调 tasks/get(taskId)
图 2 · 任务状态机:三个终态一旦到达即冻结,input_required 是唯一会「退回」working 的中间态
request in tools/call working in progress input_required waiting on client completed final output in result failed JSON-RPC error in error cancelled cooperative, not guaranteed needs input tasks/update tasks/cancel while non-terminal, the client calls tasks/get (taskId)
Figure 2 · The task state machine: the three terminal states freeze on arrival; input_required is the only intermediate state that hands control back to working
容易读错的一点

cancelled 是终态,但「发了 tasks/cancel」不等于「任务会变成 cancelled」。取消是协作式的:服务端确认收到,但任务完全可能继续跑完并落到 completed。客户端逻辑必须能接受「我取消了,结果还是回来了」。

Easy to misread

cancelled is terminal, but "I sent tasks/cancel" does not mean "the task will become cancelled". Cancellation is cooperative: the server acknowledges it, yet the task may well run to completion and land on completed. Client logic has to tolerate "I cancelled and the result came back anyway".

04线上长什么样:实际报文On the wire: actual messages

先看客户端怎么声明支持。注意它放在 每个请求的 _meta 里,不是一次性握手——这是 2026-07-28 无状态核心带来的直接后果:initialize/initialized 握手和 Mcp-Session-Id 头都已被移除,每个请求必须自描述

Start with how the client declares support. Note that it lives in each request's _meta, not in a one-time handshake — a direct consequence of the 2026-07-28 stateless core: the initialize/initialized exchange and the Mcp-Session-Id header are both gone, so every request must be self-describing.

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "run_ci_pipeline",
    "arguments": { "branch": "main" },
    "_meta": {
      "io.modelcontextprotocol/clientCapabilities": {
        "extensions": {
          "io.modelcontextprotocol/tasks": {}
        }
      }
    }
  }
}
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "run_ci_pipeline",
    "arguments": { "branch": "main" },
    "_meta": {
      "io.modelcontextprotocol/clientCapabilities": {
        "extensions": {
          "io.modelcontextprotocol/tasks": {}
        }
      }
    }
  }
}

服务端在 server/discover 的能力里声明同一个扩展:

The server advertises the same extension in its server/discover capabilities:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "capabilities": {
      "extensions": {
        "io.modelcontextprotocol/tasks": {}
      }
    }
  }
}
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "capabilities": {
      "extensions": {
        "io.modelcontextprotocol/tasks": {}
      }
    }
  }
}

于是同一个 tools/call 可能回来两种形状之一。这是客户端最需要适配的地方——结果是多态的:

As a result, the same tools/call can come back in one of two shapes. This is the main thing a client has to adapt to — results are polymorphic:

形状 A:普通同步结果

就是你一直在处理的那个 tools/call 结果,没有 resultType: "task"

Shape A: an ordinary synchronous result

The tools/call result you have always handled, with no resultType: "task".

形状 B:CreateTaskResult

resultType: "task",内含 taskIdstatusttlMspollIntervalMs

Shape B: a CreateTaskResult

resultType: "task" with a taskId, a status, a ttlMs, and a pollIntervalMs.

轮询与回填的骨架代码(伪代码,任何语言 SDK 思路一致):

The skeleton of polling and answering (pseudocode; the shape is the same in every SDK):

result = call_tool("run_ci_pipeline", {branch: "main"})

if result.resultType != "task":
    return result                      # 形状 A:直接就是答案

task_id = result.taskId
persist(task_id)                       # 关键:落盘,进程挂了还能续
interval = result.pollIntervalMs

while True:
    task = rpc("tasks/get", {taskId: task_id})

    if task.status == "input_required":
        answers = {}
        for key, req in task.inputRequests.items():
            answers[key] = ask_user_or_model(req)
        rpc("tasks/update", {taskId: task_id, inputResponses: answers})
        continue                       # 回填后继续轮询

    if task.status == "completed":  return task.result
    if task.status == "failed":     raise TaskError(task.error)
    if task.status == "cancelled":  return None

    sleep(interval)                    # 尊重服务端建议的间隔
result = call_tool("run_ci_pipeline", {branch: "main"})

if result.resultType != "task":
    return result                      # Shape A: this is already the answer

task_id = result.taskId
persist(task_id)                       # Key step: durable, so a crash can resume
interval = result.pollIntervalMs

while True:
    task = rpc("tasks/get", {taskId: task_id})

    if task.status == "input_required":
        answers = {}
        for key, req in task.inputRequests.items():
            answers[key] = ask_user_or_model(req)
        rpc("tasks/update", {taskId: task_id, inputResponses: answers})
        continue                       # keep polling after answering

    if task.status == "completed":  return task.result
    if task.status == "failed":     raise TaskError(task.error)
    if task.status == "cancelled":  return None

    sleep(interval)                    # respect the server's suggested interval

来源:MCP Tasks · Implementation guide · 2026-07-28 规范:无握手无会话

Sources: MCP Tasks · Implementation guide · The 2026-07-28 Specification: no handshake or sessions

05怎么实现:客户端与服务端清单How to implement: client and server checklists

客户端五步

Five steps for a client

  1. 声明支持——把扩展写进每个请求的 _meta 能力字段。
  2. 处理多态结果——发 tools/call 这类支持的请求时,准备好接收标准结果或 CreateTaskResult
  3. 轮询到终态——用 tasks/get,尊重 pollIntervalMs,直到 completed / failed / cancelled
  4. 处理输入请求——状态是 input_required 时读 inputRequests,把请求呈现给用户或模型,用 tasks/update 提交回应。
  5. 持久化 task ID——落盘保存,这样客户端崩溃或重启后能恢复轮询。这一步是 Tasks 全部价值的兑现点,漏了等于白做。
  1. Declare support — put the extension in the capabilities _meta of every request.
  2. Handle polymorphic results — when issuing a supported request such as tools/call, be ready for either the standard result or a CreateTaskResult.
  3. Poll to a terminal state — call tasks/get, respect pollIntervalMs, and continue until completed / failed / cancelled.
  4. Handle input requests — on input_required, read inputRequests, present them to the user or the model, and submit answers via tasks/update.
  5. Persist task IDs — store them durably so polling can resume after a client crash or restart. This is where the whole value of Tasks is cashed in; skip it and you have gained nothing.

服务端六步

Six steps for a server

  1. 声明支持——把扩展写进 server/discover 的 capabilities。
  2. 先检查客户端能力——返回 CreateTaskResult 之前,必须确认客户端在本次请求里声明了该扩展。永远不要给没声明支持的客户端返回任务。
  3. 返回 CreateTaskResult——resultType: "task",带唯一 taskId、初始状态、ttlMspollIntervalMs任务必须在响应发出之前完成持久化创建。
  4. 实现 tasks/get——每次轮询返回当前状态;终态时带上 result(completed)或 error(failed)。
  5. 实现 tasks/update——接受与未决 inputRequests 对应的 inputResponses,用空结果确认;对未知的或已满足的 key 直接忽略。
  6. 实现 tasks/cancel——用空结果确认。尽量执行,但要记住取消是协作式的,任务仍可能落到非 cancelled 的终态。
  1. Advertise support — include the extension in your server/discover capabilities.
  2. Check client capabilities first — before returning a CreateTaskResult, verify that the client declared the extension on this request. Never return a task to a client that did not declare support.
  3. Return a CreateTaskResultresultType: "task" with a unique taskId, an initial status, ttlMs, and pollIntervalMs. The task must be durably created before the response is sent.
  4. Serve tasks/get — return the current state on each poll; for terminal states include result (on completed) or error (on failed).
  5. Handle tasks/update — accept inputResponses keyed to outstanding inputRequests, acknowledge with an empty result, and ignore responses for unknown or already-satisfied keys.
  6. Handle tasks/cancel — acknowledge with an empty result. Honor it when possible, but remember cancellation is cooperative and the task may still reach a non-cancelled terminal status.
省一次往返:通知

服务端可以通过 notifications/tasks 推送状态更新,客户端通过 subscriptions/listen 机制按通知类型逐个订阅。每条通知携带完整任务状态,因此省掉一次额外的 tasks/get 往返。但要记住:轮询是默认路径,通知是可选优化——只有当服务端支持时,客户端才能依赖它代替轮询。

Saving a round-trip: notifications

Servers can push status updates via notifications/tasks, which clients opt into per notification type through the subscriptions/listen mechanism. Each notification carries the full task state, eliminating the extra tasks/get round-trip. Keep in mind, though: polling is the default. Notifications are an optional optimization — a client can rely on them instead of polling only if the server supports them.

来源:MCP Tasks · For MCP clients / For MCP servers / Notifications

Sources: MCP Tasks · For MCP clients / For MCP servers / Notifications

06Tasks / MRTR / subscriptions 怎么选Choosing between Tasks, MRTR, subscriptions

2026-07-28 一次性引入了三套「不是普通一问一答」的机制,很容易搞混。它们解决的是三个不同的问题:

The 2026-07-28 release introduced three mechanisms that go beyond plain request-and-response, and they are easy to confuse. They solve three different problems:

机制解决什么关键形状什么时候用
Tasks
扩展
操作时间太长,不能占着连接等 返回 resultType: "task" + taskId,之后走 tasks/get CI、批处理、审批、外部 job 系统、弱网客户端
MRTR
核心规范
工具执行中途缺一个输入,但整体很快 返回 resultType: "input_required",客户端带着答案重试原调用 删数据前确认、创建付费资源前确认、补一个缺失参数
subscriptions/listen
核心规范
客户端不想一直主动拉 单一订阅流,按通知类型 opt-in 已有长任务想省掉轮询开销;资源变更通知
MechanismWhat it solvesKey shapeWhen to use it
Tasks
extension
The operation takes too long to hold a connection for Returns resultType: "task" + a taskId, then tasks/get CI, batch jobs, approvals, external job systems, flaky clients
MRTR
core spec
A tool is missing one input mid-call but is otherwise fast Returns resultType: "input_required"; the client retries the original call with the answers Confirm before deleting data, confirm before creating a billable resource, supply a missing parameter
subscriptions/listen
core spec
The client would rather not keep pulling A single subscription stream, opted into per notification type An existing long task where you want to drop the polling overhead; resource change notifications
一次工具调用没法立刻返回 因为缺一个输入 因为活本身就久 MRTR input_required → 带答案重试原调用 Tasks 拿到 taskId,轮询到终态 轮询太贵? subscriptions/listen 订阅 notifications/tasks 推送 两者可以叠加 一个长任务跑到一半需要审批时,任务转入 input_required,用 tasks/update 回填,不用 MRTR 重试。
图 3 · 选择路径:缺输入走 MRTR,活久走 Tasks;两者叠加时由 Tasks 的 input_required + tasks/update 承接
A tool call cannot return immediately because an input is missing because the work is long MRTR input_required → retry the call with answers Tasks take the taskId, poll to a terminal state polling too costly? subscriptions/listen subscribe to notifications/tasks The two compose When a long task needs an approval halfway through, it moves to input_required and you answer via tasks/update.
Figure 3 · The path: missing input goes to MRTR, long work goes to Tasks; when both apply, Tasks' input_required + tasks/update carries the interaction

这里有个容易混淆的细节值得单独点出:input_required 这个词在两处出现,含义相近但机制不同。在 MRTR 里它是 resultType,客户端的动作是带着 inputResponses 重试原始调用;在 Tasks 里它是 status,客户端的动作是tasks/update 回填,原始调用不重发。判断依据很简单:你手上有没有 taskId

One detail is worth calling out because it trips people up: the phrase input_required appears in two places with related meanings but different mechanics. Under MRTR it is a resultType, and the client's move is to retry the original call with inputResponses attached. Under Tasks it is a status, and the client's move is to call tasks/update — the original call is not re-sent. The test is simple: do you hold a taskId?

来源:2026-07-28 规范:MRTR / Tasks · subscriptions 模式

Sources: The 2026-07-28 Specification: MRTR / Tasks · Subscriptions pattern

07什么场景该用 TasksWhen to reach for Tasks

官方文档给了五类场景。它们的共同点不是「慢」,而是「结果的到达时间与请求的发出时间解耦」:

The official docs list five scenarios. What they share is not slowness but the decoupling of when a result arrives from when the request was made:

长时运行的操作

CI 流水线、批量数据处理、模型训练任务——分钟级到小时级。

Long-running operations

CI pipelines, batch data processing, or model training jobs that take minutes or hours.

人在环路中

审批门、复核步骤,或任何需要暂停等用户确认的操作——任务转入 input_required,客户端把请求呈现出来。

Human-in-the-loop

Approval gates, review steps, or any operation that pauses for user confirmation. The task moves to input_required and the client presents the request.

外部作业系统

如果你的 server 包着一个本来就用 job ID 的 API(云部署、异步 API、队列),那就在创建 job 时返回 task,job 完成时解析它。这是改造成本最低的一类。

External job systems

If your server wraps an API that already uses job IDs (cloud deployments, async APIs, queued work), return a task when you create the job and resolve it when the job completes. This is the cheapest kind to retrofit.

不可靠连接

移动端、断续网络,或任何连接会掉的环境——task ID 能挺过断线

Unreliable connections

Mobile clients, intermittent networks, or environments where connections drop — task IDs survive disconnects.

批处理

处理大量条目的操作(批量导入、批量更新),部分进度本身就有意义,状态消息可以汇报进度。

Batch processing

Operations that process many items (bulk imports, mass updates) where partial progress is meaningful. Status messages report progress.

反过来,不该用 Tasks 的情形也很清楚:如果操作在一两秒内稳定返回,直接同步返回就好——多引入的持久化、轮询和状态管理都是净成本。Tasks 是给「不得不异步」的场景准备的,不是默认选项。

Conversely, when not to use Tasks is equally clear: if the operation reliably returns within a second or two, just return synchronously. The extra persistence, polling, and state management are pure overhead. Tasks exist for work that has no choice but to be asynchronous; they are not a default.

来源:MCP Tasks · When to use Tasks

Source: MCP Tasks · When to use Tasks

08六个常见坑Six common pitfalls

坑一 · 响应发出后才创建任务

规范要求任务在响应发出之前就已持久化创建。反过来做的话,会出现一个致命窗口:客户端拿到 taskId 立刻发 tasks/get,而服务端还没写库,返回「任务不存在」。分布式部署下这个窗口尤其明显。

Pitfall 1 · Creating the task after sending the response

The spec requires the task to be durably created before the response is sent. Do it the other way around and you open a fatal window: the client receives the taskId, immediately issues tasks/get, and the server — which has not written to storage yet — answers "no such task". The window is especially wide in a distributed deployment.

坑二 · 给没声明支持的客户端返回任务

服务端必须先检查客户端在本次请求的能力里带了 io.modelcontextprotocol/tasks。老客户端拿到 resultType: "task" 只会当成一个看不懂的结果,把 taskId 当答案塞进模型上下文——比直接报错更难排查。

Pitfall 2 · Returning a task to a client that never opted in

The server must first verify that the client included io.modelcontextprotocol/tasks in this request's capabilities. An older client that receives resultType: "task" will simply treat it as an unintelligible result and feed the taskId to the model as if it were the answer — far harder to debug than a clean error.

坑三 · 忽略 pollIntervalMs

pollIntervalMs 是服务端建议的轮询间隔,不是装饰。用固定 100ms 死循环轮询,一个跑两小时的任务能给服务端打出 7 万次请求。反过来间隔过长又让「完成」延迟暴露给用户。照服务端说的做。

Pitfall 3 · Ignoring pollIntervalMs

pollIntervalMs is the server's suggested interval, not decoration. A hard loop at a fixed 100 ms will send roughly 70,000 requests to the server over a two-hour task. Poll too slowly, on the other hand, and completion reaches the user late. Do what the server asks.

坑四 · 只在内存里存 task ID

Tasks 的核心卖点就是崩溃可恢复,而这个卖点完全依赖客户端把 task ID 落盘。存在进程内存里,进程一挂,任务变成孤儿——服务端还在烧钱跑,没人来收结果。

Pitfall 4 · Keeping task IDs only in memory

Crash resilience is the headline benefit of Tasks, and it depends entirely on the client persisting task IDs. Keep them in process memory and a crash orphans the task — the server keeps burning money on work nobody will ever collect.

坑五 · 忘了 TTL

CreateTaskResult 里带 ttlMs。任务不是永久的,过期后服务端可以清理。恢复轮询前先看看句柄是不是已经过了 TTL,别对着一个早被 GC 的 ID 重试到天荒地老。

Pitfall 5 · Forgetting the TTL

CreateTaskResult carries a ttlMs. Tasks are not permanent; a server may reclaim them after expiry. Check whether a handle is past its TTL before resuming polling, instead of retrying forever against an ID that was garbage-collected long ago.

坑六 · 假设取消一定生效

见第 03 节:tasks/cancel 只是表达意图。UI 上写「已取消」而不等终态确认,会和最终落到 completed 的现实打架。正确做法是显示「取消中」,以终态为准。

Pitfall 6 · Assuming cancellation takes effect

See section 03: tasks/cancel only expresses intent. Rendering "cancelled" in the UI without waiting for a terminal state will contradict reality when the task lands on completed. Show "cancelling" and let the terminal state decide.

关于本节的说明:坑一、二、五、六直接对应官方文档中的规范性要求;坑三、四是从文档明确写出的「尊重 pollIntervalMs」「持久化 task ID」两条实现要求推演出的常见后果,属于工程推断而非规范原文。

A note on this section: pitfalls 1, 2, 5, and 6 map directly to normative requirements in the official docs. Pitfalls 3 and 4 are engineering inferences drawn from two implementation requirements the docs state explicitly — "respect pollIntervalMs" and "persist task IDs" — rather than verbatim spec text.

09它在路线图上的位置Where it sits on the roadmap

2026 年 8 月 22 日,MCP 核心维护者 David Soria Parra 与 Den Delimarsky 发布了新版路线图,分为五个优先领域,「智能体消息原语」排在第一位。原文对这个方向的定位是:现代智能体负载已经不再适配标准的请求-响应模式——循环会跑更久、服务端会推流式结果、还需要在执行中途调整方向。

On 22 August 2026, MCP lead maintainers David Soria Parra and Den Delimarsky published a new roadmap organized into five priority areas, with agentic messaging primitives listed first. The stated framing: modern agentic workloads no longer fit the standard request-and-response pattern — loops run longer, servers push streamed results, and there is a clear need to steer work mid-flight.

这个方向下有三件具体的事,Tasks 占了其中一件:

Three concrete pieces of work sit under that area, and Tasks is one of them:

  1. 服务端主动事件——webhooks 与 channels,「让客户端不必再为结果一直轮询」。
  2. 组合性评审——跨 Agents、Transports、Triggers & Events 三个工作组,确认这些原语彼此配合得好,而不只是各自可用。
  3. Tasks 扩展成熟化(SEP-2663)——目标是让它能进入规范正文
  1. Server-initiated events — webhooks and channels, "so clients aren't left polling for results".
  2. A composition review across the Agents, Transports, and Triggers & Events Working Groups, to confirm these primitives work well together rather than merely working individually.
  3. Maturing the Tasks extension (SEP-2663) — the goal being to move it into the specification.
对你的实际影响

路线图明确写了:落在这五个优先领域里的 SEP 获得加速评审、通过概率最高;领域之外的提案不会被自动拒绝,但维护者的评审时间稀缺,会优先给这些方向。所以如果你正在做 MCP server 的异步能力,现在按 Tasks 扩展实现,是与规范演进方向一致的押注,而不是在赌一个可能被废弃的实验特性。

What this means for you

The roadmap says it plainly: SEPs that fall within these five priority areas get expedited review and have the best chance of acceptance. Proposals outside them are not rejected automatically, but maintainer review time is scarce and goes to these areas first. So if you are building asynchronous capability into an MCP server, implementing against the Tasks extension today is a bet aligned with where the spec is going, not a gamble on an experimental feature that might be dropped.

值得留意的一个张力:Tasks 目前是轮询优先的设计(通知是可选优化),而路线图第一条就是要做服务端主动推送。两者不冲突——通知机制早已经通过 subscriptions/listen 存在,webhooks/channels 是把「推」的能力扩展到「客户端不在线」的场景。但如果你现在就把架构死绑在轮询上,未来接推送时要动的地方会比预期多。建议把「怎么拿到状态」抽象成一层,轮询只是它的第一个实现。

One tension worth watching: Tasks today is poll-first by design, with notifications as an optional optimization, while the first item on the roadmap is server-initiated push. These do not conflict — a notification path already exists through subscriptions/listen, and webhooks/channels extend push to the case where the client is offline. But if you hard-wire your architecture to polling now, wiring in push later will touch more code than you expect. Abstract "how status arrives" behind one seam, and treat polling as its first implementation.

来源:The New MCP Roadmap · 2026-08-22 · 官方路线图页

Sources: The New MCP Roadmap · 2026-08-22 · Official roadmap page

10来源清单Sources

本文全部事实性内容来自以下公开来源,均于 2026-08-26 访问。文中凡属工程推断而非规范原文之处,已在对应位置标注。

Every factual claim in this document comes from the public sources below, all accessed on 2026-08-26. Wherever a statement is an engineering inference rather than verbatim spec text, it is flagged in place.

  1. MCP Tasks — Asynchronous task execution for long-running MCP operations(官方扩展文档:机制、状态表、实现指南、通知)
  2. modelcontextprotocol/ext-tasks(Tasks 完整规范与文档仓库)
  3. The New MCP Roadmap · 2026-08-22 · David Soria Parra、Den Delimarsky(五大优先领域;Tasks 转正目标)
  4. The 2026-07-28 Specification · 2026-07-28(无状态核心、MRTR、Tasks 移入扩展、AWS 贡献、SDK 状态)
  5. MCP 规范 2026-07-28完整变更日志
  6. SEP-2663 — Tasks extension · SEP-2322 — Multi Round-Trip Requests · SEP-2575 — Stateless MCP
  7. subscriptions/listen 模式 · 扩展客户端支持矩阵
  8. MCP 官方路线图页
  1. MCP Tasks — Asynchronous task execution for long-running MCP operations (official extension docs: mechanism, status table, implementation guide, notifications)
  2. modelcontextprotocol/ext-tasks (full specification and documentation repository for Tasks)
  3. The New MCP Roadmap · 2026-08-22 · David Soria Parra and Den Delimarsky (five priority areas; the goal of moving Tasks into the spec)
  4. The 2026-07-28 Specification · 2026-07-28 (stateless core, MRTR, Tasks moving into an extension, the AWS contribution, SDK status)
  5. MCP specification 2026-07-28 and its full changelog
  6. SEP-2663 — Tasks extension · SEP-2322 — Multi Round-Trip Requests · SEP-2575 — Stateless MCP
  7. The subscriptions/listen pattern · Extension client support matrix
  8. The official MCP roadmap page
免责声明

本文为学习整理,不是官方文档。Tasks 目前是扩展而非规范正文,客户端支持情况因宿主而异——落地前请查客户端支持矩阵并以 ext-tasks 仓库中的规范原文为准。规范仍在演进中,本文内容对应 2026-08-26 的状态。

Disclaimer

This is a study write-up, not official documentation. Tasks is currently an extension rather than part of the specification proper, and client support varies by host — check the client matrix before shipping, and treat the spec in the ext-tasks repository as authoritative. The specification is still evolving; this document reflects its state as of 2026-08-26.