KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA
06. MCP Tasks 扩展和异步工具怎样接回 Harness? — keel 龙骨
普通 tool calling 假设调用在一次模型回合内返回;报表导出、批量扫描和人工审批则可能运行数分钟甚至数小时。此时工具调用必须变成可轮询、可取消、可恢复的任务,而不是让模型一直占着 context 等待。
普通 tool calling 假设调用在一次模型回合内返回;报表导出、批量扫描和人工审批则可能运行数分钟甚至数小时。此时工具调用必须变成可轮询、可取消、可恢复的任务,而不是让模型一直占着 context 等待。
适配器状态机
working -> input_required -> working
-> completed | failed | cancelled
按 2026-08-23 核对的 MCP Tasks 扩展,Server 可以为长请求返回 durable task ID,Client 通过 tasks/get 轮询、通过 tasks/update 补充输入,并以 cooperative tasks/cancel 请求取消。Client 和 Server 必须显式协商 io.modelcontextprotocol/tasks 扩展;Host 不能假设任意 MCP Server 或 Client 都支持它。
Harness 侧应把扩展状态映射为结构化 ToolResult 或 WakeupRequest:working/input_required 只产生等待或输入事件,completed 才能进入下一步,failed/cancelled 走明确错误策略。内部可以在收到 task ID 前记录 submitted,但它不是 Tasks 扩展定义的 task status。
本章边界
本章只研究“长任务状态怎样接回 Harness”,不等于完整 MCP 课程。它不展开现代无状态请求、server/discover、Tools/Resources/Prompts、stdio/Streamable HTTP、OAuth、MRTR、缓存、Apps、Registry 和旧版本兼容。完整缺口与课程顺序见仓库文档 docs/curriculum/tracks/AI-AGENT-DEVELOPMENT-ROADMAP.md。
轮询与取消
轮询器要有 deadline、指数退避、最大 attempt 和 lease;每次 poll 都应使用 task ID 幂等。取消请求要记录请求者和原因,不能把“发出了取消”伪装成“外部任务已停止”。Tasks 的取消是 cooperative:Server 确认收到意图,不保证底层工作立即停止。适配的外部系统若只支持 best-effort cancel,最终结果必须允许 unknown 并进入 reconciliation。
与 Context Budget 的关系
长任务等待期间不要把每次进度全文塞回 prompt。保存结构化进度、最近一次状态和结果引用,只有状态变化或下一步决策确实需要时才 JIT 读取详情。这样可以把 MCP task 的生命周期和 compaction 结合起来:事件历史保留事实,context 只保留当前工作集。
本章实践
在已有 Harness 项目中实现一个 adapter:
start()返回 task ID 和预计下一次 poll 时间;poll()将外部状态映射为结构化结果;cancel()写入取消请求并等待终态;- worker 重启后只凭 task ID 和事件历史继续;
- 对迟到的 completed 结果做版本/运行状态检查。
本章不要求安装 MCP SDK;先把异步状态映射和测试做对,再在完整 MCP 课程中接入真实传输、能力协商和 SDK。
本章检查点
- 为什么
working不是工具失败,也不是模型可以忽略的空结果? - poll 重试与业务 effect 的幂等键是否相同?为什么?
- 外部任务永不返回时,哪个组件负责最终超时和告警?