ank MCP 服务器

CLI 的每个动词都是一个工具,给没有 shell 的客户端用。添加到 Claude Code 只要一行:

claude mcp add ank -- ank mcp --repo /path/to/your/repo

ank mcp 是什么

ank mcp 是那个可执行文件的一个动词,每种安装方式装的都是它。不需要再取第二个文件:CLI 分发什么,服务器就提供什么,因为它们是同一个二进制。

它通过 stdio 说 JSON-RPC,由客户端来启动。所以你不用启动它,而是配置它:一条命令、它的参数,以及它代表的仓库。

配置客户端

三个客户端里的配置项是同一段 JSON,只是放的文件不同。

.mcp.json · 配置项
{
  "mcpServers": {
    "ank": {
      "command": "ank",
      "args": ["mcp", "--repo", "/path/to/your/repo"]
    }
  }
}

放在哪里

Claude Code
仓库根目录下的 .mcp.json,这种形式会随工作树一起走,所有 clone 的人都能用上。或者用上面那行 claude mcp add,它会替你写好配置项。
Claude Desktop
claude_desktop_config.json,macOS 上在 ~/Library/Application Support/Claude/,Windows 上在 %APPDATA%\Claude\。
Cursor
仓库旁边的 .cursor/mcp.json,或者用 ~/.cursor/mcp.json 一次覆盖所有项目。

命令是 ank,mcp 是第一个参数

0.6.0 及以前的版本会额外安装一个名为 ank-mcp 的可执行文件。现在任何安装方式都不再提供它,所以还在用它的配置会得到 command not found。只需要改这一行。

一定要写 --repo

客户端会在它恰好所在的目录里启动服务器,没有 --repo 时,服务器就用那个目录。这种失败不会报错:它只是一个进程,悄悄地代表一个谁也没想要的 corpus 说话,或者什么都不代表。没有 .ank/ 的路径会在启动时被拒绝,让人能看到。

ank mcp
ank mcp --repo /tmp
error[1]: no .ank/ found from /tmp
  -> ank init

每个动词,一个工具

这些工具由二进制分发所用的同一张表生成,ank help --json 描述的也是这张表。不是精选的子集:两个入口不可能在“有哪些东西”上不一致。

  • ank_context
  • ank_claim
  • ank_show
  • ank_log
  • ank_done
  • ank_release
  • ank_new
  • ank_review
  • ank_accept
  • ank_read
  • ank_close
  • ank_amend
  • ank_attest
  • ank_find
  • ank_status
  • ank_graph
  • ank_scope
  • ank_tui
  • ank_mcp
  • ank_watch
  • ank_edit
  • ank_check
  • ank_migrate
  • ank_archive
  • ank_config
  • ank_init
  • ank_skills
  • ank_update
  • ank_help
  • 命名为 ank_<verb>

    光叫 context 会和客户端加载的其他服务器撞名。摘要变成工具描述,flag 变成输入 schema,位置参数放在 arguments 里,是一个字符串数组。

  • CLI 的回答,连同退出码

    一次调用返回的就是 --json 返回的文档,旁边附上 exitCode。拒绝就是 CLI 的拒绝,连提示一起,作为带 isError 的结果返回。JSON-RPC 错误表示请求本身有误;isError 表示 corpus 说了不。

  • CLI 不会做的 claim,它也不做

    每个 claim 都落在对应仓库的 refs/ank/claims/ 里,走同样的 compare-and-swap。服务器不替任何客户端持有什么,写入时的身份是 ank-mcp/<version>,除非 ANK_AGENT 指定了身份。

一次被 corpus 拒绝的调用
--> {"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"ank_show","arguments":{"arguments":["TASK-9999"]}}}

<-- {"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"error[2]: entity not found: TASK-9999\n  -> ank find TASK-9999"}],"isError":true,"exitCode":2,"stderr":"error[2]: entity not found: TASK-9999\n  -> ank find TASK-9999"}}

多个仓库,一个服务器

每个工具都有一个可选参数 corpus:即 ank status --json 在 "corpus" 下打印的根提交,绝不是路径。用 ank config --user corpora.<root> <path> 把每个仓库声明一次,客户端配置一个字都不用改。一个服务器可以面向多个 corpus,但绝不会合并它们的 claim。

ank_accept 和其他动词一样存在,在默认分支之外照样拒绝。批准一项决策,仍然是人的行为。

MCP 还是 CLI

两者调用的是同样的动词,返回同样的文档。按你的客户端能做什么来选。

你的智能体有 shell

用 CLI。npx skills add haksolot/ank 会给智能体装上教它这套流程的 skills,它像调用其他命令一样调用 ank。

你的客户端没有 shell

用 ank mcp。客户端会把同样的动词看作工具,每个描述里都写明了拒绝情形和退出码,所以调用之前就能知道它会拒绝什么。

在做展示 corpus 的东西?轮询 ank_status 和 ank_find。ank_context 和 ank_show 会续约 claim,ank_check 会清理过期的 ref,也就是会写入。

安装 ank

两条命令。支持 Linux、macOS 和 Windows,需要 git 2.34 或更高版本。

npm install -g @haksolot/ank
npx skills add haksolot/ank