amazing-marvin-complete-mcp
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tasks | {
"list": {},
"cancel": {},
"requests": {
"tools": {
"call": {}
},
"prompts": {
"get": {}
},
"resources": {
"read": {}
}
}
} |
| tools | {
"listChanged": true
} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
| experimental | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| test_connectionA | Check that the API token works against Marvin's API. Returns OK if the apiToken works. |
| 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. |
| mark_doneA | 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. |
| unmark_doneA | 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. |
| mark_project_doneA | 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. |
| update_taskA | 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. |
| set_priorityA | 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). |
| delete_taskA | 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. |
| get_today_itemsA | 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. |
| get_due_itemsA | 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. |
| get_done_itemsA | 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 |
| get_childrenA | 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). |
| get_categoriesA | 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. |
| 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). |
| update_category_or_projectA | 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. |
| convert_category_or_projectA | 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). |
| list_habitsA | 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. |
| get_habitA | 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. |
| 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. |
| get_today_time_blocksA | 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). |
| 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 |
| get_tracked_itemA | Show which task is being time-tracked right now. |
| start_trackingB | Start time tracking on a task. |
| stop_trackingA | 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). |
| get_time_tracksB | 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). |
| get_kudosA | 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). |
| claim_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. |
| unclaim_reward_pointsA | 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. |
| 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. |
| reset_reward_pointsA | 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. |
| get_labelsA | Get all labels with ids, for label_ids and filtering. |
| get_goalsA | Get all goals with status and check-in data. |
| get_remindersA | Get all server-side reminders, push notifications to the phone. Requires the Full Access Token. |
| 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). |
| delete_reminderA | 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=''). |
| 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. |
| get_account_infoA | Get account info (/me): email, tracking status and points balance. |
| get_rate_limit_statusA | 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. |
| list_capabilitiesA | 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. |
| get_notebooksA | 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). |
| get_notebookA | 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. |
| search_notebooksA | 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). |
| 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. |
| update_notebookA | 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. |
| delete_notebookA | 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. |
| 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. |
| update_notebook_pageA | 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. |
| delete_notebook_pageA | 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. |
| 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). |
| update_recurring_taskA | 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. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
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.