Skip to main content
Glama
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": {} }
```