amazing-marvin-complete-mcp
This server gives AI assistants full MCP access to Amazing Marvin's public API — reading and writing tasks, projects/categories, habits, time blocks, tracking, rewards, reminders, and account data.
Tasks: create, update, complete/uncomplete, delete permanently, set priority/frog, schedule, deadlines, labels, notes, estimates, sections, backburner/orbit, snooze, planned week/month, review dates, and read today's/due/done items.
Structure: create, update, and convert categories and projects, manage hierarchy, colors, icons, labels, and notes; read the full category/project tree.
Habits: list all habits with full documents, view a habit's tracking history, and record or undo check-ins.
Time blocks: read today's time blocks with category/smart-list mapping, and create time blocks (experimental) whose IDs can link tasks to block sections.
Time tracking: see what's being tracked, start/stop tracking on tasks, and read time-tracking history.
Rewards & kudos: get kudos/XP, claim, undo (task-linked only), spend, and reset reward points; get account info including the points balance.
Reminders: list server-side push reminders, set standalone reminders, and delete reminders.
Calendar: create calendar events (experimental; sync depends on the app running).
Account & monitoring: test API connection, get account info, check today's API-call budget via rate-limit status, and get a capability overview without using API calls.
Safety/quality: enforces rate limits globally, validates dates and numeric ranges, avoids known Marvin API quirks (e.g.
#title corruption, shortcut parsing), and documents unsupported operations clearly.
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., "@amazing-marvin-complete-mcp What are my tasks for today?"
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.
amazing-marvin-complete-mcp
An MCP (Model Context Protocol) server for Amazing Marvin with complete coverage of the public API: 50 tools over the 38 endpoints on Marvin's API endpoints page (read 2026-10-12; all but two, see below), a global rate limiter that respects Marvin's documented limits, least-privilege token routing, and MCP tool annotations. As of 1.1.0 every writable field in Marvin's official data model (Tasks and Categories/Projects) is either supported by a tool or explicitly documented as unsupported — see docs/field-reconciliation.md. Every non-obvious behavior claim in the tool descriptions was verified against the live API — the findings are documented below in Marvin API quirks & findings, which may be useful even if you never run this server.
Maintenance status: Bug reports are welcome and appreciated — they help keep this working for everyone. Please note this is a side project maintained when time allows: bug reports get looked at, but response times vary and feature requests are unlikely to be picked up. For installation help, paste this README into your AI assistant — it can walk you through setup and troubleshooting far faster than I can. Provided as-is, without guarantees — it's MIT, fork freely.
Tools (50)
Group | Tools |
Core |
|
Reading |
|
Recurring tasks |
|
Notebooks |
|
Structure |
|
Habits |
|
Time blocks |
|
Time tracking |
|
Kudos/rewards |
|
Misc |
|
Capability overview
list_capabilities (no Marvin API call) returns the same map as the table
below, per tool with can and cannot, plus the list of things that only
work in the app. Call it before assuming something is impossible via MCP.
The server also sends a short instructions text at initialize (shown by
Claude Code, not by the claude.ai clients). The first line of every tool
description is a whole sentence of at most 75 characters stating the
capability — the clients show only that line in their catalog; the test
tests/test_discoverability.py guards it. The same test keeps every
description and the instructions text under 2,048 characters — Claude Code
silently truncates longer text since v2.1.280.
Area | Can via MCP | Cannot via MCP (done in the app) |
Tasks | create/update/complete/delete, priority and frog 1–4, size, subtasks, a time of day and own reminders in the app's form, day, deadline, labels, estimate, note, sections, planned week/month, review date, backburner, orbit, snooze, time block; complete a project with its open tasks (two steps); read today's, due and done, filtered as the app shows them | startDate at creation (update_task afterwards); fixing occurrences that already exist; reopening a completed project |
Recurring tasks | create all eleven types (custom only as an RRULE string), change rule/window/place/properties, clean up occurrences that no longer fit (list, confirm) | reminders/frog on recurring tasks, recurring projects, custom rules in words |
Notebooks | list, read, search, create, rename, sections, pages: create/edit/move/delete, delete a notebook in two steps | undo (no trash via the API) |
Structure | the hierarchy, children, create/update/convert categories and projects, labels on both, color, icon, note; read labels and goals | creating/editing labels, goals, smart lists, strategies |
Habits | list, tracking record, record/undo; habit tasks are recorded on mark_done and undone on unmark_done | creating/editing habits |
Time | today's time blocks with mapping, create a block, time tracking start/stop/history | the smart-time-block mapping on a block |
Rewards | kudos, award/undo/spend/reset reward points | undoing a MANUAL award; reading the app's Rewards |
Reminders | own reminders on a task (list + server entry), standalone, delete; time of day via update_task | — |
Calendar | create an event (experimental) | reading/editing events |
Account | test, account info, call budget, capability overview | — |
Deliberately not included: Smart List / task-picking logic (Marvin's own
Spotlight does the picking; the server gives your assistant hands, not
opinions), and two documented endpoints without a tool:
/reminder/deleteAll, deliberately — it wipes every reminder in a single
call and delete_reminder already covers targeted cleanup — and
/downloadAttachment (attachments are not covered).
Every tool carries MCP tool annotations
(readOnlyHint, destructiveHint, idempotentHint, openWorldHint) so
capable clients can treat delete_task and reset_reward_points with the
respect they deserve.
Related MCP server: Super-Productivity-MCP
Getting your Marvin tokens
Both tokens live in Amazing Marvin under Settings → API (app.amazingmarvin.com/pre?api):
API Token (
MARVIN_API_TOKEN, required for use) — limited access; enough for reading and creating tasks. The server does start without it (so MCP clients and directories can list the tools), but every tool call returns a clear error until the token is set.Full Access Token (
MARVIN_FULL_ACCESS_TOKEN, optional but recommended) — required by all/doc*-based tools:update_task,set_priority,unmark_done,delete_task, category creation, time blocks,list_habits, reminders,reset_reward_points.
Treat them like passwords; see SECURITY.md.
Install & run
Requires Python 3.12+.
From PyPI (recommended): with uv installed
there is nothing to set up — point your MCP client at
uvx amazing-marvin-complete-mcp as shown below.
From source:
git clone <this repo> && cd amazing-marvin-complete-mcp
python -m venv .venv && .venv/bin/pip install .
# then use /path/to/.venv/bin/marvin-mcp as the command belowLocal (stdio) — Claude Desktop, Claude Code, any MCP client
The default transport is stdio, so the client starts the server itself:
{
"mcpServers": {
"amazing-marvin": {
"command": "uvx",
"args": ["amazing-marvin-complete-mcp"],
"env": {
"MARVIN_API_TOKEN": "…",
"MARVIN_FULL_ACCESS_TOKEN": "…",
"MARVIN_TIMEZONE": "Europe/Stockholm"
}
}
}
}(For Claude Code: claude mcp add amazing-marvin -e MARVIN_API_TOKEN=… -- uvx amazing-marvin-complete-mcp.)
Remote (Streamable HTTP)
MCP_TRANSPORT=http PORT=8787 MCP_AUTH_TOKEN_FILE=/path/to/token \
MARVIN_API_TOKEN_FILE=/path/to/api-token .venv/bin/marvin-mcpThe MCP endpoint is /mcp. HTTP mode fails closed: without
MCP_AUTH_TOKEN (or MCP_AUTH_TOKEN_FILE) the server refuses to start,
with instructions in the error message; set MCP_ALLOW_UNAUTHENTICATED=true
only to deliberately run an open instance on localhost. The built-in bearer
check protects every path but is an internal barrier, not a complete auth
story:
put a reverse proxy with TLS in front, and for Claude custom connectors an
OAuth 2.1-capable MCP auth proxy. A Dockerfile for HTTP mode is included
(runs as a non-root user; mount a volume on /data to persist the daily
rate-limit counter across restarts).
Configuration
All settings via environment variables — see .env.example
for the full annotated list. Highlights: every secret supports a *_FILE
variant (recommended); MARVIN_TIMEZONE should match the timezone your
Marvin account lives in (defaults to the system timezone, which is UTC in
most containers).
Rate limiting
Marvin's documented limits — no bursts faster than one request every 3
seconds, bulk creation at one per second, 1440 calls/day — are enforced by a
single process-global queue shared by all tools and sessions, with margin:
only creating calls (/addTask, /addProject, /addEvent, /doc/create)
use 1.1 s; everything else (reads, updates, /markDone, deletions) waits
3.1 s. The spacing is set by the kind of the call about to be made. After a
429 all calls pause 60 s (or Retry-After). The daily counter and the time of
the last call persist across restarts (STATE_DIR) and the counter rolls
over at midnight in the configured timezone. get_rate_limit_status shows
today's usage and how long ago the last call went out. Multi-step tools
(chains) retry a step once after a 500 or 429 and report which steps were
done if they still fail.
Marvin API quirks & findings
Everything below was verified against the live API (2026-08-19 through 2026-10-12). This is the half of the repo you can use without running it.
Habits
Non-raw
GET /habitsdoes not read your habit documents. It reads a server-side tracking registry that is created lazily on the first recording — a habit that has never been recorded is missing from the response entirely, and the entries carry no titles (onlyhabitId+ history). Use?raw=1(Full Access Token) to list actual habit documents.GET /habit?id=…returns the tracking record — history but no title.POST /updateHabitrejects integers serialized as floats:"value": 1.0→ 400 Bad request,"value": 1→ 200. Send ints as ints.
Tasks & projects
POST /markDoneworks for tasks only — projects get400 "Can only mark Tasks done with this API".By default
/addTaskparses some of Marvin's quick-add shortcut syntax server-side:~15becomes a 15-minutetimeEstimate,+YYYY-MM-DDsetsday(scheduling — not the deadline) and*p1..*p3set priority. All three are stripped from the title. Note the priority mapping is inverted relative to the stored field:*p1(highest) →isStarred: 3,*p2→2,*p3(lowest) →isStarred: 1. The other magic words (*urgent,*fire,*heavy,*weight,*love,*lowfocus,*physical) and$-words (e.g.$MONTHon a non-recurring task) are not parsed — they are stored literally in the title with no fields set; they only work in the app's quick-add. But the#shortcut is outright dangerous: any#wordin the title (a ticket reference like#123included) is stored literally asparentId(greedy up to the first hyphen, e.g.#MCP-TEST→parentId: "#MCP"and a corrupted title) without resolving any ID — even overriding an explicitly suppliedparentIdin the same request. The task then lives outside every category and outside the Inbox — effectively invisible. (First reported by lucasoeth/marvin-mcp; independently reproduced and expanded here.) This server is not affected:create_tasksends the undocumentedX-Auto-Complete: falseheader (added in MarvinAPI#50), which disables all shortcut parsing — titles are stored verbatim, and thetime_estimate_minutesparameter replaces the~15shortcut (timeEstimateis milliseconds: 15 min =900000)./addProjecthas the same#wordcorruption bug but ignores theX-Auto-Completeheader (live-tested: the title is stripped andparentIdcorrupted even with the header set). This server therefore blocks#in project titles locally (in the client layer, before any API call) with an explanatory error. Category titles are safe — they go through/doc/create, which parses nothing./addEventis unaffected (live-tested 2026-08-25): event titles with#wordare stored verbatim, with and without the header — the quick-add parsing bug exists only in/addTaskand/addProject.Generated instances of recurring tasks have deterministic IDs (
YYYY-MM-DD_<recurringTaskId>), which is why marking them done/undone through the API cannot create duplicates. The instances are generated by the Marvin client, so today's recurring tasks can be missing from/todayItemsuntil the app has been running./doc/updatecan sporadically return a transient 500; the write is atomic (no partial state) — just retry. Project renames, moves, label changes etc. all work through it./doc/updatereturns 500 instead of 404 for documents that do not exist (deleted or never created; live-tested 2026-08-29) — a permanent 500 therefore means "wrong/dead ID", not a server error or a corrupted document.startDate/endDateare ignored by/addTaskand/addProject(live-tested 2026-08-29) — they can only be set afterwards via/doc/update(the update tools)./addProjectalso ignorescolor/icon(set them viaupdate_category_or_project), and/addTasksilently dropsnoAutoOrbit(live-tested 2026-09-13) — set it withupdate_taskafter creating the task.A clock time on a task (Time/
taskTime) is fully supported by Marvin, and with auto-created reminders a set Time automatically becomes a reminder at the same time (live data 2026-09-02). Until 1.8.0 this MCP left it to the app; since 1.8.0 the tools write it in the app's own form (fields and server entries together — see Reminders and time of day below).Projects are prioritized with the string field
priority("high"/"mid"/"low"), notisStarredlike tasks (live-tested 2026-08-29) — which is whyset_priorityis task-only. Mapping (verified against the app's code 2026-08-30):high= Most important (red),mid= Very important (orange),low= Important (yellow, the one-star level). The app's fourth level Low priority (down arrow) is stored on tasks asisStarred: -1(magic words*low/*p0); projects do not have it — the app clears the priority when converting a low priority task into a project.set_priority/create_taskaccept-1.Completed tasks are readable via the undocumented endpoint
GET /doneItems?date=YYYY-MM-DD(missing from the OpenAPI spec and the wiki; live-tested 2026-08-30, may disappear without notice). It filters on the task'sday, not ondoneAt, and a pastdaysurvives completion both in the app and via/markDone(the app setsdayto today only on unscheduled and future-dated tasks).get_done_itemstherefore fetches the date plus a 7-day lookback window and filters ondoneAt; the response states its coverage (covers_from,days_fetched), complete results are cached for 30 minutes, and on a 429 the tool returns what it got, flaggedincomplete/days_missing. Single completed tasks can also be read with/doc?id=./todayItems,/dueItemsand/childrenexclude completed items;/doneTasksand/completedItemsare 404.Marvin returns 429 even with 3 s spacing when the daily average (1440/day = "1 per minute") is exceeded within a shorter, undocumented window — observed 2026-08-30 after ~100 calls in one hour. After a 429 the limiter pauses all calls for 60 s (or
Retry-After) and logs the response headers (allow-listed names only).The server validates no writes (live-tested 2026-08-29): invalid dates, negative/out-of-range numbers, mistyped values, empty titles, dead parentId/labelIds and unknown fields are stored verbatim via
/doc/update(and almost everything via/addTask). The tools therefore validate dates (strict YYYY-MM-DD, year 2000-2100), titles and numeric ranges client-side; references are not validated (orphan risk documented in the descriptions)./doc/deleteresponds 200 even for IDs that never existed or are already deleted — idempotent, no 404 (unlike/doc/update)./markDoneon the other hand gives a proper 404 for a missing ID and 400 for an already completed task — three endpoints, three different answers to "does not exist" (live-tested 2026-08-29).Read endpoints (
/todayItems,/dueItems) are pure date filters: backburner, startDate and orphan status (dead parentId) do not affect them — and orphans never show up underunassigned(live-tested 2026-08-29)./todayItems?date=Xreturns all open items withday<= X, not just day X (live-tested 2026-09-11, rollover enabled)./markDonestops running time tracking and now also writestask.times(live-tested 2026-09-02; it did not on 2026-08-29 — server behavior changed). A direct/track STOPstill does not writetimes; there/tracksis the only record.orbit/noAutoOrbitare missing from the wiki's data types but present in live data (bool, verified 2026-08-29) — exposed as explicitly undocumented passthrough parameters on the update tools.Project↔category conversion happens in place:
_id,createdAtand the children remain (verified 2026-08-29, both via an app field test and via the API). The app has two conversion paths with different behavior (verified 2026-08-30/31): the Edit Settings button permanently clearsday/dueDate/priority/isFroggedand leavesfirstScheduledbehind (a bug in Marvin's tracker), while the right-click/hover path is a lossless round trip — but that button is not in the menu by default (add it via the gear icon in the right-click menu → Add action).convert_category_or_projectis lossless by default since 1.5.0; passclear_project_fields=Truefor a clean category (the previous values are returned inremoved_project_fields). There is no official conversion endpoint — the tool setstypedirectly, which is undocumented server behavior and marked experimental./doc/createdoes not echo back a server-generated_id— supply your own if you need to reference the document afterwards.Deletion via
/doc/deleteis permanent; Marvin's trash is client-side.The app's view after API writes (live-tested 2026-09-12, PWA + Windows desktop app 1.70.0, both open at the same time): creating, field changes and moves between category/Inbox render immediately with no action needed. Deletions do not render at all — neither waiting nor switching views helps. The same held for a cleared
plannedWeek/plannedMonthwith Planning Ahead on (2026-08-29) and for strategy toggling (method rule since 2026-08-29). Rule of action: (1) confirm the server with a read (get_childrenon the parent — the task should be missing); (2) if the app still shows the row, that is the app's view, not an error — reload the client before the row is touched in the app: F5 in the web app/PWA, restart of the desktop app (it has no refresh). The reason for "before the row is touched": editing a row in the app whose document is already deleted on the server can recreate the document through the client's conflict resolution (observed once, 2026-09-12, in one of the two clients — which one is not recorded; reported upstream). This is the Marvin client's view — not to be confused withget_done_items' own 30-minute cache, which goes the other way (app → MCP).
Reward points & kudos
Kudos (XP/level, read via
/kudos) and reward points (claim/unclaim/spend/reset) are two separate systems./kudoslacksnextMultiplier(MarvinAPI issue #5) — it's in/me./markDonedoes not award a task's reward points (cf. issue #6 for kudos) —claimRewardPointsis a separate call.A
MANUALclaim (itemId: "MANUAL") cannot be undone: the server stores no entry for it, so/unclaimRewardPointsreturns404 "No such entry"(with or without apointsfield), and claiming negative points is rejected with 400. The Marvin web app never usesMANUAL— it is an API-only facility. The only compensation is spending the same amount, which inflates the spent statistics./spendRewardPointsreturns a 500 if the balance would go negative.The app's purchasable rewards are separate
db="Rewards"documents that the public API cannot reach at all (live-tested 2026-08-29:/rewardsand every variant 404, no rewards profile documents, and/docneeds an ID you can't discover). The Task fieldisRewardis decoupled from the app's reward flow and produced no UI effect when set via the API.
Reminders
A task reminder in Marvin is two writes that only the app keeps in sync: reminder fields on the task document (
taskTime,reminderTime,reminderOffset,snooze,autoSnooze) and a server-side entry via/reminder/set. Writing only one side (all the API lets you do comfortably) produces entries the app UI won't show on the task, or server-side orphans. Standalone reminders (typeM) are the safe use of the raw API; since 1.8.0 the tools write both sides the way the app does. (Risk first documented by Recon2026/marvin-mcp; confirmed by the official wiki's own warning.)
Time & planning
/todayTimeBlocksomits the block↔category link (issue #65); this server recovers the mapping from thestrategySettings.plannerSmartListsprofile document.Stopping time tracking via a direct
/track STOPdoes not update the task's owntimes/durationfields;/tracksis the source of truth./markDoneduring active tracking does write bothtimes(live-tested 2026-09-02) andduration(verified 2026-09-13; the app shows the tracked time under Completed Today when Time Tracking is on).Calendar events created via
/addEventsync onwards only while the Marvin app is running somewhere (client-side calendar sync).
UI behavior of API-set fields (verified in the app, 2026-08-29)
Toggling a strategy requires an app restart before its fields render — without one, freshly enabled strategies show nothing and look broken.
backburneris only effective on unscheduled items: scheduling (day) trumps the flag in the UI. Setday: "unassigned"together withbackburner: true.startDatehides backburner items until their start date (the Start Dates strategy's actual mechanic) — it does not hide scheduled tasks.Icon names are library-prefixed (
lucide-Rocket,huge-happy) or emoji. Projects render their icon only when Master List → Configure View → "Show Custom Icon On" includes projects — "Categories & Projects" (verified 2026-08-31) or "Just Projects" (verified 2026-09-18); the earlier "projects never render an own icon" was our own test error.A project's own
timeEstimatetakes precedence; without one the project shows the sum of its children's estimates (per Marvin support's reply to our report — our earlier "no aggregation" came from a mis-designed test that set estimates on both the project and its children).Snoozed tasks (
itemSnoozeTime) are hidden from the category view too — the wiki's "everywhere except the master list" doesn't hold there.timeBlockSectionlinks a task to a time block's section in Today — but only if the Time Block Sections strategy is on, the day view is grouped by time block (Group by → Group by time block section; set per device, not synced) and the task is scheduled on the block's day. Unscheduled tasks with the field set do not appear in Today at all. The block needs no label/category mapping of its own; that mapping (Smart Time Block) is a second, independent route. Verified in the app 2026-09-17 (1.70.0.0, PWA + desktop); the earlier "stored but not rendered" finding was our own test error (grouping never set) and is withdrawn; the correction to Marvin support is pending. Since 1.7.0create_time_blockreturnstime_block_id(set client-side, as/doc/createdoes not echo the server id), usable directly astime_block_section.reviewDateshows in the Review view; the day-view banner additionally requires the "Review Alert" workflow snippet.Auto-orbit (if enabled) pulls newly scheduled tasks into Orbit unless
noAutoOrbitis set.Project-only fields (
day,dueDate,priority,isFrogged) written onto a category are silently accepted by the server (live-tested 2026-09-11).update_category_or_projectstill type-checks and blocks them, for a structural reason: a category is never completed, so those fields belong to projects and tasks.labelIdsis different — categories have labels in the same field as projects, and an API-set label is stored and rendered (live-tested + verified in the app 2026-09-11), solabel_idsis allowed on categories since 1.6.0.
Recurring tasks and occurrences (live-tested 2026-10-09 to 2026-10-11)
Occurrences have the id
YYYY-MM-DD_<recurringTaskId>; an occurrence created by ticking off an after completion task gets a random id, and only the first one is written via the API (echo: true,echoId: null). The next one after/markDonelands on day +echoDays.createAheadmissing ornull= the account's default. The app shows acustomrule correctly only as an RRULE string, so the tools accept only RRULE (rules in words like "every other friday" are refused).Deleting one occurrence the way the app's Just this one does adds its ISO date to the recurring task's
deletedDates.With Only on weekdays, a month day on a weekend moves to Friday.
Occurrences inherit labels, priority, size, note, sections, subtasks (id
<day>_<subid>) anddueIn→dueDate.
Notebooks
GET /notebook?parentId=answered 404 "Notebook not found" 5 and 12 s after the notebook was created with/doc/create, while?id=answered 200 at the same time (cause unknown).get_notebookwithparent_idthen returns an error suggestingnotebook_id.A project's
notePageIdis computed by the server from the page with the lowestrank; writing the project'snotewrites that page.Deleting a project in the app (with its notes) leaves its notebook and page on the server — still listed by
/notebooksand/notebook?parentId=.get_notebooksflags such notebooksparent_missing(andparent_donefor a done project).
Reminders and time of day
A clock time =
taskTime"HH:MM" +reminderTime(seconds) and friends on the task, plus a server entry of typeTwhosereminderIdis the task's id. Own reminders = areminderslist on the task plus one typeMentry each.Completing a task in the app removes
TandMon the server; undoing the completion does not putTback, whileMstill notifies. Before deleting a task with reminders, delete its server entries too, or an open client keeps reminding.
Habits
/updateHabitwithundoalways removes the latest history entry; iftimeis sent with it,[time, 0]is appended as well. Never sendtimewithundo.A recording below a numeric target writes nothing on the habit task (
habitRepAtstays null).
Completing projects and other document behavior
When the app completes a project it writes
done,doneAt=workedOnAt,day(today if unset) anddoneDate=dayin one stamped write, and refuses while children are open.mark_project_donemirrors this./categoriesdoes not list done projects./markDonesets noworkedOnAtand stamps nofieldUpdates; on a task without a day it setsdayto today, also unstamped.GET /docon a deleted document answers 200 with{"error":"not_found","reason":"deleted"};/doc/updateon it gives 500./addTaskdropstaskSize(XS–XL = 1–5); it is written afterwards with/doc/update.
Rate limits
Getting started says "No bursts faster than 1 request every 3 seconds". The limiter spaces creates (
/addTask,/addProject,/addEvent,/doc/create) 1.1 s and everything else 3.1 s, keyed on the upcoming call's kind.
How this differs from existing alternatives
Several good Amazing Marvin MCP servers exist; this one was built fresh (no shared code) after studying them, with a different goal — complete coverage of the public API rather than a curated subset:
bgheneti/Amazing-Marvin-MCP — the established Python server; broad but not complete coverage, no global rate limiting.
Recon2026/marvin-mcp — smaller scope (19 tools), unusually careful research; chose to make reminders read-only over the two-write risk. This server ships reminder writes with explicit warnings instead.
lucasoeth/marvin-mcp — a different philosophy: a handful of consolidated workflow tools (brief/ capture/…) rather than an API mirror, plus direct CouchDB reads for search and completed tasks (which the public API can't do at all). If you want opinionated workflows or search, use theirs; if you want raw, complete API access with the sharp edges documented, use this one.
LucaDeLeo/amazing-marvin-mcp — a Limited-API subset.
Credits & sources
No code was copied from any of these — the build is fresh — but they materially shaped it:
amazingmarvin/MarvinAPI (+ wiki) — the official API documentation, OpenAPI spec, data types, and issue tracker this server is built against.
bgheneti/Amazing-Marvin-MCP — architecture inspiration, endpoint reference during the initial gap analysis, and the MIT-licensing precedent.
Recon2026/marvin-mcp — the reminder two-write integrity risk and the groundwork on recurring-task instances, both verified and documented here.
lucasoeth/marvin-mcp — the
#Categoryshortcut bug (reproduced here) and the insight that Marvin's sync database is a real CouchDB usable for reads.LucaDeLeo/amazing-marvin-mcp — the pointer that
/addTaskparses shortcut syntax server-side (partly confirmed, partly refuted — see the#Categoryfinding), and the idea of MCP tool annotations.
Built with Claude Code (Claude Fable 5, Fable 5.1 and Opus 5.5).
License
MIT.
Available Tools
50 toolsclaim_reward_pointsA
Award reward points for a completed task or manually. Note: mark_done does not award a task's rewardPoints automatically through the API (cf. issue #6 about kudos) — call this tool separately afterwards. WARNING: a MANUAL award CANNOT be undone through the API (verified live 2026-08-19: unclaim returns 404, negative points are rejected with 400). The only compensation is spend_reward_points for the same amount (which however inflates the spent statistics) — award MANUAL points thoughtfully.
Inputs: points (required), item_id (required), date. Returns: object
convert_category_or_projectAIdempotent
Convert a project to a category or back, in place and losslessly. EXPERIMENTAL: Convert project→category or category→project IN PLACE via /doc/update (Full Access Token; there is no official conversion endpoint, and this relies on undocumented server behavior that Marvin could change). Same _id, createdAt and children remain — conversion is a pure type change (verified against the live API 2026-08-29: the server accepts and persists the change in both directions, and the app renders correctly after an API-set change). LOSSLESS BY DEFAULT (since 1.5.0): only type is changed — the same semantics as the app's correct conversion path (the right-click/hover menu, verified as a lossless round trip 2026-08-30: all project fields incl. firstScheduled preserved through project→category→project). Project fields remaining on the category are then intentional round-trip data; the type guard in update_category_or_project only prevents NEW project fields from being written to it. If you want a clean category for a permanent conversion: set clear_project_fields=True (mimics the app's Edit Settings path — a bug in their tracker; also clears firstScheduled, which that path otherwise leaves behind) and receive the values in removed_project_fields. Note: the app's correct path (right-click/hover) is not in the menu by default — it is added via the gear icon directly in the right-click menu → Add action (app-verified 2026-08-31), so unmodified apps only show the buggy path. Do NOT convert a category that contains subcategories into a project — projects cannot contain categories (risk of orphans/cycles; check get_children first).
Inputs: to (required), item_id (required), clear_project_fields. Returns: object
create_category_or_projectA
Create a category or project with color, icon, labels and note. Create a category (via /doc/create, Full Access Token) or a project (via /addProject). Categories can contain categories; projects cannot. day/due_date/priority/frog are rejected for kind='category' for a structural reason, not a technical one: a category can never be completed or checked off, and deadline, scheduling, priority and frog belong to things that can be finished — projects and tasks. The API accepts the fields on categories (live-tested 2026-09-11) but they are not meaningful there (rule 2026-09-11). label_ids applies to both categories and projects. startDate/endDate cannot be set at creation (/addProject ignores them, verified live 2026-08-29) — use update_category_or_project afterwards.
Note: project titles must not contain '#word' — /addProject has the same corruption bug as /addTask (the string is stored unresolved as parentId and the project becomes invisible) but ignores the X-Auto-Complete header (verified against the live API 2026-08-20), so the client blocks it locally before any API call. Category titles are unaffected (/doc/create parses nothing).
Inputs: kind (required), title (required), day, frog, +11 more. Returns: object
create_eventA
Create a calendar event (experimental), synced by the app. EXPERIMENTAL: Create a calendar event. Calendar sync happens in the client — the Marvin app must be running on some device for the event to sync onwards to an external calendar.
Inputs: title (required), start_iso (required), length_minutes (required), note. Returns: object
create_notebookA
Create a standalone notebook, optionally with sections. A notebook for a project or category is not created here: it comes into being when a page is moved there (update_notebook_page move_to_parent_id) or when a second page is added in the app. Relies on undocumented writes (/doc/create and /doc/update, marked experimental by Marvin); the shape is matched against app 1.71.
Inputs: title (required), sections. Returns: object
create_notebook_pageA
Create a page at the end of a notebook, optionally in a section. Own pg_ id, rank after the existing pages. '- ' at the start of a line is written as '* ' (code protection against the app deleting lists). Relies on undocumented writes (/doc/create and /doc/update, marked experimental by Marvin); the shape is matched against app 1.71.
Inputs: title (required), notebook_id (required), text, section_id. Returns: object
create_recurring_taskA
Create a recurring task of any of the repeat dialog's eleven types. Written via /doc/create in the app's own shape, so every type shows correctly in the app's repeat dialog. Occurrences are created the first time a Marvin client opens after midnight, in the window today to today + Create occurrences — not immediately. Without create_occurrences the account's default applies. After completion: the tool writes the first occurrence on the start day, also when the start day is in the future. Custom accepts only an RRULE string (e.g. 'RRULE:FREQ=WEEKLY;INTERVAL=2;BYDAY=FR'). New occurrences inherit label, priority, size, note, subtasks, sections and hard deadline (verified against the live API). Only on weekdays: a month day on a weekend is moved to Friday (documented for monthly, verified); every_x_days then counts in weekdays (inferred from a series; the documentation does not mention the type).
Inputs: title (required), repeat (required), note, size, +20 more. Returns: object
create_taskA
Create a task with category, day, priority, size, time and reminders. The title is stored verbatim: the server's shortcut parsing is disabled (X-Auto-Complete: false), so '#Category', '~15', '*m' and '*p2' are NOT parsed — '#' in titles is safe. Use the parameters instead. startDate/endDate are set with update_task afterwards (/addTask ignores them). Size, subtasks, clock time and reminders are written in a second step with /doc/update and /reminder/set in the app's form; if a step fails the response says which ones were done (the task then already exists). A clock time requires day. Strategy-dependent fields (planned_week/month, review_date, backburner, reward_points, the sections) are stored even when the strategy is off in the app.
Inputs: title (required), day, frog, note, +19 more. Returns: object
create_time_blockA
Create a time block (experimental) and get its id back. EXPERIMENTAL: Create a time block via /doc/create (db='PlannerItems', Full Access Token). No official endpoint exists. Verify in the app that the block looks right.
The response carries time_block_id (the id is set client-side because
/doc/create does not echo the server-generated id) and can be used
directly as time_block_section in create_task/update_task — no extra
get_today_time_blocks call needed. Client-set id live-tested 2026-09-13:
accepted, listed in /todayTimeBlocks with the same id, and the block
renders in the app like one created there (verified in the app
2026-09-13, app 1.70.0.0, PWA + desktop).
Inputs: date (required), title (required), start_time (required), duration_minutes (required). Returns: object
delete_notebookADestructiveIdempotent
Delete a notebook and its pages permanently, in two steps if text. No trash via the API. The pages are deleted first and then the notebook, as the app does. Two steps: a notebook with a parent and more than one page, and a standalone notebook where any page has text, are only deleted with confirm=true — the first call shows the pages (title, size, first line). Notebook with a parent: page 1's text is written back as the project's/category's note after the deletion; the text is always included in the response, so it is not lost if a step fails. Relies on undocumented writes (/doc/create and /doc/update, marked experimental by Marvin); the shape is matched against app 1.71.
Inputs: notebook_id (required), confirm. Returns: object
delete_notebook_pageADestructiveIdempotent
Delete a notebook page permanently, with confirmation if it has text. No trash via the API. A page with text is only deleted with confirm=true; the first call shows title, size and first line. The last page of a notebook with a parent is not deleted here — use delete_notebook. If the parent's first page is deleted, the note then points to whichever page is first. Relies on undocumented writes (/doc/create and /doc/update, marked experimental by Marvin); the shape is matched against app 1.71.
Inputs: page_id (required), confirm. Returns: object
delete_reminderADestructiveIdempotent
Delete reminders, also from the task's list when task_id is given. With task_id the entries are removed from the task's reminders list and from the server (both are needed; an entry left in the list makes an open client notify). Without task_id only the server entries are removed — use it for standalone reminders or orphaned entries from get_reminders. The clock time's reminder (type T) is removed with update_task(time='').
Inputs: reminder_ids (required), task_id. Returns: object
delete_taskADestructiveIdempotent
Delete a task permanently, with no trash and no undo. Deletion via the API bypasses the app's trash and cannot be undone — use only when the user explicitly wants a deletion. If the task has reminders they are cleared and the server entries removed before the deletion, so that no notification arrives. An occurrence of a recurring task follows the app's two choices: 'this' adds the occurrence's day to the recurring task's deletedDates and deletes the occurrence; 'all' deletes all open occurrences and then the recurring task, completed occurrences are kept. If a step fails the response says which were done. THE APP'S VIEW: an open client does not render the deletion — reload the client (F5, restart of the desktop app) before the row is touched; a write from the app on the stale row recreates the document. A recurring task (db RecurringTasks) is deleted with scope 'all', on an occurrence or on the recurring task's own id — also before it has any occurrences (they are created only when a client opens after the day rolls over). Habit recurrences are not deleted here. Projects and categories: deleted only without open children — the tool reads /children first (one more call) and otherwise refuses, with the list, so that no children are left with a parent that does not exist. Completed children do not show in /children and are left behind with the parent gone. The project's notebook is not deleted (as in the app); the response gives its id for delete_notebook.
Inputs: item_id (required), scope. Returns: object
get_account_infoARead-only
Get account info (/me): email, tracking status and points balance.
Inputs: no named parameters. Returns: object
get_categoriesARead-only
Get all categories and projects as a hierarchy with id and parentId. Get all categories and projects (the whole hierarchy; parentId='root' is the top level). Use to find the right parent_id when creating/moving.
Inputs: no named parameters. Returns: object
get_childrenARead-only
Get open tasks and subprojects in a category or project. Returns direct children only — call again for deeper levels. Note: orphans (tasks whose parentId points to a deleted/non-existent document) do NOT show up under 'unassigned' — only in get_today_items/get_due_items if they have a day/dueDate (live-tested 2026-08-29). Orphans are repaired by running FIX_CYCLES() in the app's console (a documented troubleshooting path).
Inputs: parent_id (required). Returns: object
get_done_itemsARead-only
Get tasks completed on a given date.
Tasks completed on a given date (doneAt within that day, configured
timezone) — regardless of priority and deadline. Built on the
UNDOCUMENTED endpoint GET /doneItems?date= (missing from the OpenAPI
spec and the wiki; live-tested 2026-08-30, may disappear): it filters
on the task's day, not on doneAt. A past day is kept on completion
both in the app and via the API (the app sets day = today only on
unscheduled and future-dated tasks) — hence the date plus lookback_days
earlier are fetched and everything is filtered on doneAt.
The response always states its coverage: covers_from (= date − lookback)
and days_fetched. On a 429/error the fetch stops: incomplete=true,
days_missing lists the days not fetched and warning explains; the
date's own completions are always included because it is fetched
first. Complete results are cached for 30 minutes (cached=true) —
repeated calls then cost no API calls; mark_done inserts its task into
the cache, delete_task and unmark_done remove theirs. Completions or
deletions made in the app show up only once the cache expires. Not covered: a task with a FUTURE day completed via the API
(it sits under its day). Tasks only — completed projects are not
listed. Items without doneAt (older data) are excluded and counted in
skipped_without_done_at. Sorted by doneAt. Cost: one read call per day
(~3.1 s each in the queue).
Inputs: date, lookback_days. Returns: object
get_due_itemsARead-only
Get open tasks and projects with a deadline today or earlier. Filters Keep just one (carry) as the app does: only the oldest open occurrence per recurring task is shown. Only the current one (skip) is not filtered here. The mode is read only for recurring tasks with an open occurrence before today and cached until local midnight; changes made in the app take effect by the next day at the latest. The response gives hidden_count; include_hidden=true includes all.
Inputs: by, include_hidden. Returns: object
get_goalsARead-only
Get all goals with status and check-in data.
Inputs: no named parameters. Returns: object
get_habitARead-only
Get a habit's server tracking record with its full history. Get the server's tracking record for a single habit (habitId + full history — the source of truth for recordings). Note: the response lacks title and settings; those are in list_habits.
Inputs: habit_id (required). Returns: object
get_kudosARead-only
Get kudos, level and kudosRemaining from Marvin's XP system. Note: kudos is separate from reward points (the reward currency) — the point balance is in get_account_info. nextMultiplier only exists in /me, not here (known limitation, MarvinAPI issue #5).
Inputs: no named parameters. Returns: object
get_labelsARead-only
Get all labels with ids, for label_ids and filtering.
Inputs: no named parameters. Returns: object
get_notebookARead-only
Read a notebook including the text of its pages, or a single page. Give exactly one of notebook_id, parent_id (project/category) and page_id. Pages come in rank order; the first page of a notebook with a parent is the one the parent shows as its note. If the text exceeds max_chars the rest is omitted and the response says which pages.
Inputs: page_id, max_chars, parent_id, notebook_id. Returns: object
get_notebooksARead-only
List all notebooks with page titles, order and sections, without text. Standalone notebooks have parent_id null; a notebook that belongs to a project or category has parent_id and parent_type. A page's updated_at changes when the page is edited in the app. For text: get_notebook. A notebook whose parent has been deleted gets parent_missing, one on a done project gets parent_done. Costs 2 calls (1 when no notebook has a parent) plus one per distinct parent missing from /categories (deleted or done projects).
Inputs: no named parameters. Returns: object
get_rate_limit_statusARead-only
Show today's Marvin calls against the budget and the last call time. Show how many Marvin API calls have been made today (budget 1440/day, shared by all tools) and when the latest one went out. The counter is shared by every client that goes through this server. If a call went out within the last minute, another client is active. This tool costs no Marvin call.
Inputs: no named parameters. Returns: object
get_remindersARead-only
Get all server-side reminders, push notifications to the phone. Requires the Full Access Token.
Inputs: no named parameters. Returns: object
get_time_tracksBRead-only
Get time-tracking history for up to 100 tasks. Get time-tracking history for the given tasks (the source of truth, max 100 per call).
Inputs: task_ids (required). Returns: object
get_today_itemsARead-only
Get open tasks and projects scheduled today or earlier. Open tasks/projects with day <= the date (default today in the configured timezone, MARVIN_TIMEZONE), not only exactly on the date. Deadline-only items: get_due_items. Filters as the app does: for a recurring task with If you miss one = Keep just one (carry) only the oldest open occurrence is shown, for Only the current one (skip) only the date's own; Keep them all and the account default ('', assumed to be Keep them all) show all. The mode is read (/doc) only for recurring tasks with an open occurrence before today, cached until local midnight; a mode changed in the app takes effect by the next day at the latest. If the mode cannot be read the occurrences are shown. The response gives hidden_count and which were hidden; include_hidden=true includes all. Habit tasks (field habitId) are listed in habit_task_ids. Recurring occurrences are created the first time a Marvin client opens after the day rolls over; read before that, today's occurrences are missing, and a recurring task created during the day may lack them.
Inputs: date, include_hidden. Returns: object
get_today_time_blocksARead-only
Get today's time blocks with their category or smart-list mapping. The API response lacks the category link (known limitation, MarvinAPI issue #65); the mapping is therefore fetched separately from the profile setting plannerSmartLists (key = normalized block title).
Inputs: date, include_category_mapping. Returns: object
get_tracked_itemARead-only
Show which task is being time-tracked right now.
Inputs: no named parameters. Returns: object
list_capabilitiesARead-only
List every Marvin tool by area, with what it can and cannot do. Costs no Marvin API call. Call it before assuming something is impossible via MCP or requires manual work in the app; field conditions and live-tested details live in each tool's own description.
Inputs: no named parameters. Returns: object
list_habitsARead-only
Get all habits with title, settings and history. Get all habits as full documents incl. title, settings and history ([time1, value1, time2, value2, ...], unix ms). Requires the Full Access Token (the raw variant of /habits). Important (verified live 2026-08-19): non-raw /habits would be wrong here — it reads the server's tracking registry, which is created lazily on the first recording, so never-recorded habits are missing entirely, and the responses lack titles.
Inputs: no named parameters. Returns: object
mark_doneAIdempotent
Mark a task as done and clean up its reminders as the app does. Reads the task, runs /markDone (configured timezone) and then does what the app does: the server entries T and M are removed and reminderTime is cleared; the reminders list remains. Projects: /markDone responds 400 (tasks only); projects are closed with mark_project_done. 404 = does not exist, 400 = already done or a project. Habit task (field habitId): the habit is also recorded with /updateHabit at 12:00 on the task's day, and habitRepAt is set. Habit with a count (recordType number): value is recorded with the current time; the task is checked off only once the day's total reaches the target — otherwise it stays open and the response shows the total. Recurring occurrence: safe (deterministic id). After completion: the next occurrence is created +N days later. With a future day the tool does what the app does: the occurrence is moved to today before it is checked off, and the next one is counted from today. If a step fails the response says which steps were done. Running time tracking is stopped and written to task.times. /markDone does not set workedOnAt and does not stamp fieldUpdates, which the app does.
Inputs: item_id (required), value. Returns: object
mark_project_doneAIdempotent
Close a project and its open tasks, in two steps. Step 1 (without confirm_code): reads the project and its open children, lists them with the warning that they too will be marked done, and returns a code. Nothing is written. Step 2 (confirm_code from step 1): reads the children again; if the code no longer matches, nothing is closed and the response gives the new list and code. Otherwise each open task is checked off like mark_done (/markDone and reminder cleanup) and the project is closed last with a /doc/update of the app's five fields: done, doneAt and workedOnAt (now), day (the project's day, or today if it has none) and doneDate (same as day) — as the app does. The deadline (dueDate) is not touched. The app itself refuses to close a project with open children; this tool closes them on purpose. The children are checked off with /markDone, which does not set workedOnAt and does not stamp fieldUpdates — the app sets workedOnAt on the children. Stops in step 1 without a code if an open child is a sub-project, a habit task or an occurrence of a recurring task — close those first (the app's behavior there is untested). Completed projects do not show in get_categories. Undo in the app; unmark_done refuses projects. If a step fails the response says which were done; the project then stays open. Cost: step 1 two calls; step 2 three, plus one per task and two more per task with reminders.
Inputs: project_id (required), confirm_code. Returns: object
record_habitA
Record or undo a habit check-in, synced to the app. Also updates the sync database (updateDB=true) so the Marvin app shows the change immediately.
Inputs: habit_id (required), undo, value. Returns: object
reset_reward_pointsADestructiveIdempotent
Reset reward points permanently, balance and history. Reset reward points PERMANENTLY: deletes the whole earn/spend history and sets the balance to 0 (Full Access Token). CANNOT be undone — only use when the user explicitly asks for it.
Inputs: no named parameters. Returns: object
search_notebooksARead-only
Search notebooks: notebook names, page titles and page text. The list is fetched with one call; page text is cached in memory and fetched only for notebooks with pages changed since the previous search (updated_at), at most 20 notebooks per call. Notebooks whose text has not been fetched yet are listed in not_searched — search again to continue. Hits in a notebook whose parent has been deleted get parent_missing, on a done project parent_done (as in get_notebooks).
Inputs: query (required). Returns: object
set_priorityAIdempotent
Set priority (P1-P3 or low) or the dread level on a task. Set or change priority (isStarred) and/or the frog/dread level (isFrogged 1-4, read in the app 2026-10) on an existing TASK. Requires the Full Access Token. The app's four priority levels are stored as isStarred 3/2/1/-1; Low priority is shown only with 'Enable low priority' on. Does not apply to projects: they use the string field priority ('high'/'mid'/'low'); set it via update_category_or_project. A permanent 500 = the task does not exist (deleted or wrong ID).
Inputs: item_id (required), frog, priority. Returns: object
set_reminderA
Set an own reminder on a task, or a standalone reminder. With task_id the reminder is written in the app's form: an entry in the task's reminders list (reminderId of 13 characters, time in seconds) and a server entry of type M with taskId — the app shows it under Reminders and the notification arrives, and mark_done/delete_task clean it up. A clock time on a task is set with update_task(time=...). Without task_id a standalone server entry of type M is written (requires Marvin's mobile app for the notification).
Inputs: time_unix_seconds (required), title, task_id. Returns: object
spend_reward_pointsA
Spend reward points on a reward. Note (verified live): the API responds 500 Internal Server Error if the balance would go negative — check the balance (get_account_info) before large purchases.
Inputs: points (required), date. Returns: object
start_trackingBIdempotent
Start time tracking on a task.
Inputs: task_id (required). Returns: object
stop_trackingAIdempotent
Stop time tracking on a task; the time lands in get_time_tracks. Stop time tracking for a task. Note (documented API limitation, confirmed live 2026-09-02): the task's own times/duration fields are not updated by /track STOP — the tracking only lands in /tracks (get_time_tracks). Exception: mark_done during active tracking now writes task.times (see mark_done).
Inputs: task_id (required). Returns: object
test_connectionARead-only
Check that the API token works against Marvin's API. Returns OK if the apiToken works.
Inputs: no named parameters. Returns: object
unclaim_reward_pointsAIdempotent
Undo a reward-point award tied to a task. Undo a point award (e.g. after a misclick, or when the task was un-completed with unmark_done). Only works for awards tied to a real task ID: Marvin's server stores no entry for MANUAL awards (verified live 2026-08-19, /unclaimRewardPoints responds 404 'No such entry'). Compensate a MANUAL award with spend_reward_points for the same amount instead.
Inputs: item_id (required), date. Returns: object
unmark_doneAIdempotent
Undo a task's completion, and the habit's record. Projects and categories: refuses without writing — a closed project is reopened in the app. Sets done=false and clears doneAt via /doc/update (Full Access Token); the app's own undo leaves doneAt in place. A task that never had a day (firstScheduled 'unassigned' and no fieldUpdates.day stamp) got its day from /markDone: the day that mark_done set is removed (stored as 'unassigned'; the app's own undo writes null — both mean no day). Otherwise the day stays. As in the app: the clock time's reminder (T) is not restored — the time does not remind until it is set again — but own reminders in the reminders list still apply. Habit task: the habit's latest record is undone with /updateHabit undo and habitRepAt is cleared, but only if the history's last entry is the task's habitRepAt; otherwise the response says so and the habit is left untouched (undo always takes the latest entry). Open habit task (a partial record on a habit with a count): nothing is written — undo the record with record_habit undo=true, as the app's undo does. Kudos are not adjusted; reward points are undone with unclaim_reward_points. A permanent 500 = the document does not exist.
Inputs: item_id (required). Returns: object
update_category_or_projectAIdempotent
Update a category or project: labels, color, icon, note, project fields. Update fields on an existing CATEGORY or PROJECT via /doc/update (Full Access Token). For tasks, use update_task. Fields marked 'Projects ONLY' (day/due_date/priority/frog/size) are blocked for categories: if any of them is given, the tool first reads the document (1 extra API call) and refuses if it is a category. The reason is structural, not technical: a category can never be completed or checked off, and deadline, scheduling, priority and frog belong to things that can be finished — projects and tasks. The API accepts the fields on categories (live-tested 2026-09-11) but they are not meaningful there (rule 2026-09-11). label_ids applies to both categories and projects. Strategy-dependent fields (start/end date, planned_week/month, review_date, orbit) can be set even when the strategy is disabled in the app. Do not complete projects here (done via /doc/update skips the app's side effects) — that is done in the Marvin app. Note: Marvin's server can sporadically respond 500 on /doc/update (transient and atomic); just retry. But a PERMANENT 500 (persists across retries) means the document does not exist — deleted, or a wrong/never-existing ID (the server responds 500 instead of 404 for missing IDs, verified live 2026-08-29). Fetch a fresh ID via get_categories/get_children.
Inputs: item_id (required), day, frog, icon, +18 more. Returns: object
update_notebookAIdempotent
Rename a standalone notebook or change its sections. Renaming is refused on a notebook that belongs to a project or category (the app would otherwise show the notebook's name instead of the project's). The section list is read just before the write and is rewritten in full. On removal, sectionId is first set to null on the pages in that section, so that no page points to a missing section. Relies on undocumented writes (/doc/create and /doc/update, marked experimental by Marvin); the shape is matched against app 1.71.
Inputs: notebook_id (required), title, sections_add, sections_delete, +1 more. Returns: object
update_notebook_pageAIdempotent
Change a page's text, title, order or section, or move it. text replaces, append adds at the end; '- ' at the start of a line is written as '* '. New text shows up immediately in an open client. Move: the section does not follow (sectionId null). The first page of a notebook with a parent is not moved out — it is the parent's note. Moving to a project or category without a notebook creates one in the app's shape. The response says when an order change switches which page the parent's note points to. Relies on undocumented writes (/doc/create and /doc/update, marked experimental by Marvin); the shape is matched against app 1.71.
Inputs: page_id (required), rank, text, title, +4 more. Returns: object
update_recurring_taskAIdempotent
Change a recurring task: rule, window, place, end date, properties. Existing occurrences are not corrected when the rule changes via the API, and a later save in the app does not correct them either; they keep their old shape. When the rule is changed in the app's dialog, the app itself removes those that no longer match. When type, interval or Create occurrences change, the tool then works out which open, untouched occurrences from today on do not match the new rule or window. Mode 'list' (default): they are listed and removed only with confirm=true (without deletedDates: they do not come back if they do not match the rule, and outside the window they should come back). Changed occurrences, occurrences before today, custom rules that are not RRULEs and Only on weekdays are listed only. Deleted occurrences can remain visible in an open client until it reloads. New occurrences follow a rule and properties changed via the API, counted from the start day. Custom accepts only an RRULE string. Lowering Create occurrences via the API removes nothing, not even at the next generation; the app's dialog removes untouched upcoming occurrences when the lowering is saved there. confirm=true does what the dialog does.
Inputs: recurring_task_id (required), note, size, title, +20 more. Returns: object
update_taskAIdempotent
Update a task: title, day, time, reminders, size, subtasks. /doc/update (Full Access Token). Categories/projects: update_category_or_project; priority and frog: set_priority; completion: mark_done. Changing the day does what the app does: reminderTime and the server entry T move to the new day, own reminders (M) stay and are listed in the response. New clock time: new T, own reminders (including 'X min before') keep their time. time='' removes the clock time in the app's form. If the day is removed (day='unassigned') the clock time and T are removed as in the app; own reminders stay. Subtasks are written with dotted keys. parent_id or subtasks on a recurring occurrence give a warning: the recurring task stays as it is, so future occurrences follow it — change it with update_recurring_task. Strategy-dependent fields are stored even when the strategy is off in the app. A sporadic 500 on /doc/update is transient (retried once); a permanent 500 = the document does not exist.
Inputs: item_id (required), day, note, size, +25 more. Returns: object
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 50 tools
Tools are organized around distinct resources and actions (tasks, categories/projects, notebooks, reminders, habits, tracking, rewards), with clear cross-references (e.g. update_task vs update_category_or_project vs set_priority). Minor overlap exists between get_today_items/get_due_items/get_done_items and between start_tracking/get_tracked_item/stop_tracking, but descriptions disambiguate them well.
Strong overall verb_noun pattern (create_task, update_task, delete_task, get_notebook, record_habit). A few deviations (mark_done, mark_project_done, set_priority, set_reminder don't include the noun, and get_today_items vs get_due_items is fine) slightly break the pattern but remain readable and predictable.
With 50 tools this is far beyond a well-scoped surface; at this size it becomes hard for an agent to discover and select the right tool. Many tools are tightly coupled (mark_done/mark_project_done/unmark_done, or the seven notebook/page tools) and could be consolidated, making the count feel heavy for the apparent domain.
Coverage is extensive — CRUD for tasks, categories/projects, notebooks and pages, plus reminders, habits, recurring tasks, tracking, rewards, goals and labels. Missing pieces include a bulk/clear operation for recurring task occurrences and no 'get_task' single fetch (though update/delete return state), and there is no direct way to undo a manual reward award, but these are minor gaps relative to the breadth.
Maintenance
Related MCP Connectors
Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
Official Todoist MCP server. Manage tasks and projects. Hosted, no API key needed.
MCP server for progressive tool usage at any scale (see https://klavis.ai)
Hosted MCP server for live public-data APIs and Skills for AI agents.
Related MCP Servers
- AlicenseAqualityDmaintenanceAn MCP server that enables AI assistants to interact with the Taskwarrior command-line task management tool. It allows users to list, create, modify, and organize tasks using projects, tags, and annotations through natural language.132MIT
- AlicenseNot gradedqualityAmaintenanceAn MCP server that connects AI assistants to Super Productivity for managing tasks, projects, and tags. Supports quick capture, batch triage, and full planning sessions through natural language.264 npm131MIT
- AlicenseBqualityDmaintenanceAn MCP server that lets your AI assistant manage TickTick tasks, projects, habits, and more through natural language, supporting both TickTick and Dida365.521MIT
- FlicenseNot gradedqualityDmaintenanceA personal assistant MCP server that integrates Todoist, Anki, Obsidian, and Google Calendar to help you learn, organize, and stay productive through natural language interactions.2-