todoist-mcp
by lawheitung
README.md
# Todoist MCP — Cloudflare Worker
A Model Context Protocol (MCP) server for Todoist, hosted on Cloudflare Workers. Lets any AI assistant (Claude, GPT, Gemini, etc.) read and manage your Todoist tasks.
> Built against the **Todoist unified API v1** (`https://api.todoist.com/api/v1`). The older REST v2 / Sync v9 APIs are deprecated.
---
## How it works
```
Telegram message
↓
Your bot
↓
LLM (Claude / GPT / Gemini / etc.)
↓ calls tools via MCP JSON-RPC
This Worker ←→ Todoist API
```
The Worker exposes a single `POST /` endpoint that speaks [MCP JSON-RPC](https://modelcontextprotocol.io). Your bot connects to it and the LLM can call any of the 6 tools to read/write tasks.
---
## Your data model
You use Todoist's task/subtask hierarchy to simulate projects:
| Todoist concept | Your meaning |
|---|---|
| Top-level task (no parent) | "Project" — e.g. *Create telegram assistant bot* |
| Subtask (has parent_id) | Actual step — e.g. *Set up todoist MCP* |
| Top-level task (standalone) | Simple task — e.g. *Mop the floor* |
This matters for the AI: when you say "add a task to my Telegram bot project", the AI should find the parent task ID first, then call `add_task` with that `parent_id`.
---
## Tools
| Tool | What it does |
|---|---|
| `get_tasks` | List all tasks, optionally filtered. Use `mode=tree` to see subtasks nested under their parent. |
| `add_task` | Create a top-level task or subtask (pass `parent_id` to nest it). |
| `update_task` | Edit title, description, due date, priority, or labels. |
| `complete_task` | Mark a task done. |
| `delete_task` | Delete a task (and all its subtasks). |
| `move_task` | Move a subtask under a different parent (different "project"), or promote to top-level. |
### Priority values
- `1` = Normal (default)
- `2` = Medium
- `3` = High
- `4` = Urgent
### Filtering
The `filter` parameter accepts Todoist [filter query syntax](https://www.todoist.com/help/articles/introduction-to-filters-V98wIH) (e.g. `"today"`, `"overdue"`, `"@work & p1"`). When set, it takes precedence over `mode`/`parent_id`/`label` and returns a flat list.
---
## Setup
### 1. Install dependencies
```bash
npm install
```
### 2. Set your secrets
Get your Todoist token from [todoist.com/app/settings/integrations/developer](https://todoist.com/app/settings/integrations/developer).
```bash
# Your Todoist API token
npx wrangler secret put TODOIST_API_TOKEN
# A shared secret your bot sends to authenticate to this Worker.
# Generate a long random string, e.g.: openssl rand -hex 32
npx wrangler secret put MCP_SECRET
```
> **Why MCP_SECRET?** The Worker is on the public internet. Without this gate, anyone who finds the URL could read, edit, and delete your entire Todoist. Your bot must send it as `Authorization: Bearer <MCP_SECRET>` on every request.
### 3. Deploy
```bash
npm run deploy
```
Your endpoint: `https://todoist-mcp.<your-subdomain>.workers.dev`
### Local development
```bash
# Put both secrets in .dev.vars (already gitignored):
# TODOIST_API_TOKEN=your_token_here
# MCP_SECRET=pick_a_long_random_string_here
npm run dev
# → http://localhost:8787
```
---
## Connecting to your AI
See [`ai-system-prompt.md`](./ai-system-prompt.md) for the system prompt block to add to your Telegram bot so the AI knows how to use these tools correctly.
### How to call a tool (raw JSON-RPC)
Every request needs the `Authorization: Bearer <MCP_SECRET>` header.
```json
POST https://todoist-mcp.<your-subdomain>.workers.dev
Authorization: Bearer <your MCP_SECRET>
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "add_task",
"arguments": {
"content": "Set up todoist MCP",
"parent_id": "12345678",
"due_string": "tomorrow",
"priority": 3
}
}
}
```
### Discover available tools
```json
{ "jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {} }
```
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues