《MCP Server 部署形态怎么选》里划过一条分界线:stdio 适合短平快的本地工具,HTTP 适合跨进程的共享服务。但有个问题没展开——不管哪种形态,工具一旦变成"长任务"(建索引、批量转码、跑数据迁移),一次请求一次响应的简单模型就不够用了。
模型发起一次调用,等三分钟没动静,它会怎么办?重试?放弃?用户盯着转圈?这一篇把长任务的三个必答题讲清楚:进度怎么报、怎么取消、断线之后怎么恢复。三个都做对,工具才能从"玩具"变成"生产可用"。
MCP 对进度有原生支持,机制是回调式:客户端发起请求时在 _meta 里带一个 progressToken,服务端在执行过程中用它发送 notifications/progress 通知。
服务端(TypeScript SDK)大致长这样:
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const token = request.params._meta?.progressToken;
const total = files.length;
for (let i = 0; i < total; i++) {
await processFile(files[i]);
if (token !== undefined) {
await server.notification({
method: "notifications/progress",
params: {
progressToken: token,
progress: i + 1,
total,
message: `已处理 ${files[i].name}`,
},
});
}
}
return { content: [{ type: "text", text: `全部 ${total} 个文件处理完成` }] };
});三个实践细节,都是踩出来的:
progress: 4392, total: 10000 不如 正在处理 4392/10000:reports_2024.pdf——这条消息大概率会被展示给最终用户。MCP 的取消是客户端发 notifications/cancelled,携带要取消的请求 ID。关键是理解它的语义:取消是协作式的。协议只是"捎话"说别干了,服务端代码里必须有对应的检查点,任务才会真的停下来。
const cancelled = new Set<string>(); // 记录已取消的请求 ID
server.setNotificationHandler(CancelledNotificationSchema, (n) => {
cancelled.add(n.params.requestId);
});
// 长任务循环里定期检查
for (let i = 0; i < total; i++) {
if (cancelled.has(requestId)) {
await cleanup(partialResults); // 清理半成品
throw new RequestCancelledError();
}
await processFile(files[i]);
}两个必做的配套:
这是最容易被忽略的一环。stdio 场景下连接断了进程就没了,任务状态自然丢失;HTTP 场景连接可以重连,但请求-响应的对应关系已经断了——客户端重连后拿着旧的请求 ID 等响应,是等不到的。
根治的思路是换模型:别把长任务建模成一次 RPC,建模成一个作业(job)。
start_indexing 立即返回 { jobId: "job_8f3a", status: "running" },任务在服务端后台跑;get_job_status(jobId),返回进度、阶段、错误信息;start_indexing,服务端发现幂等键相同且任务还在跑,直接返回现有 jobId,不会重复执行。这个模式把"断线恢复"从协议问题变成了应用问题:连接断了无所谓,作业的真相在服务端,客户端随时可以重新对齐状态。我们实测下来,这是唯一在移动网络环境下能稳定工作的方案。
不是所有工具都需要这三件事。判断标准就一条:工具执行时间会不会超过客户端的等待意愿。
把查询工具也套上作业模型是过度设计——多一次状态查询的往返,模型多一轮调用,没有任何收益。
MCP 长任务的三件事:进度用 progressToken + 节流;取消是协作式的,检查点和半成品清理都要自己写;断线恢复靠作业化 + 幂等键,别指望连接替你记住状态。本质是把 RPC 思维升级成作业系统思维——服务端持有真相,客户端随时对齐。
下一篇计划写 MCP 的 sampling 与 elicitation:服务端反过来向客户端/用户借能力(采样、追问)时的设计边界,有兴趣可以关注。
本文是 MCP 系列的一篇,前作:《MCP Server 部署形态怎么选》《MCP Server 上线之后:怎么知道模型都在拿你的工具干什么》《别让大模型陪你跑测试:MCP Server 的分层测试实践》。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。