Docs
Agents & MCP
Your agent works the same board you do, through tada's MCP server. This page covers what the server gives it, how its edits and yours stay out of each other's way, and the rules it follows to keep the plan honest. Setting it up is on Quick start.
How an agent works a board
- It reads the board, usually just the In Progress and Planned columns.
- Before starting something, it moves that Story or Task to In Progress.
- As each piece lands it moves the Task along, and it writes what actually shipped into the description.
- Anything it finds along the way, like a bug or a follow-up, goes on the board as a new Story instead of being done silently.
You see each of those edits in your open browser as it happens, with no refresh. Whenever anything on a board is written, by you or by the agent, every open tab updates.
Finding Stories and Tasks
Tools find a Story by its title or its id, like
tada-96. Titles are what an agent reads off the board; the id survives a rename.
Each Task has its own id too, like tada-131.2. It survives reordering and
renaming, so it's the reliable way to point at one Task, in a tool call or in chat.
Lookups also find Stories that have already been filed to the Changelog, so
an agent can still edit or merge one. Only view_board is limited to the active
board.
When you and the agent edit at once
Every agent write checks whether the Story changed since the agent read it. If it did, what happens depends on what changed:
- Different parts of the Story. Say the agent moves a Task while you edit the description. Nothing is contested, so the agent's write goes through, and its reply notes that someone else was editing.
- The same part. Say you both changed the description. The agent's write is refused, and the refusal shows both versions, so the agent (or you, reading along) decides. Neither edit is silently lost.
- The Story was deleted. The agent is told it was deleted mid-write, not that it has the wrong title.
Who changed what
Anything an agent creates, edits, or merges gets the ๐ค ai-generated tag, and the
tag stays even after you edit the Story yourself. Moving a Story or Task between columns
doesn't add it.
For the detail, story_activity_log lists a Story's recent writes, newest first,
each marked with how it arrived:
browser- The web app: you, or an agent driving the browser without saying so.
browser:automated- An agent driving the web app that said so, by opening it with
?automated=1. mcp- An agent using the MCP server.
system- tada's own housekeeping.
It keeps recent history, not a permanent record, and it can answer by id for a Story or Task that has been deleted. Each client reports its own label, so the label says which way a write came in, not who a person is. Don't treat it as proof.
Access
There is no API key. The server acts as the account you name in its config, and every call is checked against that account. Only a board's owner can write to it, and a private board is invisible to everyone else: its tools answer as if it doesn't exist.
The tools
Reading
| Tool | What it does |
|---|---|
view_board | The board grouped by column, with Tasks. Descriptions show as a one-line preview. Optional status and type filters keep a big board's output small. |
get_story | One Story and its Tasks in full, by title or id. |
story_activity_log | A Story's recent writes, newest first, with how each arrived. Works by id for a deleted Story or Task. |
board_url | The link to open the board in a browser. |
Stories
| Tool | What it does |
|---|---|
add_story | Create a Story with a title and, optionally, a type (default Feature), status (default Planned), description, tags, and due date. A Bug with no description gets the Expected/Observed template. |
update_story | Change a Story's title, type, status, description, tags, or due date. |
set_status | Move a Story to another column. |
move_story | Put a Story at a position in its column, where 0 is the top. Order is priority. Pass a status to change column in the same write. |
delete_story | Delete a Story. |
merge_stories | Fold other Stories into one: their descriptions are appended, tags combine, Tasks move over, and the others are deleted. |
Relationships
| Tool | What it does |
|---|---|
link_story | Relate two Stories; by default, mark one as blocked by the other. Refused if it would make a loop of blockers. Two Stories have one relationship at most, and replace swaps it. |
unlink_story | Remove a relationship between two Stories. |
Tasks
| Tool | What it does |
|---|---|
add_subtask | Add a Task to a Story, optionally with a status and due date. The reply gives the Task's id. |
edit_subtask | Rename a Task, or set or clear its due date. |
set_subtask_status | Move a Task to another column. |
move_subtask | Put a Task at a position in its column, optionally changing column in the same write. |
delete_subtask | Delete a Task. |
Boards
| Tool | What it does |
|---|---|
create_board | Create a new, empty board you own, private unless you say otherwise. Its slug is permanent. The server stays on its own board, so the new one needs its own server entry. |
delete_board | Delete this server's board; you type its slug to confirm. Nothing is erased, and restore_board brings it back. |
restore_board | Restore a board you deleted, exactly as it was. |
The rules your agent follows
This section is tada's agent instructions: the AGENTS.md file an agent reads when
it works a board. It's generated from that file, so it says exactly what agents are told.
If a project's .mcp.json wires in the tada MCP server, you can read and
edit that project's plan. Keep it honest โ it's the shared source of truth the
human is watching in the browser. These are the rules.
Always start with view_board
Call view_board before editing so you know the exact story titles (the edit
tools look stories up by title). It shows every card grouped by column, with
tasks. On a large board, pass status (one value or a list, e.g.
status: "in-progress") to scope to just the columns you need and keep the
output small; omit it for the whole board. Notes are shown as a one-line
preview (flagged "full note via get_story" when shortened) โ call get_story
for a story's complete note.
When to update the plan
Finishing a story โ
set_status(title, "done")and, if what shipped differs from the title,update_storythenotewith what actually landed. Future agents read these โ be specific.Starting work on something already on the board โ
set_status(title, "in-progress").Doing work that isn't tracked yet โ
add_storyit first (don't do untracked work silently). Usestatus: "in-progress"if you're starting it now,"done"if you already finished it.Discovering a problem โ
add_storywithtype: "bug",status: "planned".Breaking a big story down โ
add_subtaskfor each piece; move them withset_subtask_statusas you go,edit_subtaskto reword one, anddelete_subtaskto drop one that's no longer needed.Recording what to work first โ
move_subtask(ormove_story) with aposition: 0 is the top of the column, a position past the end lands it last. Order carries priority, so put the sequence in the list itself rather than describing it in the note โ a note saying "do .6 before .3" while the list shows the opposite misleads everyone reading the board. Both tools also take an optionalstatus, so a cross-column move and its position are one write. Each task gets a stable idtada-<story>.<seq>(e.g.tada-131.2) โadd_subtaskreports it, and the set/edit/delete tools accept either that id or the task title (the same dual-address as looking a Story up by id or title). The id survives reorder and rename, so it's the reliable way to point at a specific task in chat.Something changed under you โ a task you saw is gone, a status moved, a note you didn't write appeared โ
story_activity_log(title)before acting on it. It reports each write newest-first with the surface that made it, so a concurrent human edit in the browser reads as exactly that instead of as data loss. Pass the id when the thing has been deleted: a deleted story or task has no title left to look up, and its history is the only record of what it was. This tool exists because an agent once concluded a missing task was a fault, filed a bug, and recreated it โ permanently burning its id โ when the user had simply deleted it in the browser. Ask first; "did you mean to?" is cheap and restoring someone's deliberate deletion is not.The surface is self-reported and unverifiable โ it says which front door a write came through, never who a person is.
browsermeans the web app (a human, or an agent that didn't disclose);browser:automatedan agent that did;mcpan agent over MCP;systemthe app's own housekeeping. Never gate anything on it.You are driving the tada web UI yourself (Playwright, a browser tool) โ open it once with
?automated=1. That makes your writes record asbrowser:automatedfor the rest of the session, so the next reader can tell your edits from the user's. Without it your writes are indistinguishable from theirs โ the server cannot tell the difference, by design.?automated=0turns it off.Starting a project that has no board yet โ
create_board(slug, title). Never write a board to the database by hand: the tool is the only path that sets up the story types, the id counters, and ownership correctly. The board is yours and private by default. Choose the slug with care, because it appears in every story id and can't be renamed. Your current server stays bound to its own--project, so give the new project its own.mcp.jsonentry with--project <slug>, then fully reload the editor window.The user asks to delete a board โ
delete_board(confirm: '<slug>')on the server bound to it. Do it only when asked: it hides the whole board from everyone. It's a soft delete, sorestore_board(slug)undoes it, andrestore_boardwith a slug that doesn't match lists the deleted boards you own.A story already filed to the Changelog still needs editing or merging โ every lookup-by-title tool (
get_story,update_story,set_status,move_story,delete_story,add_subtask/set_subtask_status/move_subtask/edit_subtask/delete_subtask,merge_stories) finds an archived story the same as an active one โ you don't need to know it's archived, just address it by its title or id as usual.view_boarditself only lists the active board, so a title you don't remember from there may still exist in the Changelog;get_storywill find it.Folding duplicate or related stories together (active or in the Changelog) โ
merge_stories(title, others: [...]). The firsttitleis the primary and keeps its own title/type/status; every other story's note is appended (labeled by its title), tags union in, and its tasks move onto the primary โ then the merged-away stories are deleted. Use this instead of manually copying fields over; it's easy to drop a tag or task doing that by hand.
Stories you create, edit, or merge through the MCP tools are automatically tagged
๐ค ai-generated (add_story, content edits via update_story, and
merge_stories; a plain status move isn't tagged). You don't set this โ it's a
record that an agent touched the Story, and a human editing it later won't
remove it. To mark a Story ๐๏ธ ui, ๐งช testing, or ๐ api as well, pass a
tags array to add_story / update_story (e.g. tags: ["ui"]) โ on
update_story it replaces the Story's tags, with ai-generated always kept.
A Story may carry an optional due date: pass due_date as YYYY-MM-DD to
add_story or update_story, and an empty string to update_story to clear it.
It is a calendar day, not a timestamp, and relative dates are refused โ work
out what "next Friday" is yourself and pass the date, since resolving it against
the server's clock would give an answer that depends on where the server runs.
view_board prints a due: line for each dated Story and marks the overdue ones
โ ๏ธ overdue, so you can see what has slipped without knowing today's date.
Tasks carry due dates too โ add_subtask takes a due_date, and
edit_subtask sets or clears one ("" clears, as on a Story). Same rules: a
calendar day, no relative dates. A task may be due after its parent Story;
that overrun is surfaced on the Story card, not refused, so you don't have to
move the Story's date to record a later piece of work.
What not to do
- Don't silently finish a story without moving it to
done. - Don't add vague stories. A title states an outcome, not an activity ("Health bars visible above enemies", not "Work on health bars").
Showing the human the board
If the human wants to watch progress, call board_url and give them the link.
Your edits appear in their open browser as you make them โ no refresh needed.
Terminology: Stories vs. Tasks
- Story โ a top-level card on the board. Has a type (
feature,bug,chore,infrastructure) and a status column. - Task โ a unit of work belonging to a Story, shown inside the Story Detail View. Has its own stable id (
tada-<story>.<seq>, e.g.tada-131.2), a title, and a status (idea/planned/in-progress/doneโ same vocabulary as a Story), but no type. The id is stable across reorder/rename and is how you address a specific task in the subtask tools.
("Task" is unambiguous: the maintenance Story type is Chore, so the word "Task" only ever means a unit of work belonging to a Story.)
Types vs. status
Every story is a Story โ a unit of work with one of four types. Pick the type by the nature of the work, not which part of the app it touches:
feature(โจ) โ a new or changed user-facing capability. Someone can see or use something they couldn't before. e.g. due dates on stories, dependency linking.bug(๐) โ something behaves wrong vs. its intended behavior. A defect or regression. e.g. "can't choose the Infrastructure type when creating a Story."chore(๐งน) โ maintenance / process work with no runtime behavior change: refactors, dedup, cleanup, documentation, developer tooling/skills. If a user couldn't tell it shipped, it's probably a Chore. e.g. de-duplicate a helper, split a test spec, write a skill.infrastructure(๐๏ธ) โ the platform the app runs, ships, or is verified on: hosting, CI, build/deploy, data-migration tooling โ and test coverage (tests are part of the quality platform). e.g. add MCP-server tests, add CI workers, move hosting.
Borderline calls: a pure refactor is a chore (internal, no behavior change);
adding/expanding tests is infrastructure (it hardens the platform, not the
product). When a Story would fit two types, pick the one matching its primary
outcome and note the rest.
Description templates. The card detail lives in the single note field
(markdown). Some types seed a starter scaffold to capture the detail that
matters: a bug seeds Expected / Observed markdown headings โ the
variance that defines the bug. Creating a bug via add_story without a note
seeds that scaffold for you; pass an explicit note to override it.
Each Story can also have Tasks โ use add_subtask to break it down.
- Status is the workflow column:
idea,planned,in-progress,done. Tasks share the same status vocabulary.
Type and status are independent. A bug you're actively fixing is type: "bug", status: "in-progress".
Access to the hosted board
There is no API key. Access is gated by identity: the tada MCP server runs as
a specific owner (--owner-email in the project's .mcp.json), and every read
and write is authorized against that user's board ownership โ so a private board
just works through the MCP tools, with no secret to handle.
A plain curl against a private board will 404 as an anonymous request. That is
the access check working, not a misconfiguration โ read through the MCP tools
instead.
Editing the board without MCP
Don't. The board lives in a database, not in a file you can open โ there is no board file to hand-edit or validate. If the MCP tools aren't available, fix the connection rather than reaching for the data directly.
add_story needs only a title: type defaults to feature and status to
planned.