Casefile
Casefile is a self-hosted MCP work-log server where agents and people keep one append-only "case file" per task, so work survives session hand-offs.
Find and read tasks:
get_taskreturns a task's card, parent/children, links, cited project decisions, computed features, latest summary, open questions/remarks and case index in one call;search_tasksfilters by query language or conditions with sorting and paging.Create and edit tasks:
create_task(optionally as a child, with the five sections, checks and decisions),update_task(fields, checks, assignee, priority, optimisticversionguard),move_taskto another project keeping old keys addressable.Drive status:
transitionalong a fixed table (blocks on missing assignee, open blockers or blocking questions, missing summary), andclose_task— the only way todone— filings entries, verdicts and final summary in one transaction.Write the case:
add_entry(decision, attempt, finding, artifact, remark, acceptance, note),add_summary(done/remaining/blockers/next step — the handover note),add_verdictper review check,resolveremarks,ask/answerquestions to participants,read_entriesto read them back.Project-level records:
create_project,update_project,archive_project/restore_project,get_project,list_projects, project case entries viaadd_project_entry(decisions withsupersedes, findings, artifacts) andread_project_entries, plus key/valueset_attribute/remove_attributewith history.Participants and access:
register_participantandupdate_participantmaintain the registry of humans and permanent agents that questions can be addressed to.Linkage:
link/unlink(parent/child, blocks/blocked_by, relates) with cycle and self-link guards, filing entries on both sides.Live updates:
wait_journalstreams every case entry inseqorder with filters and a timeout, so an agent can wait for an answer or another session's work.Safety properties: entries are immutable and corrections are new entries; idempotency keys on writes; version conflicts instead of overwrites; refusals carry stable error codes and details.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Casefile pick up task 12 where the last agent left off and continue"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Casefile
One case file per task — shared by every session, agent and person that touches it.
Casefile keeps a case file for every task: the decisions, failed attempts, findings and open questions that agents write down as they work. When a session ends, the next one — the same agent, an agent from another vendor, or you — reads the latest summary and the open questions and carries on. It is a self-hosted MCP server and a web board, MIT-licensed; it assigns and schedules nothing.
For anyone whose agents work on tasks longer than one session. Free to use. Made for Claude Code; Codex, Cursor and any other MCP client connect the same way.
Install on macOS / Linux
curl -fsSL https://raw.githubusercontent.com/azimov777/casefile/main/install.sh | shInstall on Windows (PowerShell)
irm https://raw.githubusercontent.com/azimov777/casefile/main/install.ps1 | iexAll you need is Docker. The board opens at http://localhost:8080, and the installer connects Claude Code and Codex by itself — a plugin and an OAuth sign-in, no token to copy. Casefile updates itself to each new release: it checks once an hour and whenever Docker starts. A small updater service does it and holds the Docker socket for that; you can turn it off.
Or let your agent do it. Paste this into Claude Code, Codex or Cursor:
Install Casefile for me by following https://raw.githubusercontent.com/azimov777/casefile/main/docs/agent-install.md
Why
A session ends or the context fills up, and the next agent starts from scratch: re-reading the code, re-trying what already failed, re-asking what you already answered.
Casefile gives every task a case file — an append-only log the agent writes as it works.
Hand-offs that survive a fresh context. The next agent reads the latest summary, the open questions and an index of the case, then carries on. No re-discovery.
Built for agents, over MCP. Agents create and split tasks, record decisions and dead ends, ask you questions, and close with a verdict on every check.
You stay in the loop. A live board and task pages show what every agent is doing. Answer questions, leave remarks and hand each agent its own access — right from the browser.
Guardrails, not bureaucracy. No closing without a summary and a passed verdict per check; no starting a blocked task. Nothing else — no sprints, no estimates, no automation.
Yours, on your machine. Runs locally in Docker and listens on localhost only. Nothing leaves your computer — unless you turn on sign-in and put it on your own server for your team (Network mode).
Related MCP server: meridian
How it's different
Not a notes file. A
CLAUDE.mdorhandoff.mdgets overwritten: the attempt that failed two days ago disappears, and two sessions edit the same file. A case file is append-only — a correction is a new entry that points at the old one. KeepCLAUDE.mdfor per-repo rules; Casefile is per task.Not a memory server. Memory MCPs recall facts by similarity. Casefile recalls nothing clever: it is a work log per task, read in a fixed order — card, latest summary, open questions, index, then only the entries you need.
Not an issue tracker with MCP bolted on. An issue is a description and a thread anyone can edit. Case entries are typed and never edited, and the tracker refuses writes that would break the record.
Not an orchestrator. It never starts agents, runs timers or moves tasks by itself. Handing out work and noticing a dead session stay with you and your agent harness.
Connect your agent
The installer connects Claude Code and Codex by itself. It installs the Casefile plugin — the skill and a connection to this installation's MCP address — and signs them in with OAuth: no token is copied and none lands in any file. If the installer had no terminal for the sign-in, it prints the commands; run them once:
claude mcp login plugin:casefile:casefile
codex mcp login casefileThe installer asks before it changes anything of another program. The plugin step edits
files that are not Casefile's: Claude Code's ~/.claude/settings.json (one line,
"autoUpdate": true, inside extraKnownMarketplaces.casefile), Codex's
~/.codex/config.toml, the manual MCP entries casefile and tracker that point at this
installation, and ~/.agents/skills. It prints that list and asks [y/N] on your terminal
(also under curl … | sh); N skips the whole plugin step, leaves those files alone and
installs the service as usual. Before the first change to a file it saves a copy next to
it — settings.json.casefile-bak, config.toml.casefile-bak, kept and never overwritten —
and it writes only the line it needs, leaving the rest of the file as it was. The copy
holds whatever the file held, a token of a manual entry the installer removes included:
delete it when you no longer need it. These variables go in front of sh:
CASEFILE_SKILL=0skips the plugin step;CASEFILE_SKILL=1answers the question in advance. Without a terminal (an agent, CI) nothing is asked and the step runs, as it does with1.CASEFILE_PLUGIN_AUTOUPDATE=0installs the Claude Code plugin withoutautoUpdate; update it yourself withclaude plugin update casefile@casefile.
The same commands sign an agent in again after you disconnect it on the board's
Access screen (in a Claude Code session, /mcp → casefile → Re-authenticate).
On your own machine the sign-in needs no password: Claude Code acts as claude, Codex as
codex.
The skill comes with the plugin. Connecting gives the agent the tools; the Casefile
skill teaches it how to use them. The installer puts it into Claude Code, Codex, Hermes
and other agents it finds on the machine, and prints one line per harness. An
installation made before v0.8.0 has no skill and no plugin, and the hourly self-update
does not add them: it updates only the service. Run the install line again, or install
just the plugin and the skill without touching the service (add CASEFILE_URL=<address>
for a server that is not on this machine):
curl -fsSL https://raw.githubusercontent.com/azimov777/casefile/main/install.sh | CASEFILE_SKILL_ONLY=1 shHow to check whether an agent has the skill, and the commands for each harness, are in step 4 of the agent guide.
Claude Desktop gets an extension for its chat. The chat of the Claude desktop app
(macOS, Windows) does not load the Claude Code plugin; it gets Casefile from
casefile.mcpb,
attached to every release. The installer downloads and opens it when it finds Claude Desktop;
by hand, double-click the file. It signs in with OAuth, needs no Node.js and works while the
installation runs (details).
Hermes signs in with OAuth (auth: oauth in ~/.hermes/config.yaml; the installer
prints the block; not yet checked against a real Hermes). Clients without OAuth use the
agent key. Scripts and any client that cannot
sign in with MCP OAuth connect over streamable HTTP at the MCP address with the agent key
in a header. The installer prints the key in their blocks; read it again with
docker compose run --rm --no-deps -T agent-token cat .secrets/agent-token in
~/casefile. Such a client that takes an mcpServers JSON uses this — with the key and,
if your installer printed a different address, that address instead:
{
"mcpServers": {
"casefile": {
"type": "http",
"url": "http://localhost:8100/mcp",
"headers": {
"Authorization": "Bearer <token>"
}
}
}
}Over stdio, as an alternative. Streamable HTTP above is the main way in. A client that can only launch a command and talk to it over stdin/stdout gets the same server that way: it starts a short-lived container of your installation, attached to the installation's database — same tools, same key, same case. There is no OAuth over stdio: the agent key goes in the client's environment, not on the command line:
claude mcp add --scope user casefile-stdio --env TRACKER_MCP_TOKEN=<token> -- \
docker compose -f ~/casefile/docker-compose.prod.yml run --rm --no-deps -T \
-e TRACKER_MCP_TOKEN mcp python -m app.mcp --stdioThe installation has to be up: the stdio process brings no database of its own. Each
client session is a process of its own, so HTTP stays the lighter choice wherever the
client supports it. An installation image older than the stdio mode answers
unrecognized arguments: --stdio — update it first.
A second agent, without the terminal. Connect an agent on the board shows this installation's MCP address and ready-made snippets. Access lists every way in, in three sections: agent connections (OAuth sign-ins), agent keys and sign-in sessions — yours, or every one of the installation if you are an administrator: who it speaks for, who issued it and when it was last used. A connection is disconnected there and a key revoked. For a client without OAuth you register an agent, issue its own key there and copy the snippet with the key already in it — shown once. Give each agent its own sign-in or key and its case entries are signed with its name. On a shared installation every person does this for their own agents, without the administrator, and sees only the connections and keys they issued or that speak for them.
Tell it what to do
Connecting the agent is only the wiring. Once it is done, tell the agent these, word for word.
Have work to hand over? Say:
File tasks in Casefile for my work: a project for it if there is none yet, and tasks with all their sections and checks, each small enough for one agent to finish in one go, each naming its environment in
context— where the work lives and how to check it is done. Don't start the work itself; if I haven't described it yet, ask me.
Then, in a new agent session, say:
Carry out the tasks for this work from the Casefile tracker. Hand them to agents, one task per agent, to save your own context, and give them cheaper models where those cope.
The two phrases go to different agent sessions: the second agent starts with a clean
context and knows only what is in the task cases. The board carries the same phrases,
with copy buttons, on its /start page.
Tools
Every MCP tool the server offers, grouped by area (app/mcp/tools/):
Tasks
get_task— returns everything about one task in a single call: card, parent and children, links, computed features, latest summary, open questions, unresolved remarks, its discussions, case index and transition targetssearch_tasks— searches tasks by a query-language string, by separate conditions, or by bothcreate_task— creates a task inbacklogin an area of its project (area_requiredwithout one), optionally as a child of a parent taskupdate_task— changes the given fields of a task; fields left out stay as they aretransition— moves a task to another status along the fixed transition tableclose_task— closes a task: files entries, verdicts and the final summary and moves it todone, in one transactionmove_task— moves a task, or each task of a list with an outcome per key, to another project with a reason; its previous key keeps leading to it
Case
read_entries— returns entry bodies of one task's case, with payload, in number orderadd_summary— files a summary: the handover note of a case, in four partsadd_entry— files an entry without payload: a decision, attempt, finding, artifact, remark or noteask— files a question to registry participants in a discussion; asked about a task, it opens the discussion and attaches the task in the same callanswer— answers a question of a discussion or of a task's case, or closes one still unanswered aswithdrawnorreplacedby a later question, with a reason; an answered question cannot be withdrawnresolve— resolves a remark on a task: its outcome and where the work wentadd_verdict— files the outcome of one review checkread_project_entries— returns entry bodies of one project's, area's or discussion's case, with payload, in number order; filters by number, type, status and a substring of the title or bodyadd_project_entry— files a decision, finding, artifact or note in a project's or an area's case, or a note in a discussion's case
Discussions
add_conclusion— files a discussion's conclusion: what is decided, superseded and still openclose_discussion— closes a discussion with its final conclusion, in one transactionlist_discussions— lists discussions with filters by project, status, turn and attached task: address, title, status, turn, open questions, closing time
Files
request_file_upload— returns a one-time link and thecurl -Tcommand that uploads a new file to a project; the bytes go from disk to the tracker and never through the modelrequest_file_replace— returns a one-time link and thecurl -Tcommand that replaces the content of a file under the same idread_file— returns a file: an image asimagecontent the model sees, any other file as its card and a one-timecurl -olinklist_files— lists files as cards with search by name and filters by project and media type, sorted by time added or opened, size or typedelete_file— erases a file's bytes and keeps its tombstone: name, type, size, who deleted it and whendelete_files— erases every file a filter picks (project, name, type,unopenedfor images not opened for the term of days) once the confirmed count matches
Links
link— links two tasks and fileslink_addedin both cases, or attaches a task to a discussionunlink— removes a link and fileslink_removedin both cases, or detaches a task from a discussion
Projects & participants
get_project— returns one project by its key, or an area by its address in the same shape: key, title, description, current attribute values, the decisions and findings in force and the index of the rest of its caselist_projects— lists the installation's projects the agent may use: key, title and archive time; archived ones only when askedlist_participants— lists the participant registry: the possible addressees of a questioncreate_project— creates a projectupdate_project— changes a project's title and description, recording each change in its casearchive_project— archives a project with a reason, freezing it and its tasks against changesrestore_project— restores an archived project with a reasonset_attribute— sets the value of a project attribute, creating it or changing it with a reason; the history stays in the project's caseremove_attribute— removes a project attribute with a reason, filing its last value in the project's caseregister_participant— registers a human or a permanent agentupdate_participant— changes a participant's description
Journal
wait_journal— returns journal entries after a sequence number, waiting for new ones
Everyday
Update right now | run the install line again |
Turn auto-update off |
|
Stay on one release |
|
Stop / start |
|
Remove everything, data included |
|
Move to another machine or your own server | |
Back up your data / restore into a clean install |
Ports and other settings live in ~/casefile/.env — see .env.example.
Updates
A new version of Casefile is a release: a git tag vX.Y.Z with its images on ghcr.io
under the version and under the stable channel. Every installation follows stable
by default. It checks when Docker starts and then once an hour, at a slightly random
minute, so a release reaches it within about an hour and ten minutes, with nothing to
restart. Commits to main without a tag never reach an installation. The update
recreates the Casefile containers and keeps your data in its volumes. An agent in the
middle of an MCP call when that happens gets a dropped connection and has to retry.
The updater holds the Docker socket. To replace containers the updater service
mounts /var/run/docker.sock, which is root access to the machine; Casefile gives it to
this one service on purpose (docker-compose.prod.yml says why). The installer tells you
this before it installs and again at the end. To turn auto-update off, put
CASEFILE_AUTO_UPDATE=false into ~/casefile/.env (exactly false: any other value
leaves it on) and run docker compose up -d --no-deps updater in ~/casefile. The
service then idles, so the socket stays mounted; docker compose stop updater stops it
until the next docker compose up or run of the installer. With auto-update off the
board tells you when a new release is out: a note at the bottom of the side panel links
to the release notes, and you update by running the install line again. That note comes
from a read of the latest release on GitHub, at most once an hour;
TRACKER_RELEASE_CHECK=false in .env turns the read off, and the note with it.
The first release on this channel was 0.2.0. An installation from before it (on
latest) moves to stable by itself the next time Docker starts, and from then on
checks every hour. A release can also bring a new updater: the update to that release is
still done by the old one, which is then replaced by itself, so what a new updater adds
(such as the rollback below, new in 0.3.0) covers updates from the next release on. If you set
CASEFILE_VERSION=latest in .env yourself, remove the line to follow releases.
CASEFILE_UPDATE_INTERVAL sets how often to check (hours, or 30m; 0 means only
when Docker starts). If a release fails to start, the installation goes back to the
version it ran before and does not try that release again; the next release is installed
as usual. docker compose logs updater tells what happened.
A release that changes the database schema costs one more step. Before installing it,
the updater takes a snapshot of the database (pg_dump -Fc, the same format as
backup and restore). The snapshot goes into a Docker volume of
the installation, casefile_updater-snapshot, as before-update.dump, and takes about
as much space as a manual backup. If that release then fails to start after changing the
schema, the database goes back to the snapshot before the previous version starts again. So the
schema and the data are exactly as they were before the update, and a manual
docker compose up -d works as usual. The price: anything written between the
snapshot and the rollback is lost. That window is the failed start, up to a few
minutes. While the new version is being brought up, the old one keeps answering for a
few seconds. The snapshot is not deleted afterwards, neither after a good update nor after
a rollback: it stays in the volume as a copy of the database from before the update, and
docker compose logs updater says where. There is one copy: the next update that changes
the schema replaces it, and a snapshot that fails never replaces the earlier one. How to
restore from it: backup and restore.
If the snapshot cannot be taken, that release is not installed this time. If it cannot be
restored, the previous version runs on the new schema, and the log says how to copy the
snapshot out.
Network mode
Out of the box Casefile listens on localhost only, and you type nothing: the installation
creates an administrator account for you (owner@localhost) and the board signs into it
by itself. To reach it from other machines, turn on sign-in: then everyone signs in
with their own email and password, and every entry is signed by the person who made it.
It is one team per installation — everyone signed in sees every task. The only role is
the administrator flag, and all it opens is managing people.
Add to
~/casefile/.env:CASEFILE_LOGIN=password # everyone signs in with email and password CASEFILE_BIND=0.0.0.0 # publish the board and MCP beyond localhost TRACKER_MCP_PUBLIC_URL=http://<server>:8100/mcp # what agents on other machines useRun
docker compose up -din~/casefile.Give yourself a password. The administrator account the installation made has none; this prints a generated one, once:
docker compose run --rm api python -m app.cli account-password --email owner@localhostAdd
--set-passwordto type your own instead (12 characters at least, asked twice, no echo). Your email can be changed too:account-update --email owner@localhost --new-email you@example.com.Add your teammates — on the board, or on the server:
docker compose run --rm api python -m app.cli account-create --email alice@example.com --name aliceIt prints Alice's password once; hand it to her.
--adminmakes her an administrator too.account-listshows everyone,account-update --disablelocks a person out and revokes their sessions and every connection and key they issued to their agents (their past entries stay signed with their name), andaccount-passwordresets a forgotten password. Casefile sends no mail: there is no address confirmation and no reset link.
The board at http://<server>:8080 now opens with a sign-in screen. Agents sign in with
OAuth behind TLS (below); Connect an agent shows the address from
TRACKER_MCP_PUBLIC_URL. Each person's agent machine gets the plugin, the skill and the
sign-in with one line — no Docker and no service installer there:
curl -fsSL https://raw.githubusercontent.com/azimov777/casefile/main/install.sh | CASEFILE_SKILL_ONLY=1 CASEFILE_URL=https://mcp.casefile.example.com/mcp shThe steps for the agent are in the agent guide.
Coming from the owner password. An installation locked with TRACKER_PASSWORD_HASH
before accounts existed keeps working after the update: sign-in turns on by itself, and
the old password becomes the password of the administrator account owner@localhost —
sign in with that email and the same password. The hash in .env is no lock any more; it
is only carried over once, and a password you set later is never overwritten by it.
Plain HTTP is a hole. Without TLS the passwords, the session cookies, the keys and
the agents' tokens cross the network in clear text for anyone on the path to read. Casefile
does not do TLS itself. Anywhere beyond a network you trust, keep CASEFILE_BIND=127.0.0.1
and put a reverse proxy with TLS in front of both ports — for example Caddy, which gets the
certificates and sends X-Forwarded-Proto by itself:
casefile.example.com {
reverse_proxy 127.0.0.1:8080
}
mcp.casefile.example.com {
reverse_proxy 127.0.0.1:8100
}with TRACKER_MCP_PUBLIC_URL=https://mcp.casefile.example.com/mcp. A proxy that sends
X-Forwarded-Proto: https gets the session cookie marked Secure.
Agents sign in with OAuth. With sign-in on and TRACKER_MCP_PUBLIC_URL on https, a
client that speaks MCP OAuth (Claude Code, Codex) needs only the address, no token. It
opens a page on the MCP host: the person signs in with email and password and picks which
agent the client acts as. By default it is their own agent, created on the first
sign-in: Alice's Claude Code signs its case entries as claude_alice, her Codex as
codex_alice (agent_<name> for other clients). The list holds their own agents and
agents without an owner, never someone else's. The connection counts as issued by that
person: they see and disconnect it on Access, an administrator sees all. Disabling
the person's account revokes every connection and key they issued — their agents' too:
a connection cannot renew itself and has to be signed in anew once the account is back.
Harnesses without OAuth, and a journal watcher running between sessions, use a key the
person issues to their agent on Access. What the proxy must do:
send the whole MCP host to port 8100, not only
/mcp: sign-in also uses/.well-known/…,/register,/authorize,/tokenand/oauth/…at the root of the host named inTRACKER_MCP_PUBLIC_URL;keep the client's
Hostheader. Caddy does; nginx needsproxy_set_header Host $host;. With sign-in on, MCP answers only on the host ofTRACKER_MCP_PUBLIC_URL, and a request under another name gets421.
Over plain http beyond localhost there is no OAuth at all — agents connect with a key
from Access. On a machine of your own (sign-in off, ports on localhost) an agent is
signed in at once, without a page: Claude Code as claude, Codex as codex, any other
client as agent, each created when first needed.
Name your proxy. Sign-in tells guessers apart by address (see Guessing below). Behind
a proxy every request arrives from the proxy, so the board must be told which address is
the proxy; only then does it take the browser's address from the proxy's
X-Forwarded-For. From anyone else that header is ignored — anybody can write it. Add to
~/casefile/.env:
CASEFILE_TRUSTED_PROXIES=172.18.0.1 # the address the board sees the proxy come fromand run docker compose up -d. That address is not the proxy's own: it is whatever
Docker shows the board. With the proxy on the same machine and CASEFILE_BIND=127.0.0.1,
it is the gateway of the installation's network, on Linux and Docker Desktop alike:
docker network inspect casefile_default -f '{{range .IPAM.Config}}{{.Gateway}}{{end}}'To check, run docker compose logs ui: each request line starts with the address it came
from — with the proxy named, the browser's; without, the proxy's. Several proxies or a
network go comma-separated (172.18.0.1,10.0.0.0/8). The network gets its address when it
is created, so after docker compose down check the gateway again. Keep
CASEFILE_BIND=127.0.0.1 behind a proxy: Docker Desktop shows every connection to a
port published to the network as 192.168.65.1, so naming that address would let anyone
claim any address. Without this line nothing breaks, but everyone behind the proxy shares
one address — and one guesser holds everybody at "try again later" again.
What else to know:
No sign-in, no network. With
CASEFILE_BINDbeyond localhost and sign-in off, the board refuses to start instead of handing the administrator key to the whole network;docker compose logs uisays why.Sessions. A sign-in lasts 7 days (
TRACKER_SESSION_HOURS). A session is a token with a deadline, kept in the database: restarting the installation does not end it. Sign out revokes it at once, a changed or reset password ends the person's other sessions, and disabling an account revokes all its sessions and every connection and key the person issued to their agents: enabling it again brings none of them back.Guessing. Wrong passwords are counted per address and per email, within a minute. After 5 from one address, sign-in answers "try again later" to that address — the right password included — until the minute has passed; after 5 for one email, from wherever they come, that email waits the same way. Other people sign in as usual. On top of that the whole installation takes at most 100 wrong passwords a minute, to keep guessing from burning the processor; guessing spread over many addresses and many emails that reaches it keeps everyone at "try again later" for as long as it goes on (an IPv6 client counts as its whole
/64). Open sessions and the agents, which use tokens, are not affected. A client is the address that opened the connection, or the one your named proxy reports (above). Docker Desktop hides the address of every connection from the network behind one of its own, so there the board tells clients apart only behind a proxy on the same machine.
Under the hood
Python 3.14 · FastAPI · PostgreSQL · MCP over streamable HTTP · React 19 · Vite · Tailwind. The backend sits at the repository root, the web UI in ui/. The web UI speaks English and Russian and the agent-facing texts are in Russian for now; design decisions live in the maintainer's tracker, so ask in an issue.
Contributing
Issues and pull requests are welcome — start with CONTRIBUTING.md. Commits need a sign-off (git commit -s): it certifies you have the right to submit the code, and CI checks it.
License
Hiring? I built Casefile and would be glad to hear about roles at Anthropic or OpenAI — reach me through GitHub.
Available Tools
30 toolsadd_entryAdd case entryA
Files an entry without payload: a decision, attempt, finding, artifact, remark, acceptance or note.
Entries are immutable: no call edits or deletes one, and a mistaken entry is
corrected by a new entry that references it in refs. Summaries, questions,
answers, verdicts and resolutions have their own tools: add_summary, ask,
answer, add_verdict, resolve.
An empty title is refused with entry_fields_invalid, which lists the fields.
Inputs: key (required), type (required), title (required), body, +2 more. Returns: A filed entry, by its address rather than its content; the entry in full is ret…
add_project_entryAdd project entryA
Files an entry in a project's case: a decision, finding, artifact or note that concerns the project rather than one of its tasks.
The entry number counts inside the project, and TRK#7 addresses the entry from
refs of any task or project case. Like a task entry filed by add_entry, a
project entry stays as filed. A task token files project entries as it files
task entries.
A project decision is in force until a later decision names it in supersedes;
no entry changes, and the status is computed on read. Withdrawing a decision with
no replacement is a decision too, one that supersedes it.
An empty title, or a reference to a missing entry, task or project, returns
entry_fields_invalid naming the offending fields.
Inputs: key (required), type (required), title (required), body, +3 more. Returns: A filed project case entry, by its address rather than its content; the entry i…
add_summaryAdd summaryA
Files a summary: the handover note of a case, in four parts, none of them empty
(entry_fields_invalid lists the empty ones).
A significant step is a decision made, a finished part of the work, a failure that changes the plan, or any point where a colleague would need an explanation of where the work stands.
Its index title is the first line of done, returned in the response. The final
summary, with unmeasured, is filed by close_task.
Inputs: key (required), done (required), blockers (required), next_step (required), +2 more. Returns: A filed entry, by its address rather than its content; the entry in full is ret…
add_verdictAdd verdictA
Files the outcome of one review check, as run by the task's assignee within the current pass.
A pass starts with each entry into in_progress, a return after a wait in
open included. Only verdicts of the current pass count for closing, and the latest
verdict on a check replaces the earlier ones: a failed verdict is filed when
it happens, like a passed one. Verdicts of earlier passes stay in the case
without counting. close_task also takes verdicts, together with the closing.
Inputs: key (required), outcome (required), check_no (required), evidence, +1 more. Returns: A filed entry, by its address rather than its content; the entry in full is ret…
answerAnswer questionA
Answers a question of the same task, or closes it unanswered as withdrawn or
replaced. Any holder of a task token answers, in any task.
The first answer closes the question and later ones add to it; neither a
question nor an answer changes the task status. A withdrawn or replaced question
leaves questions of get_task and the open-question counts, and stays in the
case with this entry; a question that already has an answer is not withdrawn.
The tracker builds the title from the question reference and the outcome.
Inputs: key (required), question_no (required), body, outcome, +2 more. Returns: A filed entry, by its address rather than its content; the entry in full is ret…
archive_projectArchive projectA
Archives a project with a reason and files an archived entry in its case.
The project and its tasks freeze as they are: statuses stay, open tasks need no
closing. From then on any change in the project or its tasks — a new task, an
entry, a transition, an edit, an attribute, a new link — is refused with
project_archived, until restore_project. The one change still accepted is
unlink of a link with its task: an open archived task keeps blocking its
blocked_by tasks and holding its parent until the link is removed. Reads work
as before.
An already archived project is refused with project_archived.
Inputs: key (required), reason (required). Returns: Project after archiving or restoring, by the entry that records it; the entry i…
askAsk questionA
Files a question to registry participants. The tracker delivers nothing: an addressee sees the question when reading the feed or their inbox.
A question stays open until an answer with its number is filed in the same
task; it counts toward open_questions, and with blocking toward
open_blocking_questions.
A blocking question is the carrier of a wait on its addressee or on an event
outside the tracker that the addressee learns of; any participant files the
answer. The answer to the last such question makes a task in open a candidate
again, with no status move.
What the cases of the parent, its ancestors and sibling tasks already record is
readable through get_task and read_entries, without a question.
Inputs: key (required), title (required), blocking (required), addressees (required), +2 more. Returns: A filed entry, by its address rather than its content; the entry in full is ret…
close_taskClose taskA
Closes a task: files the given entries, then the verdicts, then the final
summary, and moves the task to done, all in one transaction. It is the only
way into done.
A refusal of any part files nothing and leaves the status as it was. The exit
conditions are checked after filing: a latest verdict other than failed on
every review check within the current pass (checks_not_passed), closed
children (task_has_unclosed_children), the task in in_progress
(transition_not_allowed). An empty summary part is refused with
entry_fields_invalid.
A latest verdict partial or unverifiable on any check adds a service entry
warning after the verdicts, naming those checks and outcomes. The task then
enters done with the open_warnings feature at 1 until an acceptance or a
remark is filed after the warning.
For a parent task the final summary covers the whole work: the children's results are in their own closing summaries.
Tasks this one blocked (blocks) lose the blocked feature, without an entry
in their cases.
Inputs: key (required), summary (required), entries, verdicts, +1 more. Returns: Closed task: key, new status and version, and every entry the call filed.
create_projectCreate projectC
Creates a project with a key, a title and a description.
Inputs: key (required), title (required), description, idempotency_key. Returns: Project key in its stored, upper-case form; the project in full is returned by…
create_taskCreate taskA
Creates a task in backlog; a new task starts in no other status.
With parent, the task is born as the parent's child in the same call: the link
files link_added both in the new task's case and in the parent's case, and
parent_entry in the response is the number of the parent's entry.
A child task takes a part of the parent's work when the parent's output falls into separate results, its checks cannot all pass in one pass, the work does not fit one pass, or it depends on something that does not exist yet. These signs appear on entry into the parent and after each attempt.
The response carries the key issued by the tracker. An empty title or
description is refused with task_fields_invalid.
Inputs: title (required), project (required), description (required), parent, +5 more. Returns: Task state after the call and the entries it filed; the card in full is returne…
get_projectGet projectARead-onlyIdempotent
Returns one project by its key: key, title, description, current attribute values, the project decisions in force and the index of the project's case.
The keys of the installation's projects are listed by list_projects.
Inputs: key (required). Returns: Project with its description, attributes and the index of its case.
get_taskGet taskARead-onlyIdempotent
Returns everything about one task in a single call: card, parent and children, links from both sides, the project decisions it relies on, computed features, latest summary, open questions, unresolved remarks, case index and transition targets.
parent and children are fields of their own and are absent from links,
which holds blocks, blocked_by and relates, each named by this task's
role. parent carries the parent's goal, cut when long (goal_truncated); the full
text, its summary and decisions are in the parent's own case.
The summary covers the case up to its own no; entries with a greater no are
returned by read_entries with after_no. The index carries titles only, and
entry bodies come from read_entries.
A remark in remarks changes nothing in the task: it does not block
in_progress, does not change the status and does not unlock the sections. It
stays in remarks and in open_remarks until resolve gives it an outcome.
transitions lists the targets of the transition table from the current status,
not moves checked in advance: sections, summary, verdicts, blockers, blocking
questions and children are checked by the transition call itself. Whether
in_progress is open shows in the blocked and open_blocking_questions
features.
Inputs: key (required). Returns: Everything about one task: card, parent and children, links, features, latest s…
linkLink tasksA
Links two tasks and files link_added in both cases.
kind is the role of key: blocks means key blocks other, and the card
of other shows the link as blocked_by. A link is stored once: the same link
from the other side is refused with link_exists, like a repeat. A link closing
a cycle is refused with link_cycle_detected, a link of a task to itself with
link_self_not_allowed.
parent and child set the hierarchy, shown by get_task in the fields
parent and children rather than in links. A task has one parent: a second
one is refused with task_has_parent, the current parent named in
details.parent.
An open blocker raises the blocked feature of the blocked task and keeps it
out of in_progress with task_blocked.
A closed task (done, cancelled) accepts only relates, the link to a
continuation grown from it; parent and blocks on it are refused with
task_closed. Only a link shows the lineage on the cards of both tasks: a key
mentioned in an entry body or in refs does not.
Inputs: key (required), kind (required), other (required), idempotency_key. Returns: Entries filed by `link` or `unlink` on both sides of the link.
list_participantsList participantsARead-onlyIdempotent
Lists the participant registry: humans and permanent agents, the possible addressees of a question. Temporary agents are not registered and are absent from it.
Inputs: limit, cursor. Returns: items, next_cursor
list_projectsList projectsARead-onlyIdempotent
Lists the installation's projects, one page at a time: key, title and archive
time. A project's description is returned by get_project.
Inputs: limit, cursor, include_archived. Returns: items, next_cursor
move_taskMove task to projectA
Moves a task to another project, recording the move, both keys and the reason
as a moved entry of the task.
The task gets the next number of the new project, or its own earlier key there
when it returns to a project it has been in: a task holds at most one key per
project. The key it leaves goes to previous_keys and keeps addressing the task
in every call that takes a key; no other task ever gets it. Status, sections,
links, parent, children and case stay as they are, and a closed task moves too.
One key moves one task: its children stay in their project.
Moving into or out of a frozen project fails with project_archived.
A list of keys moves each task on its own, in list order, so new numbers follow
that order; each moved task gets its own moved entry with the one reason. The
answer is results, one per listed key, repeats included: moved, already
(the task is in that project already) or error with the code a single move
would give, such as task_not_found or project_archived of the task's own
project. A refusal of one task leaves the others moved. A blank reason, an unknown or
archived target project and a list size out of range refuse the whole call before
any move.
Inputs: key (required), reason (required), project (required). Returns: A key as a string: the task after the move, by the entry that records it; the c…
read_entriesRead case entriesARead-onlyIdempotent
Returns entry bodies of one task's case, with payload, in number order.
Filters combine with and: types=["summary"] gives every summary, after_no
everything filed after the named entry, and both together the entries of those
types filed after it. decision and attempt entries hold the choices already
made and the attempts already tried, failed ones included.
Entries of many cases in one stream, with a wait for new ones, come from
wait_journal.
Inputs: key (required), nos, limit, types, +2 more. Returns: items, next_cursor
read_project_entriesRead project entriesARead-onlyIdempotent
Returns the bodies of a project's case entries, payload included, ordered by entry number.
The project's case holds decisions, findings, artifacts and notes about the
project, and the tracker's own entries about its card. Filters combine with
and, as in read_entries. attribute gives one attribute's history:
attribute_created, attribute_changed, attribute_removed entries with that
name. The case index, titles only, comes with get_project.
Inputs: key (required), nos, limit, types, +3 more. Returns: items, next_cursor
register_participantRegister participantA
Registers a human or a permanent agent. The new participant's token is issued through
the REST API. An existing participant's description is changed by update_participant.
Inputs: kind (required), name (required), description, idempotency_key. Returns: Participant name in its stored, lower-case form; the registry is returned by `l…
remove_attributeRemove attributeA
Removes a project attribute and files an attribute_removed entry in the
project's case with its last value and the reason. The attribute's history stays
in the case; a later set_attribute with the same name creates it anew.
A name that matches no attribute of the project is refused with
attribute_not_found. A task token removes attributes.
Inputs: key (required), name (required), reason (required), idempotency_key. Returns: Removed project attribute, by the entry that records the removal; the entry in…
resolveResolve remarkA
Resolves a remark on a task: its outcome and where the work went.
Any outcome resolves the remark, needs_detail included: the resolution removes
it from open_remarks, while accepted keeps it in remarks_in_work until the
continuation task is closed. Its title is made of the remark reference and the
outcome.
Inputs: key (required), outcome (required), remark_no (required), body, +2 more. Returns: A filed entry, by its address rather than its content; the entry in full is ret…
restore_projectRestore projectA
Brings an archived project back: files a restored entry carrying the reason in
its case, and its tasks resume where the archive left them.
A project that is not archived is refused with project_not_archived.
Inputs: key (required), reason (required). Returns: Project after archiving or restoring, by the entry that records it; the entry i…
search_tasksSearch tasksARead-onlyIdempotent
Searches tasks by a query language string, by separate conditions, or by both.
Conditions from both sources combine with and and give the same result as one
string of the same meaning; no condition at all selects every task of the
projects that are not archived. A task of an archived project is found only when
the search names it with = or in: its project in project, the task itself in
key, its parent in parent or a root of its subtree in under. Rows are
ordered by sort, by key when it is left out. A long text is cut at the
installation limit and marked by <field>_truncated and <field>_length; one
task in full, with its case and links, is returned by get_task.
An unknown field, operator or value is refused with search_field_unknown,
search_operator_not_supported or search_value_invalid, the allowed values
listed in details.
Inputs: key, sort, text, limit, +16 more. Returns: items, next_cursor
set_attributeSet attributeAIdempotent
Sets the value of a project attribute: a reference fact of the project such as its repository or main branch. One call both creates and changes; which entry it files follows from the attribute's state.
No attribute with this name, ignoring case: the attribute is created and an
attribute_createdentry is filed; the reason is optional.An attribute with another value: the value changes and an
attribute_changedentry is filed with the previous value; the reason is required.The same value: nothing changes and nothing is filed.
The name keeps the spelling it was created with; another spelling addresses the
same attribute and does not rename it. Attributes carry no types and no search:
the tracker stores the text and acts on none of it. A task token sets
attributes.
Inputs: key (required), name (required), value (required), reason, +1 more. Returns: Project attribute after the call; the project with all attributes is returned b…
transitionChange task statusA
Moves a task to another status along the fixed transition table.
Refusals: leaving in_progress without a summary filed since the last entry
into it — summary_required; entering in_progress without an assignee —
assignee_required, by anyone but the assignee — assignee_mismatch (assignee
and caller signature in details), with an open blocker — task_blocked, with
an unanswered blocking question — task_has_open_blocking_questions (question
numbers in details.questions); open with incomplete sections —
task_sections_incomplete; cancelled with open children —
task_has_unclosed_children; done — closing_not_a_transition, since a task
is closed by close_task; a move outside the table — transition_not_allowed,
the allowed targets in details.allowed.
A task waiting in open stays there when its carrier closes: neither an answer
nor a closed blocker moves it, and it becomes a candidate again with no
status_changed entry. Each entry into in_progress starts a new pass of the
task.
cancelled takes no verdicts. It clears the blocked feature of the tasks this
one blocked (blocks), with no entry in their cases.
The response names the new status and version and the number of the filed
status_changed entry.
Inputs: to (required), key (required), reason. Returns: Task state after the call and the entries it filed; the card in full is returne…
unlinkUnlink tasksA
Removes a link and files link_removed in both cases.
A link is removed from either side and under either name of its kind: blocks
from TRK-1 to TRK-7 and blocked_by from TRK-7 to TRK-1 are the same
link. On a closed task parent and blocks stay (task_closed), relates is
removed. A link that does not exist is refused with link_not_found.
Inputs: key (required), kind (required), other (required). Returns: Entries filed by `link` or `unlink` on both sides of the link.
update_participantUpdate participantADestructiveIdempotent
Changes a participant's description. Name and kind never change: the name signs entries already filed. The previous description is not kept.
Inputs: name (required), description (required). Returns: Participant name in its stored, lower-case form; the registry is returned by `l…
update_projectUpdate projectAIdempotent
Changes a project's title and description; a field left out stays. The key never
changes. Each changed field files a field_changed
entry with the previous and the new value in the project's case;
a value equal to the current one files nothing.
Inputs: key (required), title, description. Returns: Project key in its stored, upper-case form; the project in full is returned by…
update_taskUpdate taskAIdempotent
Changes the given fields of a task; fields left out stay as they are.
Title, description and sections are fixed from open on. A task past backlog
has them edited by a return to backlog through transition with a reason,
this call, and a move forward again to open and in_progress.
Each changed field files section_changed or field_changed, an assignee
change files assignee_changed. An edit of one check names its number, and the
earlier verdicts on that check become outdated.
Inputs: key (required), changes (required), version. Returns: Task state after the call and the entries it filed; the card in full is returne…
wait_journalWait for journal entriesARead-onlyIdempotent
Returns journal entries after the sequence number after, waiting for new ones.
The journal is every case entry of the installation in one stream, task cases
and project cases alike, in seq order; task, project and types narrow
it. The call returns as soon as a matching entry appears, and after timeout
seconds at the latest. The next call
continues from the seq of the last entry received. With types=["answer"] and
task, one call covers an answer expected within timeout.
Entries already filed in one case, by number, are returned by read_entries and
read_project_entries.
Inputs: task, after, limit, types, +3 more. Returns: items, next_cursor
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 30 tools
Each tool targets a clearly distinct resource and action, with explicit boundaries between similar operations (e.g., add_entry vs add_summary vs add_verdict, transition vs close_task). The descriptions explicitly state which tool to use for which purpose, preventing misselection despite the large number of tools.
The vast majority of tools follow a consistent verb_noun pattern (e.g., create_task, get_project, read_entries). A few tools use single verbs (ask, answer, resolve, link, unlink) and one uses a noun/verb (transition), but these are conventional and still readable.
With 30 tools, the set is on the heavy side for an MCP server, though the domain is complex and many tools have specialized roles. Some consolidation could be possible (e.g., entry-filing tools), making the count borderline rather than clearly appropriate.
The tool surface covers the full lifecycle of tasks, projects, entries, participants, attributes, links, and a journal stream. No obvious gaps exist for the stated purpose; operations like archive/restore, attribute management, and entry reading are all present.
Maintenance
Related MCP Connectors
Shared task layer for AI agents: tasks, handoffs and decisions that persist across tools.
Shared project context for AI agents and teams: docs, tasks, and messages that stay current.
Project management for AI agents: tasks, docs, decisions and time in one shared team context.
Task management for teams building with AI agents. Agents claim tasks and report progress.
Related MCP Servers
- AlicenseBqualityDmaintenanceEnables AI agents and humans to collaboratively plan and manage tasks with a shared kanban and dependency graph, all stored locally.3129 npm82MIT
- FlicenseNot gradedqualityBmaintenancePersistent memory and task coordination for AI coding agents. Tracks sprint items, logs tasks, manages session handoffs, and surfaces HITL requests so you stay in control across single or parallel Claude Code, Codex, Cursor, and Windsurf sessions.-
- AlicenseAqualityCmaintenanceShared memory and handoff hub for AI agents, enabling seamless context transfer between sessions with token-budgeted resumes and automatic handoffs.105 npmMIT
- AlicenseAqualityCmaintenanceEnables AI agents to manage a task board by investigating needs, creating well-specified tasks, and executing them end-to-end.61MIT