MCP 持久化 Job Manager
解释为什么长任务不应绑定单次 MCP RPC,以及如何用持久化 Job 状态、日志和取消语义把任务生命周期与请求生命周期解耦。
[!info] related notes
- 所属 MOC: mcp-moc
- 前置概念:
- 并列概念:
- 易混淆概念:
- 关系笔记: mcp-in-agent-harness、backpressure
MCP 持久化 Job Manager
一句话定义
MCP 持久化 Job Manager 是一种把“启动长任务”和“等待长任务完成”拆开的执行模式:MCP 工具调用只负责快速创建、查询、读取或取消 Job,真正的构建、测试、Agent 运行等长任务由独立进程继续执行。
核心不变量是:
MCP request lifecycle ≠ task lifecycle
网络请求可以结束,任务仍然继续;下一次 MCP 调用通过 job_id 恢复观察和控制。
为什么同步 run_command 不适合长任务
最朴素的远程 shell MCP 往往这样工作:
ChatGPT
→ tools/call(run_command)
→ tunnel
→ stdio MCP
→ shell process
→ 等 shell 完成
→ 返回 stdout/stderr
如果命令需要几秒,这很简单;如果需要几分钟,请求会同时占住多层生命周期:
- ChatGPT / hosted orchestrator 的工具等待;
- Secure MCP Tunnel 的 command response budget;
- stdio request;
- shell timeout;
- stdout/stderr 缓冲。
任意一层先过期,都会出现“远端任务可能还在跑,但本次 RPC 已经失效”的状态。继续单纯提高 timeout,只会把一次请求变得更长,并不能消除跨层 deadline 不一致。
Job 模型
一个最小、稳定的工具面通常包括:
| 工具 | 作用 |
|---|---|
job_start | 创建任务,快速返回 job_id |
job_status | 查询 running / completed / failed / cancelled |
job_output | 分页读取 stdout/stderr 或统一日志 |
job_cancel | 终止任务及其进程组 |
job_list | 查看近期或当前任务 |
典型流程:
job_start("pnpm test")
↓
job_20261001_xxx
MCP RPC 结束
↓
任务继续执行
job_status(job_id)
↓
running
job_output(job_id, offset=0)
↓
返回一段日志 + next_offset
job_status(job_id)
↓
completed, exit_code=0
Job 需要持久化什么
至少应保存:
job_id
command / command 摘要
cwd
created_at
started_at
finished_at
status
pid / process group
exit_code
stdout/stderr 日志位置
cancelled_at
“持久化”不一定意味着数据库。对单机开发 Agent,一个类似下面的目录已经足够:
~/.local/state/<agent>/jobs/
<job-id>/
meta.json
output.log
exit-code
关键不是存储技术,而是Job 状态不能只存在于发起那次 RPC 的内存栈里。
为什么要使用独立进程组
如果长任务通过 shell 又拉起子进程:
shell
├─ pnpm
│ ├─ node
│ └─ worker
└─ test runner
只 kill 最外层 PID 可能留下孤儿进程。因此 Job Manager 应创建独立 process group,并在取消或超时时终止整个组。
这使 job_cancel 的语义接近:
取消这次执行,而不是只杀掉一个父 shell。
日志为什么要分页读取
如果 Job 已经跑了十分钟,把完整日志一次性返回会重新制造一个“大 RPC”。
因此 job_output 应提供:
offset
limit
next_offset
truncated / eof
这和文件分页读取是同一原则:长生命周期和大数据量都不应绑在单次 MCP response 上。
Job Manager 不解决什么
它不负责:
- 业务工作流编排;
- 跨机器分布式调度;
- exactly-once;
- 自动重试所有失败;
- Agent 的高层计划。
如果需要跨服务、跨主机、事务补偿、长期数小时/数天工作流,应该进一步考虑队列系统、CI runner、Temporal 等更完整的任务系统。
MCP Job Manager 的定位更窄:
给本地或单机远程 Agent 提供一个可靠的“长命令执行面”。
何时使用同步工具,何时 Job 化
经验规则:
- 几百毫秒到几秒:直接同步。
- 可确定在较短预算内结束:同步。
- 构建、全量测试、安装依赖、Coding Agent、未知耗时脚本:Job。
- 会持续产生日志:优先 Job。
- 用户希望稍后继续查看状态:Job。
具体秒数是运行环境的工程参数,不是 MCP 协议规范。当前 ChatGPT broad-agent 基线采用默认 20 秒、同步最大 30 秒,是为了给 Tunnel 与 hosted orchestration 留出足够余量。
学习检查
- 为什么“把
run_commandtimeout 从 60 秒调到 5 分钟”不能从根本上解决长任务问题? job_start为什么应该快速返回,而不是等待任务进入稳定阶段?- 为什么 Job 的日志仍然需要 pagination?
- 为什么取消长任务时要处理进程组,而不是只 kill 一个 PID?
- Job Manager 与 Temporal / Celery 这类工作流系统的边界在哪里?