关于 Task Checkpoint
Task Checkpoint 是面向长时间编码或文档任务的 Python MCP 服务。它记录工作区变更和步骤说明,便于在新会话续做或回退,且不会改动 Git 历史。
主要功能
- 01保存工作区变更和任务步骤
- 02在新会话恢复任务进度
- 03预览并还原较早状态
- 04导出和导入交接包
- 05跟踪多个挂起任务
适用场景
- 01中断后继续尚未完成的工作
- 02任务中撤销错误变更
- 03将任务交接到其他目录或机器
MCP 相关
stdio MCP 服务提供初始化、保存、恢复、查看、还原和转移检查点的工具。配套的 `SKILL.md` 会指导 Agent 在每一步完成后保存。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
10收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| goal | string | 否 | 任务目标 |
| name | string | 是 | 任务名 |
| root | string | 是 | 工作区根目录 |
| store | string | 否 | 存档目录;省略时用工作区内 .checkpoints |
| exclude | array | 否 | 额外的排除模式,按单个名字段匹配(目录名或路径第一段),如 ["config", "*.local"] |
| constraints | string | 否 | 约束 |
详细介绍
Task Checkpoint MCP
给长任务做"做一步、存一步"的存档:中断后新会话能接着做,做错了能退回任意一步。
一个 stdio MCP 服务器,只用 Python 标准库,不装任何第三方包。
快速开始 | 工具 | 客户端配置 | 使用限制 | English
快速开始
一、装
pipx install task-checkpoint-mcp
二、在客户端里接上(Claude Code、Codex、Cursor 等都是同一段配置)
{
"mcpServers": {
"task-checkpoint": { "command": "tc-mcp", "args": [] }
}
}
三、把 SKILL.md 装进技能目录,不装的话 Agent 不会主动存档
tc-mcp --install-skill ~/.claude/skills/task-checkpoint
SKILL.md 随包一起装(wheel 里的包数据),不需要仓库、也不需要联网。想看内容用 tc-mcp --print-skill,想知道它在哪用 tc-mcp --skill-path。
(技能目录按客户端而定,见下面"把 SKILL.md 装进 Agent 的技能目录"。)
四、交给 Agent 用。 一个任务就四步:
tc_init 开任务
tc_save 每完成一步存一次(带 conclusion 和 next)
tc_resume 新会话开头先调这个接上进度
tc_restore 退回去(先 apply:false 预览,再 apply:true)
五、不用记工具名,直接说人话。
用 task-checkpoint 开个任务,目标是把 user 模块拆成 service 和 repository 两层。每完成一步存一次档,带结论和下一步。
它会调 tc_init 建任务,之后每完成一步调一次 tc_save。中途换会话、或者隔天接着做,你说一句"接着上次的做",它调 tc_resume 就把进度拿回来了。
("每完成一步就存档"这条约束的可靠来源是 SKILL.md —— 装进技能目录之后它是硬规则;不装,就只能靠你每次提醒。)
中途被打断、隔天换个模型重开,新会话调一次 tc_resume 拿到的是这个:
任务:重构认证模块
目标:把 session 改成 JWT
当前步骤:3
下一步:把三个视图函数切到新入口,删旧函数
未落档:src/views/auth.py, src/views/user.py
handoff 字段就是这段文本,可以直接粘给新会话。它同时告诉你哪几个文件改了但还没落档。
为什么不直接用 git stash 或随手 commit 一下
| 做法 | 缺什么 |
|---|---|
git stash | 一次性的,不能命名、不能跨会话交接,也没有"这一步在做什么、结论是什么" |
| 随手一个 commit | 会污染真实历史;半成品未必允许 commit;还得你记得先 commit |
| 手写进度笔记 | 和文件状态脱钩,回退时要自己对着时间线拼 |
| 本工具 | 文件变化后台自动记(变更层),步骤语义由模型声明(任务层);只额外加 refs/checkpoints/...,不动你的 git 历史 |
值得装:多步、可能被中断、需要能退回的编码或文档任务。 不值得装:一次能做完、不需要回退也不需要交接的改动。
装之前先知道
- 需要 Python 3.10 或更新,只用标准库。
- 不碰你的 git 历史:不 commit、不 reset、不 clean、不动已有分支和 HEAD,只额外加
refs/checkpoints/...引用。 - 不是 git 仓库也能用,功能一样,只是少了那层 git 引用。
- 不保存聊天记录,只保存工作区文件和模型主动声明的步骤说明。
- 存档默认写在工作区的
.checkpoints/;指定外部store时,工作区仅留一个不含文件内容的.task-checkpoint-store.json路径指针。 - 敏感文件识别是路径黑名单,盖不全。 别把凭据放在被扫路径里 —— 详见"使用限制"。
安装
方式一:装成命令(推荐)
pipx install task-checkpoint-mcp
# 或
pip install task-checkpoint-mcp
也可以直接从 git 装,跟着仓库走、不走 PyPI 的版本号:
pipx install "git+https://github.com/qddfxp/task-checkpoint-mcp"
装完在客户端里用 tc-mcp 启动。包里有两个等价的入口点:tc-mcp 和 task-checkpoint-mcp(后者是给 uvx 这类按包名找命令的 runner 用的):
{
"mcpServers": {
"task-checkpoint": { "command": "tc-mcp", "args": [] }
}
}
方式二:直接用源码(不用安装)
git clone https://github.com/qddfxp/task-checkpoint-mcp
{
"mcpServers": {
"task-checkpoint": {
"command": "/absolute/path/to/python",
"args": ["/absolute/path/to/task-checkpoint-mcp/scripts/tc_mcp.py"]
}
}
}
command 要写解释器的绝对路径,不要写 python —— 客户端不一定能解析到你要的那个。装过之后也可以直接 python -m tc_mcp。
方式三:用 npx 启动
npm 上有一个同名的 task-checkpoint-mcp,但它是启动器,不是服务器本体:
npx -y task-checkpoint-mcp
它只做一件事:找到本机装了 tc_mcp 的 Python 解释器,然后把 stdio 透传给 python -m tc_mcp。所以本机仍然要先有 Python 包(方式一或方式二)。没有的话它会打印安装命令、以退出码 1 结束,而不是丢一堆 traceback 给你。
适合客户端只认 npm 式启动命令的情况:
{
"mcpServers": {
"task-checkpoint": { "command": "npx", "args": ["-y", "task-checkpoint-mcp"] }
}
}
npx -y task-checkpoint-mcp --version / --help 是启动器自己的开关,不需要本机有 Python。
配置文件放哪
上面那段 mcpServers JSON 要写进客户端的配置文件。各客户端的位置和顶层键:
| 客户端 | 配置文件 | 顶层键 |
|---|---|---|
| Claude Desktop | Windows %APPDATA%\Claude\claude_desktop_config.json;macOS ~/Library/Application Support/Claude/claude_desktop_config.json | mcpServers |
| Claude Code | 项目根目录 .mcp.json | mcpServers |
| Cursor | 全局 ~/.cursor/mcp.json;项目内 .cursor/mcp.json 优先 | mcpServers |
| Windsurf | ~/.codeium/windsurf/mcp_config.json | mcpServers |
| ZCode | ~/.zcode/cli/config.json | mcpServers |
| VS Code / Copilot | 项目内 .vscode/mcp.json;用户级 %APPDATA%\Code\User\mcp.json | servers |
| Codex CLI | ~/.codex/config.toml | TOML 的 [mcp_servers],不是 JSON |
两个容易踩的:
VS Code 的顶层键是 servers,不是 mcpServers。 直接抄上面的 JSON 不会生效,得改键名:
{
"servers": {
"task-checkpoint": { "command": "tc-mcp", "args": [] }
}
}
Codex 用 TOML。 同样一段配置要写成:
[mcp_servers.task-checkpoint]
command = "tc-mcp"
args = []
其余客户端以各自的文档为准。
怎么用
最重要的一条:文件变化由后台线程自动记录,但"这一步在做什么、结论是什么、下一步干什么"只有模型主动调 tc_save 才会留下。
所以要让 Agent 每完成一步就存一步 —— 这份约束写在 SKILL.md 里,得把它装进 Agent 的技能目录(见下面"把 SKILL.md 装进 Agent 的技能目录")。不装它,文件回退点照常产生,但没人知道当初要干什么、下一步该干什么。
四条铁律:
- 开长任务前先
tc_init,一个任务一个名。已有活动任务会被挂起而不是关闭,可以tc_switch切回去。 - 每完成一步就
tc_save,并且必须填conclusion和next。不填,新会话就只看得到一堆文件路径。 - 新会话、或中断后继续时,第一件事是
tc_resume,不要从零猜进度。 - 回退前先预览:先
apply: false看清单,确认了再apply: true。
一步的粒度:能独立说清"做完了、结论是 X"的单元。太细(每个文件一次)会淹没有效信息,太粗(整个任务一次)就失去了回退的意义。
工具
10 个。第一个参数都是 root,指工作区目录(通常是当前项目的绝对路径)。
| 工具 | 什么时候用 | 必填 |
|---|---|---|
tc_init | 长任务开工。已有活动任务会被挂起而不是关闭 | root, name |
tc_save | 每完成一步 | root, title |
tc_resume | 新会话开头、接手别人中断的工作(只读) | root |
tc_show | 想看有哪些步骤 / 变更记录 | root |
tc_restore | 退回某一步或某条变更记录。先预览再执行 | root,加 index 或 drift_id |
tc_capture | 想立刻记一次文件变化(不等后台线程) | root |
tc_switch | 回到之前挂起的任务 | root, task_id |
tc_export | 导出交接包给另一个目录 / 另一台机器 | root, to |
tc_import | 导入别人的交接包,在这里接续任务 | root, package |
tc_compress | 存档太大了,回收旧变更层空间 | root |
tc_save 的完整参数:
title(必填)——这一步一句话标题,写"做了什么",不写"改了什么"conclusion——这一步的结论。最有价值的字段next——下一步要干什么。接续工作的关键description——为什么这么做(决策理由,事后没人记得)verified——已验证项列表(跑了什么测试、确认了什么事实)open_questions——还没解决、留给后面的问题close: true——收尾时用。关闭后不能再存档,但还能读
回退操作本身也会被记成一条变更记录,返回里的 recovered 就是它的 drift_id —— 退错了可以再用它退回来。
返回值里几个值得看的字段
tc_resume.handoff—— 一段现成的交接文本,可以直接粘给新会话;drift_paths是工作区里还没落档的改动;health.errors是存档自身的问题tc_save.idempotent—— 同一步(标题、文件摘要、结论、下一步、已验证项都一样)重复存,不会产生重复步骤tc_save.active_task_changed—— 传了别人的task_id时活动任务会被切过去(原任务变 suspended),这个字段就是在提示这件事tc_restore.recovered—— 回退本身也会被记成一条变更记录,这就是它的drift_idtc_restore.plan.unrestorable/skipped_paths—— 目标里没能还原的文件;预览和执行给的是同一份清单tc_capture.waiting/partial—— 还在静默期,以及中间那条不完整的记录tc_init.exclude—— 回读你这次设进去的排除模式tc_export.path—— 交接包目录,里面有一个HANDOFF.md,是给接手方的提示词
把 SKILL.md 装进 Agent 的技能目录
SKILL.md 是这个项目的另一半功能,不是可选文档:MCP 服务器负责存取,SKILL.md 负责让模型每完成一步真的去调 tc_save。
复制到客户端扫描的技能目录,目录名即技能名:
| 客户端 | 放这里 |
|---|---|
| Claude Code | ~/.claude/skills/task-checkpoint/SKILL.md |
| Codex CLI | ~/.codex/skills/task-checkpoint/SKILL.md |
| ZCode | ~/.zcode/skills/task-checkpoint/SKILL.md |
| 跨客户端共用 | ~/.agents/skills/task-checkpoint/SKILL.md |
~/.agents/skills/ 是多个客户端共用的技能目录;客户端专属目录优先于它 —— 同名技能以客户端专属目录里的那份为准。OpenCode 等其它客户端放到它自己文档里的技能目录即可,规则不变。
tc-mcp --install-skill ~/.claude/skills/task-checkpoint
SKILL.md 是随包一起安装的(wheel 里的包数据),所以 pipx install 的用户不需要仓库,也不需要联网。目标已存在时会拒绝覆盖 —— 确实要覆盖加 --force;只想看内容用 tc-mcp --print-skill。
克隆了仓库的话,cp SKILL.md <技能目录>/ 效果一样。
Windows 路径形如 C:\Users\<你>\.claude\skills\task-checkpoint\SKILL.md。各客户端的技能目录可能不同,以它自己的文档为准;要求只有一条:文件落在技能目录下的 <技能名>/SKILL.md。重启客户端后生效。
自检(可选)
装完最快的一条,确认入口点活着、拿到版本号:
tc-mcp --version # 版本号
tc-mcp --help # 所有开关
下面几条都要先克隆仓库 —— pip 装出来的包里没有 tests/ 和 tools/。
在克隆下来的仓库里跑一遍回归测试:
python -m unittest discover -s tests -p "test_*.py" -v
最后一行是 OK 就对了。整套测试(100 多个用例)跑一次的时间完全看环境:
| 跑在哪 | 实测 |
|---|---|
| CI 的 ubuntu runner | 4.7 秒 |
| CI 的 windows runner | 39.8 秒 |
| 本机 Windows(E 盘) | 156~340 秒 |
同一套代码差了七十倍。开销集中在 git 子进程(refs/checkpoints/ 相关用例占大头)和静默期等待,对磁盘和杀毒的实时扫描极其敏感 —— 别把秒数当承诺,也别拿一个数字去对比另一台机器。
全部只用标准库,不需要 pytest;在 Python 3.10 上也能跑,只有读 pyproject.toml 的那几条会因为 tomllib 被跳过(3.11+ 全跑)。
仓库里还能跑真机端到端演练(起真 MCP 子进程、真杀进程、建约 190 MB 的重工作区、逐项核对"不动你 git"的承诺):
python tools/drill.py
它跑完默认把场地删掉;想看场地就 TC_DRILL_KEEP=1 python tools/drill.py(演练有未通过项时也会自动保留,方便排查)。
确认 MCP 服务器能起来 —— 会回一行带 serverInfo 的 JSON:
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18"}}' | python scripts/tc_mcp.py
从 pip 装的版本没有 scripts/,把上面那条改成 ... | tc-mcp(或 python -m tc_mcp)就行。
环境变量
| 变量 | 默认 | 说明 |
|---|---|---|
TC_STORE | <工作区>/.checkpoints | 存档目录 |
TC_WATCH | on | 设成 off 关掉后台自动记录 |
TC_WATCH_INTERVAL | 15 | 后台检查间隔(秒) |
TC_DRIFT_MAX_WAIT | 60 | 静默期上限(秒) |
TC_GIT | on | 设成 off 就完全不建 refs/checkpoints/... 引用 |
TC_GIT_VERIFY | on | 建引用前后对比 git 指纹,自证"没动过用户 git"。设成 off 跳过自证,每次 tc_save 少几趟 git 子进程;跳过后返回的 git.unchanged 是 null |
TC_SHA_REUSE | off | mtime+size 没变的文件不再重读、直接复用索引里的 sha。风险见"使用限制" |
使用限制
敏感文件:黑名单,盖不全
敏感文件不存内容、连哈希都不存,只能看出 mtime 和 size 变了没有。判定按路径匹配,所以两头都会漏:
- 漏判:名字无辜的凭据抓不到。
config/database.yaml、docker-compose.yml、config.json会被当普通文件明文存进payload/。别把凭据放在被扫路径里;避不开时用tc_init(exclude=[...])把整个目录排掉。 - 误拦:被误判为敏感的源码永远拿不到内容副本,回退时会静默跳过(只出现在
plan.unrestorable里)。名单分两层就是为压低误拦。
拦的范围:
- 目录名(整个目录都不存)
.ssh.aws.gnupg.docker.kube.env.envdir.secrets.tokens,或名字含secret/credential/token的目录。 - 无歧义文件名
- dotenv:
.env.env.**.env*.env.*.envrc - 私钥:
*.pem*.key*.p12*.pfx*.p8*.jks*.keystore*.kdbx*.ppk*.ovpn*_rsa*_dsa*_ed25519*_ecdsa*_key - 凭据:
.netrc.git-credentials.npmrc.pypirc.pgpass.htpasswd.my.cnfmy.cnfpasswdshadow - 云与基础设施:
kubeconfig*.kubeconfig*.tfstateterraform.tfvars*.tfvars*.jwt - 通配:
credentials*creds*secret*secrets.jsontokentoken.jsontoken.txt*.token*_tokenauth.json - 框架配置:
settings.pylocal_settings.pysettings.xmlwp-config.phpweb.configwrangler.toml.dev.vars
- dotenv:
- 高歧义子串,只在文件名以配置类后缀结尾时才拦
- 子串:
*secret**password**passwd**credential**api_key**apikey**token**auth_token* - 后缀:
.json .yaml .yml .toml .ini .cfg .conf .config .properties .xml .txt .env .envrc .cnf .sh .ps1 .bat .cmd .sql .php - 所以
prod_credentials.json拦;src/password_policy.py、src/tokenizer.py、docs/secrets.md不拦。
- 子串:
exclude 的匹配方式和内置名单一致:按单个名字段做 glob(目录名,或路径的第一段)。所以 exclude=["config"] 会排掉任意层级的 config/ 目录,但不能只排某个具体文件(要排 config/database.yaml 就得连 config/ 一起排)。带 / 的模式会被直接拒绝,不会静默失效。
这条能力不构成隐私保护或合规保证。 存档目录的访问权限由你自己管;别把未排除的凭据、令牌、私钥、个人资料放进被扫路径。
时间与粒度
- 静默期最长约 60 秒。文件一直在写时,中间那条记录标
partial: true。 - 进程突然死掉后、下次
tc_save/tc_capture之前,最后那段改动只在tc_resume的未落档路径里,还不是一条变更层记录。
变更层只看 mtime+size
tc_capture 和后台线程用 mtime+size 判断变化。内容改了但 mtime 和长度都没变时(cp -p、rsync -t、tar -x 会保留 mtime),它不会觉得文件变了。
任务层 tc_save 默认按内容算 sha,不受此限;但 TC_SHA_REUSE=on 会把任务层也拉到同一判据上 —— 见下面那条。
回退
- 会一并还原文件权限位(POSIX 可执行位),不还原 mtime —— 把旧时间戳写回去会让 make 类工具误以为文件没变。注意:只改权限、内容没变时不会触发重写(回退只重写内容不同的文件),那种情况下权限位也回不去。
- 不能还原的文件会被跳过(敏感文件、超过 100 MB 未存内容、payload 已丢),其余照退;跳过的列在返回的
plan.unrestorable里。预览和实际执行用同一份清单,所以一个.env不会让整次回退失效。 - 二进制能还原,但不能做行级 diff;不合并并发编辑。
大文件
不超过 100 MB 的文件原样按 sha 存进 payload/,相同内容只存一份;超过 100 MB 只记流式哈希和元数据,不保存内容,因而不能回退内容。
存档不会自动缩
旧变更层的内容会一直留着,用 tc_compress 手动回收。它的默认参数是保留 7 天,所以小工作区上默认调用往往是 converted: 0(什么都没回收)—— 返回值里带 keep_seconds / minimum / maximum / eligible / bytes_freed,看得出"为什么没回收"。想真的清就用小一点的 keep_seconds(步骤基线和 recovered 层永远不动)。
TC_SHA_REUSE=on 的风险(默认关的原因)
任务层默认每次都按内容算 sha,不信任 mtime,所以一次 save 会读完工作区里所有非敏感文件(不保留内容,内存占用与工作区大小无关)。
开了 TC_SHA_REUSE=on 之后,mtime+size 没变的文件不再重读、直接复用索引里的 sha。遇到 mtime 被还原(os.utime)、文件系统 mtime 粒度粗(FAT、部分网络盘)、或有工具保留 mtime(cp -p、rsync -t、tar -x、部分生成器)时,内容改动会被完全漏掉:不读盘、不存新内容,却把旧 sha 记进步骤,回退时静默给出旧内容。默认关闭时这条路径不存在。
tc_resume 和 tc_show 无论哪种模式都不读文件内容(只看 mtime+size),因此也可能漏掉"保留 mtime 的改动"。
参数与状态约束
tc_restore必须且只能传index或drift_id其中一个;apply=true不允许修改已关闭任务。tc_save传了task_id(而且不是当前活动任务)时,会先把活动任务切过去(原任务置为 suspended)再存这一步;返回值里的active_task_changed: true就是告诉你这件事发生了,想切回去用tc_switch。tc_save在已有基线且检测到未落档变化时,会先把该变化挂到本次 save 的 manifest 上,再写任务步骤。tc_export要求目标父目录已存在;tc_import会校验任务 id 和工作区路径,拒绝重复任务、..穿越与符号链接目标;导入失败会撤销已写入的目标文件。store不得等于工作区根目录;自定义store位于工作区内时会被扫描器自动排除。
git 相关
在 git 仓库里每次 tc_save 会多花几百毫秒(建引用的 git 子进程开销)。不想付这笔开销就 TC_GIT=off 或 TC_GIT_VERIFY=off。
实现备注
只有改这份代码的人才需要关心:
- 归档目录里的
.git/index字节数会被 git 自己刷新(status/ls-files会更新 stat cache),但 HEAD、分支、暂存内容都不会变 —— 自证就是拿status/ls-files的输出前后对比的。 refs/checkpoints/下已有同名引用时拒绝覆盖。tc_show/tc_resume只在读取状态快照时持有短锁;工作区扫描和文件读取不长期阻塞写入操作。- manifest 是增量链,每 20 份强制一个全量锚点。
存储
<工作区>/.checkpoints/
├── current.json 活动任务指针,按工作区 root 分键(同一个 store 可服务多个工作区)
├── state.lock 并发锁
├── server.log 日志(不会写到 stdout,stdout 只走 MCP 帧)
├── payload/<sha前两位>/ 内容寻址的文件副本(多个任务/步骤共享,按 sha 去重)
└── tasks/<task_id>/
├── state.json
├── index.json 基线索引:每个文件的 mtime/size/sha256
├── steps/sNNNN.json 任务层:每一步的说明
├── drifts/dNNNN.json 变更层:文件变化
└── manifest/mNNNN.json 一致性清单(增量链,每 20 份一个全量锚点)
活动指针和基线索引都按范围隔离:同一个 store 被几个工作区共用时,互不串味。
目录结构
.
├── README.md 本文件
├── SKILL.md 给 Agent 看的使用约束(功能的一部分,会随包一起安装)
├── LICENSE MIT
├── pyproject.toml 打包配置,源码不改成包目录
├── MANIFEST.in sdist 包含哪些文件
├── server.json 官方 MCP Registry 的发布元数据
├── glama.json Glama 目录的所有权标记
├── scripts/
│ ├── tc.py 业务核心
│ └── tc_mcp.py stdio MCP 适配层 + 给人用的命令行开关
├── tools/
│ └── drill.py 真机端到端演练探针(不进测试套件,手动跑)
├── npm/ npm 启动器包(只是壳,服务器本体不在这里)
├── tests/
│ ├── test_tc.py 完整验收测试(回退安全、并发锁、git ref、返回字段、打包契约)
│ ├── test_tc_regressions.py 针对已修缺陷的定向回归
│ └── test_packaging_consistency.py 三处版本号 / 注册表元数据 / README 所有权标记的一致性
└── .github/workflows/ci.yml CI:跑测试、构建、校验 server.json 与 wheel
构建产物不进仓库。发布走两条:PyPI 上的 task-checkpoint-mcp(pipx install task-checkpoint-mcp)是主渠道,wheel 和 sdist 同时挂在 GitHub Releases 上。
参与
欢迎提交 Issue 和 PR。动代码前先跑一遍测试(见上面“自检”,100 多个用例,本机 2.5~6 分钟)。
tests/test_tc.py是完整验收套件:回退安全、并发锁、git ref 可达性、每个分支的返回字段、打包契约tests/test_tc_regressions.py是已修缺陷的定向回归 —— 新修一个 bug 就往这里加一条,别只改代码
有一条硬约束:只用标准库。所以不接受任何新增依赖,测试依赖也不行(测试用 unittest,不用 pytest)。
许可证
MIT,见 LICENSE。
Task Checkpoint MCP(英文)
为长期 Agent 任务提供检查点和续接:逐步保存每一步,在新会话中继续工作,并回滚到任意较早步骤。
一个用纯 Python 标准库编写的 stdio MCP 服务——无需第三方包。Python 3.10+。
快速开始
1. 安装
pipx install task-checkpoint-mcp
2. 将其接入客户端(Claude Code、Codex、Cursor 等均使用同一配置块)
{
"mcpServers": {
"task-checkpoint": { "command": "tc-mcp", "args": [] }
}
}
**3. 将 SKILL.md 安装到技能目录。**没有它,Agent 不会自行创建检查点。
mkdir -p ~/.claude/skills/task-checkpoint
curl -fsSL -o ~/.claude/skills/task-checkpoint/SKILL.md \
https://raw.githubusercontent.com/qddfxp/task-checkpoint-mcp/main/SKILL.md
(不同客户端的技能目录有所不同——参见下文“将 SKILL.md 安装到 Agent 的技能目录”。)
**4. 让 Agent 使用它。**一个任务需要调用四次:
tc_init start a task
tc_save once per completed step (with conclusion and next)
tc_resume first call in a new session, to pick up where you left off
tc_restore roll back (apply:false to preview, then apply:true)
5. 不必记住工具名称——直接描述需求即可。
使用 task-checkpoint 启动任务:目标是将用户模块拆分为服务层和仓储层。每完成一步都创建检查点,并说明结论和下一步计划。
它会调用 tc_init 创建任务,并在每一步之后调用 tc_save。切换会话,或第二天再回来,说“从上次停下的地方继续”,它就会调用 tc_resume,接着推进进度。
(可靠的“每一步之后都创建检查点”要求来自 SKILL.md——安装到技能目录后,它就是硬性规则。没有它,你每次都得提醒 Agent。)
任务中途被打断,第二天在另一个模型上恢复——只需在新会话中调用一次 tc_resume,即可得到:
任务:重构认证模块
目标:把 session 改成 JWT
当前步骤:3
下一步:把三个视图函数切到新入口,删旧函数
未落档:src/views/auth.py, src/views/user.py
这段文本就是 handoff 字段,可直接粘贴到新会话中。它还会列出已更改但尚未记录的文件。
为什么不用 git stash,或者直接提交?
| 方式 | 缺少的功能 |
|---|---|
git stash | 一次性操作:没有名称,无法跨会话交接,也没有关于这一步做了什么或得出了什么结论的信息 |
| 快速提交 | 污染真实历史;未完成的工作不一定能提交;还得记得先提交 |
| 手写进度笔记 | 与文件状态脱节——回滚时需要手动对照时间线进行协调 |
| 此工具 | 后台线程记录文件变更(漂移层);模型声明步骤语义(任务层)。它只添加 refs/checkpoints/...,绝不触碰你的 git 历史 |
适合安装的场景:可能被打断、需要可逆操作的多步骤编码或写作任务。不适合的场景:可以一气呵成完成,且不需要回滚或交接的改动。
安装前须知
- Python 3.10 或更高版本,仅使用标准库。
- 不会改动你的 git 历史记录:不会执行 commit、reset、clean、branch 或 HEAD 更改。只会添加
refs/checkpoints/...引用。 - 也可在 git 仓库之外运行——功能相同,只是不包含引用层。
- 不会存储聊天记录——只存储工作区文件和模型声明的步骤说明。
- 存储位置默认为
<workspace>/.checkpoints/。使用外部store时,工作区只会保留.task-checkpoint-store.json路径指针,不包含任何文件内容。 - **敏感文件检测采用路径黑名单,无法覆盖所有情况。**请勿在扫描路径中放置凭据——参见限制。
安装
选项 1 — 从 PyPI 安装(推荐)
pipx install task-checkpoint-mcp
# or
pip install task-checkpoint-mcp
也可以直接从 git 安装,跟踪仓库并跳过 PyPI 版本管理:
pipx install "git+https://github.com/qddfxp/task-checkpoint-mcp"
然后将客户端指向 tc-mcp。此软件包提供两个功能相同的入口点——tc-mcp 和 task-checkpoint-mcp(后者适用于 uvx 等按软件包名称查找命令的运行器):
{
"mcpServers": {
"task-checkpoint": { "command": "tc-mcp", "args": [] }
}
}
选项 2 — 从源代码运行(无需安装)
git clone https://github.com/qddfxp/task-checkpoint-mcp
{
"mcpServers": {
"task-checkpoint": {
"command": "/absolute/path/to/python",
"args": ["/absolute/path/to/task-checkpoint-mcp/scripts/tc_mcp.py"]
}
}
}
command 必须是解释器的绝对路径,不能只写 python——客户端可能无法解析你想要使用的解释器。安装后也可以使用 python -m tc_mcp。
选项 3 — 通过 npx 启动
npm 上有一个同名软件包,但它是启动器,不是服务端:
npx -y task-checkpoint-mcp
它只负责查找一个可导入 tc_mcp 的 Python 解释器,并将 stdio 转交给 python -m tc_mcp。因此,你仍需先安装 Python 软件包(选项 1 或 2)。如果没有安装,启动器会打印安装命令并以状态码 1 退出,而不会输出一长串 traceback。
当客户端只支持 npm 风格的启动命令时,可以使用它:
{
"mcpServers": {
"task-checkpoint": { "command": "npx", "args": ["-y", "task-checkpoint-mcp"] }
}
}
npx -y task-checkpoint-mcp --version / --help 属于启动器,无需本地 Python。
配置文件的位置
mcpServers 块应放在客户端的配置文件中。以下是配置文件位置和顶层键:
| 客户端 | 配置文件 | 顶层键 |
|---|---|---|
| Claude Desktop | Windows %APPDATA%\Claude\claude_desktop_config.json;macOS ~/Library/Application Support/Claude/claude_desktop_config.json | mcpServers |
| Claude Code | 项目根目录中的 .mcp.json | mcpServers |
| Cursor | 全局配置 ~/.cursor/mcp.json;项目本地配置 .cursor/mcp.json 优先 | mcpServers |
| Windsurf | ~/.codeium/windsurf/mcp_config.json | mcpServers |
| ZCode | ~/.zcode/cli/config.json | mcpServers |
| VS Code / Copilot | 项目本地 .vscode/mcp.json;用户级 %APPDATA%\Code\User\mcp.json | servers |
| Codex CLI | ~/.codex/config.toml | TOML [mcp_servers],而非 JSON |
两个容易踩的坑:
**VS Code 使用的键是 servers,不是 mcpServers。**直接复制上面的 JSON 不会生效——请将键名改为:
{
"servers": {
"task-checkpoint": { "command": "tc-mcp", "args": [] }
}
}
**Codex 使用 TOML。**对应的条目如下:
[mcp_servers.task-checkpoint]
command = "tc-mcp"
args = []
对于其他客户端,请参阅其自身文档。
用法
最重要的一条规则:后台线程会自动记录文件更改,但“这一步做了什么、得出了什么结论、接下来要做什么”只有在模型调用 tc_save 后才会留下记录。
因此,Agent 必须在每完成一步后创建检查点。这项约定写在 SKILL.md 中,你需要将它安装到 Agent 的技能目录(见下文)。没有它,你仍能获得文件回滚点,但没人知道你在做什么或接下来要做什么。
四条规则:
- 开始长任务前调用
tc_init,每个任务使用一个名称。已有的活动任务会被暂停,而不是关闭;使用tc_switch返回该任务。 - 每完成一步后调用
tc_save,并且务必填写conclusion和next。否则,下一个会话只会看到一堆文件路径,除此之外一无所知。 - 在新会话中或中断后,先调用
tc_resume。不要从头猜测进度。 - 回滚前先预览:先设置
apply: false查看列表,再设置apply: true。
步骤粒度:一个可以描述为“已完成,结论是 X”的单元。粒度太细(每个文件一步)会淹没关键信息;粒度太粗(每个任务一步)则失去回滚的意义。
工具
共有 10 个工具。每个工具都将 root 作为第一个参数,即工作区目录(通常是项目的绝对路径)。
| 工具 | 使用时机 | 必填参数 |
|---|---|---|
tc_init | 开始长任务时。已有的活动任务会被暂停,而不是关闭 | root、name |
tc_save | 每完成一步后 | root、title |
tc_resume | 新会话开始时,或接手被中断的工作时(只读) | root |
tc_show | 检查步骤/漂移记录 | root |
tc_restore | 回滚到某个步骤或漂移记录。应用前先预览 | root,以及 index 或 drift_id |
tc_capture | 立即强制捕获文件更改,无需等待线程 | root |
tc_switch | 返回先前暂停的任务 | root、task_id |
tc_export | 将交接包导出到另一个目录/机器 | root、to |
tc_import | 导入他人的交接包并在此继续工作 | root、package |
tc_compress | 从旧漂移层中回收空间 | root |
tc_save 字段:
title(必填)——用一行说明你做了什么,而不是更改了什么conclusion——这一步得出的结论。最有价值的字段next——接下来要做什么。让工作可以继续推进的关键description——这么做的原因(以后没人记得的推理过程)verified——你实际运行并确认的内容open_questions——留给后续处理的未解决问题close: true——结束任务。已关闭的任务仍可读取
回滚本身也会记录为一条漂移记录;返回的 recovered 是该记录的 drift_id,因此错误的回滚也可以回滚。
值得关注的响应字段
tc_resume.handoff——可直接粘贴到新会话中的现成交接文本;drift_paths是磁盘上尚未记录的更改;health.errors是存储本身的问题tc_save.idempotent——重复保存相同步骤(标题、文件摘要、结论、下一步和已验证内容相同)不会产生重复记录tc_save.active_task_changed——传入另一个任务的task_id会切换活动任务(旧任务会被暂停);此字段会告知你发生了切换tc_restore.recovered——回滚自身的drift_idtc_restore.plan.unrestorable/skipped_paths——无法还原的文件;预览和执行使用相同的列表tc_capture.waiting/partial——仍处于静默期内,以及尚未完成的中间记录tc_init.exclude——回显你设置的排除模式tc_export.path——交接包目录,其中包含供接手工作的人使用的HANDOFF.md提示词
将 SKILL.md 安装到 Agent 的技能目录
SKILL.md 是本项目的另一半,并非可有可无的文档:MCP 服务负责存储,SKILL.md 则让模型在每个步骤之后实际调用 tc_save。
将它复制到客户端会扫描的目录中;该目录名就是技能名称。
| 客户端 | 路径 |
|---|---|
| Claude Code | ~/.claude/skills/task-checkpoint/SKILL.md |
| Codex CLI | ~/.codex/skills/task-checkpoint/SKILL.md |
| ZCode | ~/.zcode/skills/task-checkpoint/SKILL.md |
| 在多个客户端之间共享 | ~/.agents/skills/task-checkpoint/SKILL.md |
多个客户端共享 ~/.agents/skills/;如果同一技能名称同时存在于共享目录和客户端专属目录中,客户端专属目录优先。对于 OpenCode 和其他客户端,请使用该客户端文档中说明的技能目录——规则不变。
tc-mcp --install-skill ~/.claude/skills/task-checkpoint
SKILL.md 作为包数据包含在 wheel 中,因此 pipx install 用户既不需要仓库,也不需要网络访问。它不会覆盖已有文件——如果确实需要覆盖,请添加 --force。tc-mcp --print-skill 会打印文件内容。
如果使用克隆的仓库,cp SKILL.md <skill-dir>/ 效果相同。
在 Windows 上,路径如下:C:\Users\<you>\.claude\skills\task-checkpoint\SKILL.md。不同客户端可能会扫描不同目录——请查阅各自的文档。唯一的要求是文件位于 <skill-dir>/<skill-name>/SKILL.md。重启客户端以使其生效。
验证(可选)
最快的检查方式是确认入口正常运行,并查看所用版本:
tc-mcp --version # version
tc-mcp --help # all flags
以下所有操作都需要克隆的仓库——pip 安装包不包含 tests/ 或 tools/。
在克隆的仓库中运行回归测试套件:
python -m unittest discover -s tests -p "test_*.py" -v
最后一行应为 OK。完整测试套件(100 多个用例)所需时间完全取决于运行环境:
| 环境 | 测得耗时 |
|---|---|
| CI ubuntu runner | 4.7 s |
| CI windows runner | 39.8 s |
| 本机,Windows(E: 驱动器) | 156–340 s |
相同代码,耗时相差 70 倍。开销来自 git 子进程(refs/checkpoints/ 用例占了大头)和静默期等待,而且对磁盘速度和实时防病毒扫描极其敏感——因此,不要将这些数字视为保证,也不要据此比较不同机器。
仅使用标准库(unittest),不需要 pytest。它也可在 Python 3.10 上运行;读取 pyproject.toml 的用例会在该版本中跳过,因为 tomllib 需要 Python 3.11 或更高版本。
仓库还提供了一个针对真实工作区的端到端演练(启动真实的 MCP 子进程,在运行中途将其终止,构建一个约 190 MB 的大型工作区,并逐项验证“不会触碰你的 git”这一承诺):
python tools/drill.py
演练结束时会删除工作区;使用 TC_DRILL_KEEP=1 python tools/drill.py 可保留工作区(如果演练失败,也会自动保留,以便诊断)。
确认 MCP 服务能够启动——它会返回一行包含 serverInfo 的 JSON:
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18"}}' | python scripts/tc_mcp.py
通过 pip 安装的副本没有 scripts/;请将最后一部分改为 ... | tc-mcp(或 python -m tc_mcp)。
环境变量
| 变量 | 默认值 | 含义 |
|---|---|---|
TC_STORE | <workspace>/.checkpoints | 存储目录 |
TC_WATCH | on | 设为 off 可禁用后台记录器 |
TC_WATCH_INTERVAL | 15 | 后台轮询间隔(秒) |
TC_DRIFT_MAX_WAIT | 60 | 静默期上限(秒) |
TC_GIT | on | 设为 off 可完全停止创建 refs/checkpoints/... |
TC_GIT_VERIFY | on | 前后检查 git 指纹,以证明未触碰你的仓库。设为 off 可让每次 tc_save 少运行几个 git 子进程;此时 git.unchanged 将返回 null |
TC_SHA_REUSE | off | 当 mtime 和 size 未变化时,复用已索引的 sha,而不是重新读取。风险说明请参阅“限制” |
限制
敏感文件:覆盖不全的黑名单
敏感文件既不存储内容,也不存储哈希——只记录 mtime 和大小是否发生变化。检测依据路径匹配,因此会出现两种漏判:
- 假阴性:凭证文件名看起来很普通,因此不会被拦截。
config/database.yaml、docker-compose.yml、config.json会以明文形式存储在payload/中。请勿将凭证放在扫描路径中;如果无法避免,请使用tc_init(exclude=[...])排除整个目录。 - 假阳性:被误判为敏感文件的源文件不会保存内容副本,因此回滚时会静默跳过这些文件(它们只会出现在
plan.unrestorable中)。这就是名单分为两层的原因。
以下内容会被拦截:
- 目录名(整个目录):
.ssh.aws.gnupg.docker.kube.env.envdir.secrets.tokens,以及名称包含secret/credential/token的任何目录。 - 明确的文件名:
.env.env.**.env*.envrc;*.pem*.key*.p12*.pfx*.p8*.jks*.keystore*.kdbx*.ppk*.ovpn*_rsa*_dsa*_ed25519*_ecdsa*_key;.netrc.git-credentials.npmrc.pypirc.pgpass.htpasswd.my.cnfmy.cnfpasswdshadow;kubeconfig*.kubeconfig*.tfstateterraform.tfvars*.tfvars*.jwt;credentials*creds*secret*secrets.jsontokentoken.jsontoken.txt*.token*_tokenauth.json;settings.pylocal_settings.pysettings.xmlwp-config.phpweb.configwrangler.toml.dev.vars。 - 含义模糊的子串(
*secret**password**passwd**credential**api_key**apikey**token**auth_token*):仅当文件名以配置类型后缀结尾时才会被拦截——.json.yaml.yml.toml.ini.cfg.conf.config.properties.xml.txt.env.envrc.cnf.sh.ps1.bat.cmd.sql.php。因此,prod_credentials.json会被拦截,而src/password_policy.py、src/tokenizer.py和docs/secrets.md不会。
exclude 模式遵循相同规则:glob 模式匹配单个名称段(目录名,或路径的第一段)。exclude=["config"] 会排除任意深度下的 config/ 目录,但无法排除某个特定文件——排除 config/database.yaml 意味着排除整个 config/ 目录。包含 / 的模式会被直接拒绝,而不是静默忽略。
**这并不构成隐私保护或合规保证。**请自行管理存储的访问权限;不要将未排除的凭证、令牌、私钥或个人数据留在扫描路径中。
时间与粒度
- 静默期最长约为 60 秒。在文件持续变化期间,中间记录会标记为
partial: true。 - 如果进程突然退出,变更末尾部分只会出现在
tc_resume的尚未记录路径中,直到下一次tc_save/tc_capture。
漂移层仅检查 mtime+size
tc_capture 和后台线程通过 mtime+size 检测变化。如果内容发生变化,但 mtime 和长度未变(cp -p、rsync -t、tar -x 会保留 mtime),则检测不到变化。
tc_save 默认会对内容进行哈希处理,不受影响——但 TC_SHA_REUSE=on 时例外,此时任务层也会采用相同的检测方式。
回滚
- 会恢复权限位(POSIX 可执行位),但不会恢复 mtime——写回旧时间戳会让类似 make 的工具认为文件没有变化。注意:内容相同、仅权限发生变化时不会触发重写,因此这种情况下权限位会保持原样。
- 无法恢复的文件会被跳过(敏感文件、超过 100 MB 且未存储内容的文件、载荷丢失的文件),其余文件继续处理;被跳过的路径会出现在
plan.unrestorable中。预览和执行使用相同的列表,因此单个.env不会导致整个回滚失败。 - 二进制文件会恢复,但不会生成逐行差异。并发编辑不会合并。
大文件
大小不超过 100 MB 的文件会按原样存储在 payload/ 中,并根据内容去重。超过 100 MB 的文件只保留流式哈希和元数据——不保留内容,因此无法回滚内容。
存储不会自行缩小
旧的漂移内容会一直保留,直到你调用 tc_compress。其默认设置是保留 7 天,因此在较小的工作区中,默认调用通常会报告 converted: 0——响应中的 keep_seconds / minimum / maximum / eligible / bytes_freed 会说明原因。传入更小的 keep_seconds 才能真正回收空间。步骤基线和 recovered 层永远不会被触及。
使用 TC_SHA_REUSE=on 的风险(以及它为何默认关闭)
默认情况下,任务层会在每次保存时对内容进行哈希——它不信任 mtime——因此每次保存都会读取工作区中的每个非敏感文件(不保留内容,因此内存用量不会随工作区大小增长)。
使用 TC_SHA_REUSE=on 时,mtime+大小未变化的文件不会重新读取,而是复用已索引的 sha。如果 mtime 曾被还原(os.utime)、文件系统的 mtime 精度较低(FAT、某些网络驱动器),或工具会保留 mtime(cp -p、rsync -t、tar -x、某些生成器),内容编辑就会完全无法检测到:不会读取任何内容,也不会存储任何新内容,过期的 sha 会写入步骤记录,而回滚会悄无声息地返回旧内容。默认设置下不会出现这种情况。
在这两种模式下,tc_resume 和 tc_show 都不会读取文件内容(只检查 mtime+大小),因此也可能漏掉保留 mtime 的编辑。
参数和状态限制
tc_restore必须且只能接收index或drift_id之一;apply=true不会修改已关闭的任务。- 将另一个任务的
task_id传给tc_save时,会先切换当前活动任务(原任务将变为挂起状态);active_task_changed: true会告知你发生了切换。使用tc_switch可切回原任务。 - 如果存在基线且检测到未记录的更改,
tc_save会在写入步骤前将这些更改附加到本次保存的清单中。 tc_export要求目标的父目录已存在;tc_import会验证任务 ID 和工作区路径,拒绝重复任务、..路径遍历和符号链接目标,并在失败时回滚已写入的文件。store不能等于工作区根目录;如果自定义的store位于工作区内,则会自动排除在扫描之外。
git
在 git 仓库中,每次 tc_save 都会额外耗费几百毫秒(用于创建引用的 git 子进程)。可通过 TC_GIT=off 或 TC_GIT_VERIFY=off 避免。
存储布局
<workspace>/.checkpoints/
├── current.json active-task pointer, keyed by workspace root (one store can serve many)
├── state.lock concurrency lock
├── server.log logs (never stdout; stdout carries MCP frames only)
├── payload/<sha2>/ content-addressed file copies, deduped by sha across tasks
└── tasks/<task_id>/
├── state.json
├── index.json baseline index: mtime/size/sha256 per file
├── steps/sNNNN.json task layer: one record per step
├── drifts/dNNNN.json drift layer: file changes
└── manifest/mNNNN.json consistency manifests (incremental chain, full anchor every 20)
活动指针和基线索引按工作区分别管理:多个工作区共用同一个 store 时,彼此的数据不会泄漏。
仓库布局
.
├── README.md this file
├── SKILL.md the agent-facing usage contract (part of the feature, ships with the package)
├── LICENSE MIT
├── pyproject.toml packaging; sources stay in scripts/, not a package dir
├── MANIFEST.in what goes into the sdist
├── server.json publish metadata for the official MCP Registry
├── glama.json ownership marker for the Glama directory
├── scripts/
│ ├── tc.py core
│ └── tc_mcp.py stdio MCP adapter + the human-facing command-line flags
├── tools/
│ └── drill.py end-to-end probe against a real workspace (not in the test suite, run by hand)
├── npm/ the npm launcher package (a shell; the server itself is not here)
├── tests/
│ ├── test_tc.py full acceptance suite (rollback safety, concurrency lock, git refs, return fields, packaging contract)
│ ├── test_tc_regressions.py targeted regressions for fixed defects
│ └── test_packaging_consistency.py the three version numbers / registry metadata / README ownership marker
└── .github/workflows/ci.yml CI: run the tests, build, validate server.json and the wheel
构建产物不会放入仓库。有两个发布渠道:PyPI 上的 task-checkpoint-mcp(pipx install task-checkpoint-mcp)是主要渠道,wheel 和 sdist 也会附在 GitHub Releases 中。
参与贡献
欢迎提交问题和 PR。修改代码前请先运行测试(参见上文“验证”——这里有 100 多个用例,耗时 2.5–6 分钟)。
tests/test_tc.py是完整的验收测试套件:回滚安全性、并发锁、git 引用可达性、每个分支的返回字段、打包约定tests/test_tc_regressions.py收录了针对已修复缺陷的回归测试——修复 bug 时,请在这里添加用例,而不只是修改代码
有一项硬性要求:只能使用标准库。不得添加新依赖,包括测试依赖(测试使用 unittest,而不是 pytest)。
许可证
MIT——参见 LICENSE。
供修改此代码的人员参考的实现说明(.git/index 字节计数、引用冲突行为、锁定策略、清单锚定)见上文的中文部分。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。