The ank MCP server

Every verb of the CLI as a tool, for a client that has no shell. One line to add it to Claude Code:

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

What ank mcp is

ank mcp is a verb of the one executable every install route places. There is no second file to fetch: what the CLI dispatches is what the server serves, because they are the same binary.

It speaks JSON-RPC over stdio, and the client spawns it. So you do not start it, you configure it: a command, its arguments, and the repository it speaks for.

Configure a client

The entry is the same JSON in all three clients. Only the file it goes in changes.

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

Where it goes

Claude Code
.mcp.json at the root of the repository, the form that travels with the tree and reaches everyone who clones it. Or the one-line claude mcp add above, which writes the entry for you.
Claude Desktop
claude_desktop_config.json, in ~/Library/Application Support/Claude/ on macOS and %APPDATA%\Claude\ on Windows.
Cursor
.cursor/mcp.json beside the repository, or ~/.cursor/mcp.json for every project at once.

The command is ank, and mcp its first argument

Releases up to 0.6.0 placed a second executable named ank-mcp. No route places it any more, so a configuration still naming it gets command not found. That is the one line to change.

Always write --repo

A client spawns the server in whatever directory it happens to be in, and without --repo the server takes that directory. The failure is not an error: it is a process quietly speaking for a corpus nobody meant, or for none. A path with no .ank/ under it is refused at startup, where a person sees it.

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

Every verb, one tool each

The tools are generated from the same table the binary dispatches from and ank help --json describes. Not a curated subset: the two surfaces cannot disagree about what exists.

  • 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
  • Named ank_<verb>

    A bare context would collide with every other server a client has loaded. The summary becomes the description, the flags become the input schema, and positionals arrive as arguments, an array of strings.

  • The CLI’s answer, exit code included

    A call returns the document --json returns, with exitCode beside it. A refusal is the CLI’s refusal, hint and all, returned as a result with isError. A JSON-RPC error means the request was wrong; isError means the corpus said no.

  • No claim the CLI would not take

    Every claim lands in refs/ank/claims/ of that repository, under the same compare-and-swap. The server holds nothing on a client’s behalf and writes as ank-mcp/<version> unless ANK_AGENT names an identity.

a call the corpus refuses
--> {"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"}}

Several repositories, one server

Every tool takes an optional corpus argument: the root commit ank status --json prints under "corpus", never a path. Declare each repository once with ank config --user corpora.<root> <path> and the client configuration does not change by a character. One server may address several corpora; it never merges their claims.

ank_accept is there like every other verb, and still refuses off the default branch. Ratifying a decision stays a human act.

MCP or the CLI

Both reach the same verbs and return the same documents. Pick by what your client can do.

Your agent has a shell

Use the CLI. npx skills add haksolot/ank gives the agent the skills that teach it the loop, and it calls ank like any other command.

Your client has no shell

Use ank mcp. The client sees the same verbs as tools, with the refusals and exit codes written into each description, so it can read what a call will refuse before making it.

Building something that shows a corpus? Poll ank_status and ank_find. ank_context and ank_show renew a claim, and ank_check prunes stale refs, so it writes.

Install ank

Two commands. Linux, macOS and Windows, with git 2.34 or newer.

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