Skip to main content
Glama
sjvadrevu
by sjvadrevu

todoist-mcp-server

A custom Node.js Model Context Protocol (MCP) server that connects Claude to Todoist using a permanent API token. No OAuth, no re-authentication, works identically on Claude web, Claude Code, and Claude mobile.

Why this exists

The problem

Todoist needs to work across three Claude surfaces:

  • Claude web (claude.ai) on PC

  • Claude Code on PC and Linux

  • Claude mobile on iPhone

Every existing solution fails at least one of these. The core issue is authentication. Todoist's official MCP server (ai.todoist.net/mcp) uses OAuth, but Todoist does not issue OAuth refresh tokens. When the access token expires, Claude cannot silently refresh it and forces manual re-authentication. On Claude mobile this is impossible to fix: you cannot re-authenticate from the iOS app. This is a confirmed bug tracked in the Claude Code issue tracker: #5706, #12447, #19456, #28256.

What we evaluated

Option

Why it failed

Todoist official MCP

OAuth with no refresh token. Re-auths daily. Broken on mobile.

ClickUp

Official MCP in Claude directory but free tier caps at 300 MCP calls/day.

Microsoft To Do

M365 connector requires a business Entra tenant. Personal Microsoft accounts explicitly blocked.

Trello

No hosted MCP URL. All implementations are local stdio only, which don't work on Claude web or mobile.

TickTick

Local stdio only, no hosted URL.

Apple Reminders via CalDAV

iOS 13+ broke all third-party CalDAV write access to the native Reminders app. Items written via CalDAV do not appear in the app. Not fixable.

Zapier MCP

Free tier: 100 tasks/month. Each MCP call costs 2 tasks = 50 calls/month total.

n8n cloud

No meaningful free tier. $24/month plan exhausted in ~9 days by polling.

Composio

Works for Claude Code only via local config. Cannot be used as a remote connector for Claude web or mobile.

Bindify

Unverified indie credential proxy. No GitHub presence, no security audit.

Asana

Team-focused. Missing personal task richness, no Todoist import.

Why a custom server works

Todoist issues a permanent API token (Settings > Integrations > Developer). This token never expires unless manually revoked. It is not an OAuth token and requires no refresh.

A custom MCP server that authenticates to Todoist using this token (stored as an Azure environment variable, never in code) gives a hosted remote MCP URL that:

  • Never requires re-authentication on any surface

  • Works on Claude web, Claude Code, and Claude mobile identically

  • Has no rate limits from a middleware layer

  • Is fully under the owner's control

  • Updates by editing one file in GitHub (Azure redeploys automatically)

API version note

Todoist REST API v2 and Sync API v9 are deprecated as of early 2026. This server uses the unified Todoist API v1 at https://api.todoist.com/api/v1.


Architecture

flowchart TD
    A["๐Ÿ–ฅ๏ธ Claude web (claude.ai)"] --> D
    B["๐Ÿ’ป Claude Code (PC / Linux)"] --> D
    C["๐Ÿ“ฑ Claude mobile (iOS)"] --> D

    D["todoist-mcp-server\nNode.js ยท Azure App Service"]

    D -->|"Todoist API v1\npermanent Bearer token, never expires"| E["api.todoist.com"]

    style D fill:#0078d4,color:#fff,stroke:none
    style E fill:#db4035,color:#fff,stroke:none

Key design decisions:

  • Stateless HTTP transport: StreamableHTTPServerTransport with no session ID generator. Each request is self-contained. Safe on any platform that recycles processes (Azure App Service, Cloud Run, etc.).

  • Native fetch: Node 20+ has built-in fetch. No axios, no node-fetch at runtime.

  • X-Request-Id on mutations: Todoist uses this for idempotency on POST/DELETE. Generated per request with crypto.randomUUID().

  • Tool-per-operation: Each API endpoint is its own MCP tool with typed Zod parameters. Gives Claude precise descriptions and significantly improves how reliably it picks the right tool.

  • No Express: Node's built-in http module keeps dependencies minimal and startup fast.


Tools (33 total)

Tasks (8)

Tool

Description

get_tasks

List active tasks. Filter by project, section, label, or Todoist filter query (e.g. today, overdue, p1, #ProjectName)

get_task

Get a single task by ID

create_task

Create a task with content, description, project, section, parent, labels, priority (1-4), due date, assignee

update_task

Update any field of an existing task

complete_task

Mark a task as done

reopen_task

Reopen a completed task

delete_task

Permanently delete a task

move_task

Move a task to a different project, section, or parent task

Priority scale: 4 = urgent (red), 3 = high (orange), 2 = medium (blue), 1 = normal.

Projects (5)

Tool

Description

get_projects

List all projects

get_project

Get a single project by ID

create_project

Create a project with optional color, favorite flag, and view style (list or board)

update_project

Update project name, color, favorite status, or view style

delete_project

Delete a project and all its tasks

Sections (5)

Tool

Description

get_sections

List all sections, optionally filtered by project

get_section

Get a single section by ID

create_section

Create a section inside a project

update_section

Rename a section

delete_section

Delete a section and all its tasks

Labels (5)

Tool

Description

get_labels

List all personal labels

get_label

Get a single label by ID

create_label

Create a label with optional color, order, and favorite flag

update_label

Update a label's name, color, or favorite status

delete_label

Delete a label

Comments (5)

Tool

Description

get_comments

Get all comments on a task or project (one of task_id or project_id required)

get_comment

Get a single comment by ID

create_comment

Add a comment to a task or project

update_comment

Edit the text of a comment

delete_comment

Delete a comment

Reminders (4)

Requires Todoist Pro. Reminders fire a notification at a specific time or relative to a task's due date.

Tool

Description

get_reminders

Get all reminders, optionally filtered by task

create_reminder

Create a reminder: absolute (specific datetime) or relative (N minutes before due)

update_reminder

Update an existing reminder's time

delete_reminder

Delete a reminder

Productivity stats (1)

Tool

Description

get_productivity_stats

Karma score, task completion counts, streaks, and daily/weekly goal progress


Adding to Claude

claude.ai (web and mobile)

  1. Go to claude.ai and sign in.

  2. Click your profile icon, then Customize.

  3. Go to Connectors and click +.

  4. Select Add custom connector and fill in:

    • Name: Todoist

    • Remote MCP server URL: https://your-deployed-app-url/mcp

    • Leave all OAuth fields blank.

  5. Click Add.

Syncs automatically to the Claude mobile app and Claude Code. No re-authentication ever.

Claude Code (.mcp.json)

{
  "mcpServers": {
    "todoist": {
      "type": "http",
      "url": "https://your-deployed-app-url/mcp"
    }
  }
}

Local development

npm install
export TODOIST_API_TOKEN=your_token_here
npm start

Health check: http://localhost:8080/health MCP endpoint: http://localhost:8080/mcp

Get your API token from: todoist.com/app/settings/integrations/developer


A note

This server exists because I love both Claude and Todoist, and the broken OAuth re-authentication flow was ruining the experience of using them together. Hopefully this workaround becomes unnecessary someday. If Anthropic fixes Claude's MCP OAuth token refresh (#5706, #12447, #19456, #28256) or Todoist issues proper refresh tokens, the official MCP server at ai.todoist.net/mcp would make this redundant. Until then, this works.


License

MIT. Copyright (c) 2026 Shashank Vadrevu. See LICENSE.

A
license - permissive license
-
quality - not tested
C
maintenance

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sjvadrevu/todoist-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server