Skip to main content
Glama
Todoist Weekly Review MCP

Todoist Weekly Review MCP

by yugmarwaha

Todoist Weekly Review MCP

An MCP server for doing a weekly review of your Todoist with Claude. It surfaces the three kinds of mess a task list collects — overdue pileups, stale tasks nobody has touched in months, and "hidden projects" (single tasks that are really whole projects) — with the signals that matter: days overdue, days since activity, priority, how often you've postponed each one. Claude suggests a fix per task, you approve or veto each one in chat, and only the approved changes are applied. The server decides nothing on its own and makes no LLM calls. Unofficial — not affiliated with Doist.

Design history and roadmap: PLAN.md, CONTEXT.md.

Quick start

npm install && npm run build
cp .env.example .env   # paste your token from Todoist → Settings → Integrations → Developer
# (or create ~/.config/todoist-weekly-review/.env instead — see below)

Open Claude Code in this folder, approve the todoist-weekly-review server when asked, and say "run my weekly review".

Related MCP server: Todoist MCP Server Extended

Install as a Claude Code plugin

/plugin marketplace add yugmarwaha/todoist-weekly-review-mcp
/plugin install todoist-weekly-review@yugmarwaha-plugins

Then create ~/.config/todoist-weekly-review/.env containing TODOIST_API_TOKEN=<your token> (or export that variable in your shell) and run /todoist-weekly-review.

Tools

Tool

What it does

get_overdue_tasks

Read-only. Overdue tasks + signals.

get_stale_tasks

Read-only. Tasks untouched for N days (default 60).

get_projects

Read-only. Your projects.

apply_changes

The only write. Actions: reschedule, set_priority, move_to_project, reword, complete, apply_label, split.

Safety

  • Nothing is written without your explicit per-item approval in chat.

  • No delete exists. Tasks are retired by completing them or moving them to "Someday/Maybe" (created automatically if missing).

  • Token lives in the gitignored .env (or ~/.config/todoist-weekly-review/.env) — never logged, never committed.

  • Read errors are loud (bad token, rate limit, etc.) — never a silent empty list.

  • A malformed change rejects the whole batch before anything is written.

Design notes

  • "Times rescheduled" comes from the task's postponed_count field, so the plan-gated activity log isn't needed. Omitted when Todoist doesn't return it.

  • Priority is inverted in the API: 4 = highest (the UI's P1). Passed through as-is.

  • Endpoints verified against the live v1 OpenAPI spec. Two fixes vs. the draft plan: filtering is GET /tasks/filter?query=…, and moving a task is POST /tasks/{id}/move.

  • Todoist quirk: a task due earlier today doesn't count as "overdue" until tomorrow.

  • Days overdue are computed in your Todoist account's timezone.

  • Recurring tasks are flagged isRecurring — completing one advances it to the next occurrence instead of retiring it.

  • Hidden-project detection is the client model's judgment call — the server just provides the split action to break a task into sub-tasks.

Tests

npm test

61 unit tests (API mocked, no live calls) plus a smoke test that boots the real server over stdio without any token.

Available Tools

3 tools
apply_changesApply approved changesA

Executes an explicit, already-approved list of changes against Todoist. ONLY call this with changes the user has explicitly approved ITEM BY ITEM in chat. Never infer, batch, or add a change the user did not individually confirm — if the user vetoed or didn't respond to an item, leave it out of the changes array entirely.

Each change is { taskId, action, params? } where action is one of:

  • reschedule: params.dueDate (YYYY-MM-DD) OR params.dueString (Todoist natural language like "next monday"). Provide exactly one.

  • set_priority: params.priority, an integer 1-4, sent RAW to the API — 4 is highest/urgent, 1 is normal (inverse of the UI's P1 label). Do not remap.

  • move_to_project: params.projectName (a name, e.g. "Someday/Maybe"). The server finds the project by exact case-insensitive name, or creates it if missing, then moves the task there. This is the primary way to retire a task without deleting it.

  • reword: params.content, the new task text.

  • complete: no params. Marks the task done — the other way to retire a task.

  • apply_label: params.label. Fetches the task, appends the label if not already present, and saves it.

There is NO delete action in this server — tasks are never destroyed, only completed or moved. split also does not exist in v1.

Input validation note: the changes array is validated as a whole against a strict schema before any writes happen. If ANY item is malformed (unknown action, missing required params, wrong type), the ENTIRE call is rejected and NOTHING is written — no partial execution of a batch that contained a bad item. This is deliberately the safer of two valid designs. Once validation passes, each item is executed independently and the result reports success/failure per item (so a live API failure on one item, e.g. a task that was deleted in Todoist meanwhile, does not stop the others).

Inputs: changes (required).

get_overdue_tasksGet overdue tasksA

Returns every currently-overdue Todoist task as weekly-review candidates: { id, content, projectId, projectName, priority, dueDate, daysOverdue, timesRescheduled? }.

This tool is READ-ONLY and writes nothing. After calling it, propose a fix for EACH task in chat (reschedule to a concrete date, change priority, or retire it via complete / move_to_project to a project like "Someday/Maybe") and get the user's explicit approval or veto PER ITEM before ever calling apply_changes. Never batch-apply changes the user hasn't individually confirmed.

Signal reading guide: higher daysOverdue and higher timesRescheduled together indicate a stronger candidate to RETIRE (complete or move to Someday/Maybe) rather than reschedule yet again — a task rescheduled 6 times is a task the user isn't going to do. A task that is merely a day or two overdue with 0 reschedules is a normal reschedule candidate. timesRescheduled is omitted entirely when Todoist didn't report it for that task.

IMPORTANT: priority is the raw Todoist API value (1-4) where 4 = highest/urgent and 1 = normal. This is the INVERSE of the Todoist UI's "P1" (P1 in the UI = API value 4). Do not remap it — read it as-is and account for the inversion when you describe it.

Inputs: no named parameters.

get_projectsGet projectsA

Returns every Todoist project as { id, name } (paginated to completion). Use this to see what projects already exist — in particular, to check whether a "Someday/Maybe" (or similarly named) retirement project already exists before proposing to move a task there. You do NOT need to create it yourself: apply_changes' move_to_project action finds-or-creates the target project by name automatically.

Inputs: no named parameters.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.9/5.0

    Scored across 3 tools

    Disambiguation5/5

    Each tool has a distinct purpose: get_overdue_tasks reads overdue items, get_projects lists projects, apply_changes writes changes. No overlap, clear boundaries.

    Naming Consistency5/5

    All names follow a consistent verb_noun pattern in snake_case: get_overdue_tasks, get_projects, apply_changes. The verbs accurately describe the action.

    Tool Count5/5

    Three tools are well-scoped for weekly review: data retrieval (overdue tasks, projects) and change execution. No extraneous or missing tools.

    Completeness5/5

    The set covers the full weekly review workflow: get overdue tasks, see available projects, and apply all required actions (reschedule, priority, move, complete, etc.). No obvious gaps.

    Related MCP Connectors

    Related MCP Servers