idea-base-mcp-server
OfficialAn MCP server for IDEA Base project management: manage projects, tasks, sprints, time tracking, products, verification, and support from AI tools.
Projects: list, create, update, get details, and filter by status or parent/sub-project.
Tasks: list, create, update, search, change status, manage assignees, dependencies, subtasks, milestones, and GitHub issue/PR links.
Time tracking: start, pause, resume, and stop measured work sessions; log measured time; quick-create completed tasks with time.
Activity & audit: add work notes, comments, and set resume context.
Verification: run AI verification against acceptance criteria/PR diff and record feedback.
Sprints: list, get, create, close, add/remove tasks, and manage sprint cadence.
Products: list, create, get details, and link projects to products.
Support: file IDEA Base bug reports through the Help Desk.
Links tasks to GitHub pull requests, fetches PR diffs for AI verification, and matches GitHub CI check/workflow events back to task CI status.
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., "@idea-base-mcp-server List my projects"
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.
@idea-base/mcp-server
MCP (Model Context Protocol) server for IDEA Base — AI-powered project management. Manage projects, tasks, and time tracking directly from Claude Code, Cursor, or any MCP-compatible AI tool.
Quick Setup
1. Get your API key
Sign in to IDEA Base, go to Settings > API Keys, and create a key.
2. Add to your MCP config
Claude Code (~/.claude/claude_desktop_config.json):
{
"mcpServers": {
"idea-base": {
"command": "npx",
"args": ["-y", "@idea-base/mcp-server"],
"env": {
"IDEA_BASE_API_KEY": "ib_your_api_key_here"
}
}
}
}Cursor (.cursor/mcp.json in your project):
{
"mcpServers": {
"idea-base": {
"command": "npx",
"args": ["-y", "@idea-base/mcp-server"],
"env": {
"IDEA_BASE_API_KEY": "ib_your_api_key_here"
}
}
}
}Or via Claude Code CLI:
claude mcp add idea-base -- npx -y @idea-base/mcp-server \
--env IDEA_BASE_API_KEY=ib_your_api_key_here3. Start using it
Ask Claude to manage your projects:
"List my projects"
"Create a task in project 1: Implement login page"
"Log 2 hours on task 42 — built the auth flow"
"What tasks are in progress?"
Related MCP server: wootech-jira-mcp
Available Tools
Projects
Tool | Description |
| List all projects with task counts and progress |
| Get project details and statistics |
| Create a new project or sub-project |
| Update a project name, description, status, parent (re-parent, or |
| List a product's sprints with real task counts and its cadence |
| One sprint: tasks, counts, commitment ledger; optionally candidates + blocked work |
| Open a sprint — next on the cadence, or an explicit window |
| Commit tasks (refuses a task already in another sprint) |
| Take tasks out of a sprint |
| Close it; unfinished work carries forward. Irreversible |
| Read a product's sprint rhythm |
| Set length, start weekday, carry-over, auto-open |
Slash commands (MCP prompts)
Typed directly in Claude Code. The idea-base part is whatever you named the
server in your MCP config.
Command | What it gives you |
| A sprint laid out to work: goal, window, days left, and items split into assigned-to-you / unassigned / blocked-with-reason. Omit the id for the sprint covering today. |
| Committed vs completed, what carried and how often, from the commitment ledger written at close. |
| Cadence, the next window, the eligible pool, and what is held back as blocked. |
| Whether this project emits session tracking, every missing piece with the literal text to add, and a live check that a heartbeat actually lands. Writes nothing. |
| What is wired on this machine — each capability on, off, configured-but-not-firing, or undetermined with the reason. Read-only. |
| The installed version, the latest published one, and the one command that updates both the package and what it installed here. Reports; the command does the work. See Updating. |
Tracker text in these results is fenced as data, and item lists are capped with the true total printed.
Session tracking
With it installed, a long-running agent session appears on the dashboard's Agents Working Now board while it works and leaves within seconds of ending. Without it the board is empty, and there is no way to tell that from "no agents are running" — which is the failure this exists to prevent.
npx @idea-base/mcp-server install # DRY RUN — prints the plan, writes nothing
npx @idea-base/mcp-server install --apply --actor <agent-user-id>
npx @idea-base/mcp-server doctor # does a heartbeat actually land?
npx @idea-base/mcp-server features # the inventory, read-only
npx @idea-base/mcp-server@latest update # move an existing install forwardAlready installed? Updating is the one you want — install leaves
an existing heartbeat.sh alone on purpose, so it will not move an older one
forward.
install is safe to run as often as you like. It merges rather than
overwrites: your other tools' hooks, your permissions, your env and
everything else in .claude/settings.json are preserved, and a second run
leaves the file byte-identical. If it finds the same heartbeat hook registered
twice it collapses it to one — a duplicate heartbeat double-writes. A
settings.json it cannot parse is refused, never rewritten: a file that
will not parse is not an empty file, and rebuilding it would delete every hook
you have.
It installs three hooks — SessionStart, PostToolUse, SessionEnd — plus
the script they call, at .claude/hooks/heartbeat.sh. The script is written
before the hooks that reference it, because a hook pointing at a missing
script fails closed and can deny Bash and Edit, including the edit needed
to repair it.
--actor is not optional in practice, and is never guessed
The API key belongs to a person. Without env.IDEABASE_ACTOR every heartbeat
an agent posts is attributed to that person, and because the board filters to
agents, the session is filtered off it entirely.
It is never defaulted, deliberately:
| Result |
unset | a row is written, attributed to the key's human owner. Mislabelled, but visible and fixable. |
an id that does not exist |
|
an id belonging to a human |
|
A wrong guess lands in the invisible row. So install asks for it rather than
inventing one.
Verifying, and what "verified" means
doctor posts one labelled probe and reports the server's own verdict, because
POST /api/heartbeat answers HTTP 202 with {"recorded":false} when it
discards a ping — a success status for "I threw this away". Exit status:
Exit | Meaning |
| recorded (or heartbeats are deliberately switched off) |
| the endpoint refused or skipped it |
| could not get as far as asking |
1 and 2 are kept apart on purpose: "I asked and was told no" and "I never
managed to ask" need different fixes.
A session with no task is untracked, by design. The hook sends nothing
unless the session has declared what it is working on — normally by calling
start_working. Nothing about your branch names or directory layout affects
this. A hub on main, an exploratory session, anything not working a task: all
correctly absent, and doctor says which.
What this does not do
It never changes a task's status. A session ending marks the session finished; deciding a task is done is a judgment with a verification gate in front of it.
These three hooks are Claude Code mechanisms. Nothing here transfers to
another agent harness — POST /api/heartbeat is not Claude-Code-specific, so
you can wire it to any session lifecycle hook yourself, but there is no
packaged install for another harness today.
Requires curl on PATH, a write-scoped API key, and Node 20+.
IDEABASE_HEARTBEAT=off disables emission entirely.
Tasks
Tool | Description |
| List tasks for a project (filter by status). Compact rows by default ( |
| Get task details, acceptance criteria, and time entries |
| Create a task with title, description, estimate, priority, start/due dates, assignee; |
| Update task details, priority, start/due dates, assignee; |
| Change task status (todo/in_progress/blocked/done) |
| Search tasks across all projects, ranked title-first then description then recency. Compact rows by default; filter by |
| Create + complete + log MEASURED time in one step; |
Time Tracking
Tool | Description |
| Hand-log measured time against a task. Rarely what you want — see below. Refuses a negative entry, and refuses an agent entirely on a task that already has measured time |
| Open a timed work session on a task, and mark yourself actively working (surfaces the saved resume context + recent work notes so a cold session re-orients). Calling it twice never opens a second session — it resumes or reports the one you have |
| Pause the open session without ending it, with a required |
| Close the pause and carry on in the same session |
| Close the session with a UTC end instant, write the task's time entry from the measured duration, and drop the active flag (optionally capture a |
stop_working logs your time. You type no number.
An agent has no clock, so a duration it fills in by hand is a guess about how long the work felt. Closing a session writes the task's time entry from the session's measured worked duration — computed by the database from the recorded instants — and the response tells you the entry it wrote. Each entry records how its number was arrived at:
| Meaning |
| written by |
| somebody supplied the number by hand |
(unset) | unknown — the entry predates provenance being recorded. Unknown is not measured |
So: start_working, work, stop_working. Reach for log_time only for work no
session measured, and say in the notes why there is no measurement. Two refusals
you will meet if you ignore that:
an agent hand-logging on a task that already has measured time is refused whatever the amount — the measurement exists, so there is nothing to supply;
negative minutes are refused on every path. Correct the original entry rather than filing an offsetting one: a correction stored as a second row makes the total a chain, and a reader entering it at the wrong row gets a wrong number with nothing to signal it.
An estimate is a different field — estimated_minutes on the task. An estimate
is a plan and is allowed to be wrong; logged time is a measurement and is not.
A session under half a minute rounds to zero and logs nothing, rather than being rounded up to a minute. That is deliberate — do not then log it by hand.
The pause reason decides the number. start_working records a UTC start
instant, stop_working a UTC end instant, and each pause in between records both
its boundaries plus why work stopped:
| Effect on worked time |
| Excluded. A person has to act before you can continue |
| Counted. A sub-agent blocked on another active sub-agent is still working |
| Counted |
Only waiting on the human is not working. Use pause_working rather than
stop_working whenever you intend to carry on — stop_working ends the session
and the reason is lost.
Activity & Audit Trail
For AI agents, these leave a durable trail of what was done and why on each task — so a future session (or a human reviewer) can see the reasoning, not just the final state.
Tool | Description |
| Append a timestamped progress note to a task's activity log (append-only journal) |
| Add a comment to a task's discussion thread (customer-visible by default) |
| Overwrite the task's pinned "where I left off" block, read first on |
Products
Tool | Description |
| List products (top-level containers) |
| Get product details with linked projects |
| Create a new product |
| Link a project to a product |
Support
Tool | Description |
| File a bug report against IDEA Base itself through the existing Help Desk intake ( |
Environment Variables
Variable | Required | Description |
| Yes | Your API key from Settings > API Keys |
| No | Custom API URL (default: |
Real-time Notifications
When you use the MCP server to update tasks or log time, changes are broadcast to all connected users. Team members viewing the project in their browser see live toast notifications — when Claude updates a task, everyone sees it immediately.
Security
All data access is scoped to your account via API key
API keys support read/write permissions
No data is stored locally — all operations go through the IDEA Base API
Cross-account access is blocked server-side
Rate limited per API key
Development
# Run the server directly
IDEA_BASE_API_KEY=your_key npm start
# Watch mode
IDEA_BASE_API_KEY=your_key npm run dev
# Live integration test — spawns a real server (this checkout, or a
# published version via --cmd) and drives it against the real API.
# Creates and deletes its own scratch tasks; see test/live-mcp.test.mjs.
IDEA_BASE_API_KEY=your_key node test/live-mcp.test.mjs
IDEA_BASE_API_KEY=your_key node test/live-mcp.test.mjs --cmd "npx -y @idea-base/mcp-server@2.5.0"Updating
One command, run in the repository you want tracked:
npx @idea-base/mcp-server@latest update@latest is the part that does the npm work — npx resolves and fetches the
current version in order to run it. The command then:
Refreshes the files the package installed here. Today that is
.claude/hooks/heartbeat.sh.installdeliberately skips a script that is already present (an installer that clobbers on every run is worse), which means an install made at an older version stayed at that version with nothing saying so. This is the command that moves it forward.Re-asserts the three hook registrations, merging exactly as
installdoes — your other tools' hooks, yourpermissions, yourenvand everything else in.claude/settings.jsonare preserved, and a second run leaves both files byte-identical.Runs
doctorand prints its verdict, so you finish knowing whether tracking actually works rather than whether files were copied.
A script you edited yourself is never overwritten. The decision is made by
sha256 against the list of bodies this package has ever published
(src/assets/heartbeat.provenance.json, known_shas):
Your | What happens |
the current version | nothing is written |
an older version we shipped | refreshed, with both sha256s printed |
absent | written |
anything else | left exactly as it is, and reported |
--dry-run prints all of the above and writes nothing.
Then restart. A running MCP server keeps the code it started with, so none of this reaches the session you ran it from — start a new session, or restart your editor. The command says so on every run; it does not pretend to restart anything for you.
Knowing you are behind
A running server tells you once, on the first tool call of the session, when a
newer version is published — and tells you the same command. /idea-base:update
reports the installed and latest versions on demand.
The check is one unauthenticated GET to the npm registry for this package's
latest dist-tag: no credential, no request body, and nothing about you, your
project or your data. It is fired once at startup and never awaited on the
tool path, so an unreachable registry costs no tool call any time at all — it
simply says nothing, because "I could not ask" and "you are up to date" must not
look the same. Set IDEA_BASE_UPDATE_CHECK=off to disable it entirely.
Releasing
Merging to main does not ship anything. Every consumer runs whatever
npm view @idea-base/mcp-server version currently answers — not whatever is
on main. A global npm install -g (several deployments run this way) is
frozen at that version until someone explicitly re-installs it; nothing about
publishing a new version moves it. GH #410 is seventeen days of merged work
(update_task(parent_task_id), update_task(github_issue_id)) that sat
unreleased because nothing said the registry had fallen behind main.
To actually ship a change:
Bump
versioninpackage.json,npm install --package-lock-only.Commit, push,
gh release create vX.Y.Z—publish.yml(Trusted Publisher, Node 24, no tokens) publishes to npm on the release event.Then check who is still on the old version. A global install has no version spec to resolve and will not move on its own — that upgrade is a deliberate decision on each box, not a side effect of publishing.
The .github/workflows/version-drift.yml workflow runs on every push to
main and reports (as a non-blocking warning) when main has commits beyond
the published gitHead — so "merged but unpublished" is visible instead of
being rediscovered by whoever trips over it next.
License
MIT - IDEA Management LLC
Available Tools
36 toolsadd_assigneeA
Assign a user to a task WITHOUT disturbing anyone already assigned. task_assignments holds one row per (task_id, user_id) — POST /api/tasks/:id/assignments upserts exactly that one row and leaves every other assignee alone, unlike update_task's assignee_user_id (which REPLACES the whole set — use that only when you actually want a single owner). This is the fix for the defect where a second MCP assignment silently dropped the first.
Idempotent and is_active-safe: this checks the task's current assignees first, and if the user already holds a row (assigned OR actively working) it does nothing and says so, rather than re-POSTing. That matters because the REST endpoint's upsert OVERWRITES is_active on conflict — a blind re-assign of someone with a running timer (is_active=1, set by start_working) would silently stop their clock. Skipping the no-op re-POST is what keeps assignment and active-work presence independent.
Rejects a user who is not a member of this account (404, surfaced as an error) rather than creating a dangling assignment.
Inputs: task_id (required), user_id (required).
add_commentA
Add a comment to a task's discussion thread. Unlike work notes (progress journal), comments are for communication and are customer-visible by default.
Inputs: comment (required), task_id (required), is_internal.
add_tasks_to_sprintA
Commit tasks to a sprint. Each task is checked on its own and the result says which were added and which were refused and why — a task already in another sprint is REFUSED, not moved, because a task belongs to at most one. Tasks must belong to a project under the sprint's product.
Inputs: task_ids (required), sprint_id (required).
add_work_noteA
Append a timestamped work note (status update) to a task's activity log. Use this to record progress so a future session can pick up where you left off (e.g. "finished auth handler, tests green, next: wire the callback"). Notes are append-only and never overwrite each other.
Inputs: note (required), task_id (required), is_internal.
close_sprintA
Close a sprint. Unfinished work moves into the next sprint and its carry count increments (unless the product's cadence has carry-over off, in which case it is released); finished work stays attached. Every task held is written to the commitment ledger first, so what the sprint committed to survives the carry. This is an action, not a status edit — closing is irreversible.
Inputs: sprint_id (required), carry_to_sprint_id.
create_productA
Create a new product. Products are top-level containers for projects. Every product belongs to a customer (customer_id is required) — use an internal/own-company customer for internal work. Find customer ids via list_products (each shows its customer_id).
Inputs: name (required), customer_id (required), description, github_repo_url.
create_projectA
Create a new project. A TOP-LEVEL project requires product_id; a sub-project (parent_project_id set) inherits its parent's product. Returns the created project with its ID.
Inputs: name (required), product_id, description, parent_project_id.
create_sprintA
Open a sprint on a product. With no dates it opens the NEXT one on the product's cadence, which is the normal case; pass starts_on/ends_on only to set an explicit window. One sprint per product per period — a second for the same period is refused rather than duplicated.
Inputs: product_id (required), goal, name, state, +2 more.
create_taskC
Create a new task in a project. Returns the created task with its ID.
Inputs: title (required), project_id (required), due_date, priority, +13 more.
get_productA
Get details of a specific product including linked projects, team members, and statistics.
Inputs: product_id (required).
get_projectA
Get details of a specific project including name, description, status, and task statistics.
Inputs: project_id (required).
get_sprintA
Get one sprint: its tasks with derived status, counts taken from real task rows, and — for a CLOSED sprint — the commitment ledger saying what it committed to and what became of each item. Pass candidates:true to also get the open work that could be pulled in, and the work being held back as blocked (with the reason).
Inputs: sprint_id (required), candidates.
get_sprint_cadenceA
Get a product's sprint rhythm: length in days, which weekday it starts, whether unfinished work carries over, and whether sprints open unattended. A product with no cadence row still reports the weekly-Monday default, flagged as not configured.
Inputs: product_id (required).
get_taskA
Get detailed information about a specific task including description, acceptance criteria, time entries, and status.
Inputs: task_id (required).
link_project_to_productB
Link an existing project to a product.
Inputs: product_id (required), project_id (required), is_primary.
list_productsA
List all products accessible to the authenticated user. Products are top-level containers for organizing related projects.
Inputs: status.
list_projectsA
List all projects accessible to the authenticated user. Returns project names, IDs, status, and task counts.
Inputs: status, parent_id.
list_sprintsB
List a product's sprints, newest first, with real task counts and its cadence. Sprints are owned by the PRODUCT: the product sets the rhythm and owns the lifecycle, while projects add and remove the work. A sprint can hold tasks from any project under that product, sub-projects included.
Inputs: product_id (required).
list_tasksA
List all tasks for a specific project. Returns compact rows (id, title, status, priority, estimated_minutes, time_spent, due_date, snippet) by default to keep payloads small; pass verbose:true for full rows including description, acceptance_criteria, tags, and assignments.
Inputs: project_id (required), status, verbose.
log_timeA
Log MEASURED time on a task — time you actually observed, never an estimate and never a guess.
IN ALMOST EVERY CASE YOU SHOULD NOT CALL THIS. Use start_working and stop_working instead: stop_working writes the task's time entry itself, from the work session's measured duration, read back out of the database. You have no clock, so a number you type here is a guess about how long something felt.
An estimate goes in the task's estimated_minutes (create_task / update_task), NOT here. Logged time is a measurement; an estimate is a plan. They are different fields and must not be confused — writing an estimate into logged time is what this tool's description used to invite, and an agent did it: 75 minutes logged for work its own sessions measured at 15.7, 14.5 and 8.1.
Use it ONLY for work no session measured — something done away from the keyboard, or work from before you started a session — and say in the notes why there is no measurement. An AGENT logging by hand on a task that already has measured time is REFUSED, whatever the amount. Negative minutes are refused too: correct the original entry rather than filing an offsetting one.
Inputs: minutes (required), task_id (required), date, notes.
pause_workingA
Pause your open work session without ending it, and record WHY. Use this whenever work stops but is not finished — you asked the human a question, you are waiting on another agent, you are blocked on a build.
THE REASON IS THE POINT, and it decides the number: waiting_on_human — you are blocked on a person and CANNOT proceed. The paused interval is EXCLUDED from worked time. waiting_on_agent — you are blocked on another agent or an automated run that is itself active. The interval IS COUNTED as worked time: a sub-agent blocked on another active sub-agent is still working. other — anything else. Counted as worked.
Only waiting on the human is not working. Choose waiting_on_human ONLY when a person has to act before you can continue; if a machine or another agent is doing the work, it is waiting_on_agent.
Resume with resume_working. Do NOT use stop_working for a pause — that ends the session and the reason is lost.
Inputs: reason (required), task_id (required), detail.
quick_logA
Quick workflow for recording work that is ALREADY FINISHED: creates a task, marks it done, and logs time in one call.
minutes must be MEASURED time, not an estimate. This is a hand-entry path — there is no work session behind it — so only use it for work you actually observed the duration of, and prefer start_working / stop_working whenever the work is still ahead of you, because stop_working writes the time entry from the measured session and you type no number at all.
An estimate goes in estimated_minutes, which is a separate optional argument here precisely so the two cannot be the same number by accident. Omit it and it defaults to minutes, as before.
Inputs: title (required), minutes (required), project_id (required), notes, +3 more.
record_verification_feedbackA
Record whether the most recent verify_task verdict on this task was accurate — calibration data, not a correction. Attaches to the latest ai_generations row of type completion_review for this task, so verify_task must have run at least once first. Does NOT change the stored ai_completion_score or re-run any model — it costs nothing (no Anthropic call, just an audit-log row) and is safe to call as often as useful.
Inputs: agreed (required), task_id (required), notes, actual_score.
remove_assigneeA
Remove exactly one user's assignment from a task, leaving every other assignee untouched. DELETE /api/tasks/:id/assignments?user_id=... removes that one task_assignments row outright (both the assignment and any is_active flag on it) — this is a full unassign, not stop_working's "still assigned, no longer active" (use stop_working to end a work session without unassigning).
Inputs: task_id (required), user_id (required).
remove_tasks_from_sprintA
Take tasks out of a sprint. The tasks themselves are untouched — the sprint is the time box, not the owner of the work.
Inputs: task_ids (required), sprint_id (required).
report_bugA
File a bug report against IDEA Base itself as a support ticket, through the existing Help Desk intake (POST /api/tickets, category "bug") — not a second intake path. Use this when something in IDEA Base is broken, not for a customer project's own bugs. Staff see it in the Help Desk queue.
Inputs: subject (required), priority, project_id, description.
resume_workingA
Resume your paused work session — closes the pause by recording the UTC instant you came back, so the paused interval has both boundaries and can be measured. The session itself was never closed and keeps its original start.
Fails if the session is not paused, rather than pretending to resume something. start_working also resumes a paused session, so use whichever reads better; this one does not re-fetch the orientation block.
Inputs: task_id (required).
search_tasksA
Search for tasks across all projects by title or description. Returns compact rows (id, title, status, priority, project_id, project_name, product_name, customer_name, estimated_minutes, time_logged, due_date, snippet) ranked with title matches first, then description matches, then recency. Pass verbose:true for full rows.
Inputs: query (required), limit, status, verbose, +3 more.
set_resume_contextA
Set (overwrite) the task's resume context — a single pinned block describing the current state, what is done, what is next, and where to look. This is read first when a work session restarts cold (e.g. via start_working) so you can re-orient immediately. Update it at the end of a work session.
Inputs: context (required), task_id (required).
set_sprint_cadenceA
Set a product's sprint rhythm. "Mon-Sun weekly" is length_days 7 + start_weekday 1; a Wednesday fortnight is 14 + 3. Changing it does not move sprints that already exist — it applies from the next sprint opened. Setting a cadence is also what opts a product into having sprints opened and closed on the schedule.
Inputs: product_id (required), enabled, auto_create, length_days, +2 more.
start_workingA
Start work on a task. Two things happen: you are shown to the team as actively working on it (and a todo task moves to in_progress), and a TIMED WORK SESSION is opened with a UTC start instant, so how long the task was actually worked becomes measurable instead of guessed.
Calling it a second time on the same task NEVER opens a second session. If your session is paused it is resumed; if it is already running you are told so, with how long it has been going. The response always says which of the three happened.
It also orients a cold session: the response carries the task's resume context and its recent work notes, so read the whole thing before you start. Pair it with stop_working — which is what LOGS the time, from the measured session, with no number typed by you — and use pause_working when you have to wait on something.
Inputs: task_id (required).
stop_workingA
Stop working on a task: close your open work session with a UTC end instant, and drop the "actively working" flag (you stay assigned).
THIS IS WHAT LOGS YOUR TIME, and you supply no number. Closing the session writes the task's time entry from the session's measured worked duration — computed by the database from the recorded start and end instants, minus any waiting_on_human pause — and the response tells you the entry it wrote. Do NOT call log_time afterwards: the time is already recorded, and a hand entry on a task that already has measured time is refused. A session shorter than half a minute rounds to zero and logs nothing rather than being rounded up, and the response says so.
Requires an open session — if you have none this FAILS and writes nothing, rather than inventing a session with no start. Call start_working first.
This ENDS the session. If you are only waiting on something and intend to carry on afterwards, use pause_working instead: a pause keeps the session open and records why you stopped, which is what decides whether the interval counts as worked time.
Optionally capture your state on the way out: pass note to append a work note and/or resume_context to overwrite the resume block, so the next session can pick up where you left off.
Inputs: task_id (required), note, resume_context.
update_projectA
Update a project: its name, description, status, or where it sits in the hierarchy. Omitted fields are left exactly as they are. Set parent_project_id to re-parent the project, or to null to DETACH it into a top-level project; a detached project keeps the product it had inherited, so product_id is only needed to move it to a DIFFERENT product. A sub-project inherits its parent's product, so re-parenting under a parent in a different product moves the project there too. The API refuses a move that is circular, crosses accounts, or exceeds the plan's nesting depth or sub-project count, and says which.
Inputs: project_id (required), name, status, product_id, +2 more.
update_taskC
Update task details like title, description, estimates, or acceptance criteria.
Inputs: task_id (required), title, due_date, priority, +15 more.
update_task_statusA
Change the status of a task (todo, in_progress, blocked, done). This triggers a real-time notification to users viewing the project. Note that a task with subtasks or dependencies also has a DERIVED status which can differ from the one set here — see effective_status / status_reason on get_task.
SETTING "done" IS GATED (GH #387). A task holding any subtask that is not done is REFUSED with 403 OPEN_SUBTASKS, naming the blocking children. The bar is "done": a subtask that is todo, in progress or blocked all refuse it for the same reason. This is not advisory and there is no automated way around it — the override is reserved for a human account owner or admin, and an automated identity is refused it explicitly (403 FORCE_NOT_PERMITTED, reason actor_is_agent). So finish the subtasks; do not attempt to force the parent.
Inputs: status (required), task_id (required), blocked_reason.
verify_taskA
Run AI verification: grades this task's acceptance_criteria against the diff of its linked PR (a Haiku relevance pre-screen, then a Sonnet deep review) and PERSISTS the result to ai_completion_score / ai_completion_notes. This is exactly what update_task_status checks when the task's verification_mode is "ai_review" or "all" — call this to satisfy that gate yourself rather than repeatedly hitting 403 VERIFICATION_REQUIRED with no way through it.
COSTS MONEY — DO NOT LOOP THIS. Every call is metered via recordAIUsage and recorded to ai_generations (Sonnet, large PR-diff context) for audit. It runs FREE while your account is within its plan's included monthly verification cap; once over that cap each call is billed as AI credit overage — 2 credits per call on this account. Calling this repeatedly hoping for a higher score spends real credits for no guaranteed gain; if a genuine score lands below the 0.8 gate, that is a finding about the work, not a reason to retry.
GRADES THE DIFF, NOT THE RUNNING SYSTEM. A high score means the PR's code changes look like they satisfy the criteria on paper — it is NOT proof the deployed/running behavior actually works. Never report this score as end-to-end verification; that is a human's job or an independent task-verify sub-agent's, not this tool's.
Requires the task to already have non-empty acceptance_criteria (set via update_task) and a way to see the work: either the task's own github_pr_url (set via update_task) or a pr_url/work_description passed here. Fails with a clear message naming what is missing rather than grading an empty contract — refusal is free, no credits spent.
Inputs: task_id (required), pr_url, work_description.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 36 tools
Most tools map to a distinct resource+action, and the descriptions go out of their way to separate easily-confused pairs (add_comment vs add_work_note, log_time vs stop_working, add_assignee vs update_task's assignee_user_id). The remaining overlaps are mild: resume_working duplicates start_working's resume behavior by the author's own admission, and log_time / quick_log / stop_working all touch time recording, requiring careful reading to pick correctly.
Nearly every name is snake_case verb_noun with a predictable verb vocabulary (create_/get_/list_/update_/add_/remove_/set_/start_/stop_/pause_/resume_/close_), making resource and action readable at a glance. Deviations like quick_log, report_bug, and update_task_status are still idiomatic and unambiguous within the same convention.
36 tools is heavy and sits above the comfortable band, but the domain genuinely spans products, projects, tasks, sprints, work sessions, assignments, verification, and support, so most tools earn a place. There is still consolidation headroom (log_time vs stop_working, resume_working vs start_working, record_verification_feedback), which pushes it to borderline rather than well-scoped.
Core workflows are covered end to end: product/project/task CRUD-ish surfaces, sprint lifecycle with cadence, measured work sessions, assignments, and AI verification. Notable gaps remain, though: no delete anywhere (tasks, projects, products, sprints), no update_product, no list_comments or way to read a task's comment thread, and no way to list or correct existing time entries other than the initial write.
Maintenance
Related MCP Connectors
Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
MCP Server for Slima - AI Writing IDE for Novel Authors with AI Beta Reader.
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with yo…
Shared memory MCP server for Claude Code, Codex, Gemini CLI and Cursor. Cited, editable.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceA comprehensive MCP server for time tracking, project management, and AI-powered memory storage using semantic search. It enables users to log time, manage client billing, and capture shared or personal ideas through integrated tools and team collaboration features.1MIT
- AlicenseNot gradedqualityDmaintenanceMCP Server to log time and view Jira statistics directly from Cursor IDE.ISC
- AlicenseBqualityFmaintenanceMCP server for Panel Todo — lets AI assistants manage your tasks, issues, and sprints directly in VS Code.388 npmMIT
- AlicenseNot gradedqualityDmaintenanceMCP server that enables AI assistants in IDEs to interact with RefBase webapp for managing conversations, bugs, features, and project context via natural language.7 npm3Apache 2.0