Skip to main content
Glama

todo-mcp

一个 MCP 服务器,其存储是一个 TODO.md,你可以手动阅读、编辑和 diff 它。写入采用字节区间拼接(byte-range splice),因此文件始终归你所有:手工编写的表格、制表符缩进以及任务之外的任何正文,绝不会被重新序列化。

对 MCP 客户端使用 stdio,对其他一切使用 Streamable HTTP。

致谢

基于 CalamityAdam/mcp-todo,原始脚手架来自该项目:createTodoMcpServer 工厂形态、Express Streamable HTTP 包装器以及会话处理。

除此之外几乎没有其他保留。那个版本把待办事项作为带编号的记录存放在 ~/.mcp-todos.json 的 JSON blob 中,提供三个基于 { id, title, done } 的工具。本版本用 Markdown 文档取代了存储,用 slug 取代数字 id,并将工具面扩展到七个,包含状态、领域、引用面包屑、带日期的日志记录、全文查询和重复检测。两个项目不再共享实现。

上游没有附带 LICENSE 文件;其 package.json 声明为 ISC,本仓库沿用这一声明。

Related MCP server: MCP Project Manager

安装

直接从 GitHub 运行,无需克隆:

npx github:adrianhardy/todo-mcp

推送任何更改后,使用 npx --ignore-existing github:adrianhardy/todo-mcp 来获取更新。

常规使用的话,安装一次即可忘记它的存在:

npm i -g github:adrianhardy/todo-mcp
todo-mcp

两种方式都会在安装时通过 prepare 脚本从源码构建,因此 dist/ 不会被提交。需要 Node 20 或更高版本。

用法

todo-mcp 默认启动 HTTP 服务器,因为当一个人在终端中运行时这才是最有用的做法。设置 MCP_STDIO=1 可改用 stdio,这正是 MCP 客户端将其作为子进程生成时想要的。

与 MCP 客户端一起使用

{
  "mcpServers": {
    "todo": {
      "command": "npx",
      "args": ["-y", "github:adrianhardy/todo-mcp"],
      "env": { "MCP_STDIO": "1" }
    }
  }
}

全局安装后,那将变成 "command": "todo-mcp",使用相同的 env 块。

工作目录决定你获得哪个文件。 TODO_FILE 相对于进程的 cwd 解析,默认为 TODO.md,因此在项目中启动的客户端会编辑该项目的 todo 列表。如果无论服务器从哪里启动都想共享一个列表,请将 TODO_FILE 设置为绝对路径。

通过 HTTP

PORT=8080 TODO_MCP_TOKEN=$(openssl rand -hex 32) todo-mcp
  • POST /mcp - JSON-RPC 请求

  • GET /mcp - 用于服务器通知的 SSE 流

  • DELETE /mcp - 结束会话

设置 TODO_MCP_TOKEN 后,以上三个操作都要求 Authorization: Bearer <token>。保持不设置则禁用身份验证,这在 localhost 上没问题,其他任何地方都不行。

配置

变量

默认值

含义

TODO_FILE

TODO.md

存储路径,相对于 cwd 解析

MCP_STDIO

未设置

1 选择 stdio 而非 HTTP

PORT

3000

HTTP 端口

TODO_MCP_TOKEN

未设置

bearer 令牌;未设置表示无身份验证

如果存在 .env 文件则会读取。参见 .env.example。

存储

TODO.md 是存储,而不是 JSON blob。文件就是记录:可读、可手动编辑,并且可在 git 中 diff。

任务是一个 ## 小节。服务器拥有的字段位于标题正下方的注释块中;其下的所有内容是您人类拥有的正文。

## Feature Idea version two: the new widget which tracks things

<!-- todo
id: feature-idea-version-two
area: inventory
status: next
refs: [./src/do_stuff.ts, ClassName.Method, OtherClassName]
created: 2026-08-19
updated: 2026-08-22
-->

**Next step:** close the ledger. ClassName.Method uses 0.25 and it needs 17.2%.

**Already known:** ...

### Log

- 2026-08-22 Slab_Wall_1x3 not started; parade places 24 of those to every 6 of the 3x3.

ID 是 slug 而非数字,因此它们能在重排和删除后幸存。文件顺序即优先级顺序,这就是为什么没有优先级字段。

写入采用字节区间拼接:一次变更只重写它拥有的跨度。手工撰写的表格、制表符缩进以及任务小节之外的任何正文都绝不会被重新序列化,因此不会被重排或丢失。各个处理器通过锁串行化处理。因为两个交错的读-修改-写循环会针对不再描述文件的偏移量进行拼接。

设计

  • 列表是摘要的。list_todos 返回一行索引,绝不返回任务正文。要获取一个完整的部分,使用 get_todo。q 或 ref 是预期的路径,列出所有内容是例外情况。

  • 捕获(Capturing)只需要一个字段。 只有 title 是必需的,新任务默认状态为 captured。如果某个工具要求在注意到某件事时就提供领域和下一步,那它就不会被使用,而文件大致上只有在事情被记录时才发挥作用。Triage 后面会将 captured 移到 open/next/parked/someday。

状态值

状态

含义

captured

原始的、未经分诊的。新任务的默认值。从不含过滤条件的列表中隐藏

open

待办事项,需要跟进,已理解

next

现在就要做的工作

parked

刻意推迟的,正文中会说明原因。

someday

雄心的、愿望清单式的

done

完成。为了记录保留在文件中。从不含过滤条件的列表中隐藏

工具

  • list_todos - 摘要索引;可过滤 area、status、ref、q、limit

  • get_todo - 单个任务的完整 Markdown,包含正文

  • add_todo - 捕获一个任务;只需要 title。会报告可能的重复项

  • update_todo - 修改任一字段;只重写传入的字段

  • append_note - 在任务的日志中添加一个带日期的项目符号条目

  • set_status - 通过分诊推进任务状态

  • remove_todo - 删除一个任务及其正文。优先选择 set_status done

资源

  • todos://list - 当前任务的一行索引

架构

  • src/todo.ts - markdown 存储:解析、字节区间修补、查询、去重

  • src/server.ts - MCP 工具表面。纯工厂,导入时无副作用

  • src/http.ts - Streamable HTTP 传输、身份验证和会话映射

  • src/cli.ts - todo-mcp 二进制;选择传输方式并启动它

Related MCP Connectors

Related MCP Servers