todoist-mcp 1.2.4 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -12
- package/dist/tools/tasks.js +23 -21
- package/dist/utils/TodoistClient.d.ts +1 -1
- package/dist/utils/TodoistClient.js +10 -23
- package/dist/utils/version.d.ts +1 -1
- package/dist/utils/version.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -32,24 +32,43 @@ You'll need a Todoist API token to use this MCP server.
|
|
|
32
32
|
2. Navigate to Settings → Integrations
|
|
33
33
|
3. Find your API token under "Developer"
|
|
34
34
|
|
|
35
|
-
### Usage
|
|
35
|
+
### Usage
|
|
36
36
|
|
|
37
|
-
Add to your
|
|
37
|
+
Add to `mcpServers` in your platform config:
|
|
38
38
|
|
|
39
39
|
```json
|
|
40
|
-
{
|
|
41
|
-
"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
"args": ["-y", "todoist-mcp"],
|
|
45
|
-
"env": {
|
|
46
|
-
"API_KEY": "your_todoist_api_token_here"
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
40
|
+
"todoist": {
|
|
41
|
+
"command": "npx",
|
|
42
|
+
"args": ["-y", "todoist-mcp"],
|
|
43
|
+
"env": { "API_KEY": "your_todoist_api_token" }
|
|
50
44
|
}
|
|
51
45
|
```
|
|
52
46
|
|
|
47
|
+
| Platform | Config |
|
|
48
|
+
|----------|--------|
|
|
49
|
+
| Claude Desktop | `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS), `%APPDATA%\Claude\` (Windows) |
|
|
50
|
+
| Claude Code | `.mcp.json` — `claude mcp add --transport stdio --scope project --env API_KEY=token todoist -- npx -y todoist-mcp` |
|
|
51
|
+
| Cursor | `~/.cursor/mcp.json` or `.cursor/mcp.json` |
|
|
52
|
+
| Codex | `~/.codex/config.toml` or `.codex/config.toml` — see example below |
|
|
53
|
+
| Gemini CLI | `~/.gemini/settings.json` — `gemini mcp add -e API_KEY=token todoist npx -y todoist-mcp` |
|
|
54
|
+
|
|
55
|
+
**Codex** — CLI:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
codex mcp add todoist --env API_KEY=your_token -- npx -y todoist-mcp
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Or add to `config.toml`:
|
|
62
|
+
|
|
63
|
+
```toml
|
|
64
|
+
[mcp_servers.todoist]
|
|
65
|
+
command = "npx"
|
|
66
|
+
args = ["-y", "todoist-mcp"]
|
|
67
|
+
|
|
68
|
+
[mcp_servers.todoist.env]
|
|
69
|
+
API_KEY = "your_todoist_api_token"
|
|
70
|
+
```
|
|
71
|
+
|
|
53
72
|
## Available Tools
|
|
54
73
|
|
|
55
74
|
### Tasks
|
package/dist/tools/tasks.js
CHANGED
|
@@ -48,21 +48,29 @@ const create_fields = {
|
|
|
48
48
|
};
|
|
49
49
|
createApiHandler({
|
|
50
50
|
name: 'get_tasks_list',
|
|
51
|
-
description: 'Get tasks list from Todoist',
|
|
51
|
+
description: 'Get tasks list from Todoist. For advanced filtering use get_tasks_by_filter tool',
|
|
52
52
|
schemaShape: {
|
|
53
53
|
project_id: z.string().optional().describe('Filter by project'),
|
|
54
54
|
section_id: z.string().optional().describe('Filter by section'),
|
|
55
55
|
label: z.string().optional().describe('Filter by label'),
|
|
56
|
-
filter: z
|
|
57
|
-
.string()
|
|
58
|
-
.optional()
|
|
59
|
-
.describe('Natural language english filter like "search: keyword", "today", "date before: +4 hours", "date after: May 5", "no date", "no time", "overdue", "7 days & @waiting", "created before: -365 days", "assigned to: person", "added by: me", "#Project & !assigned", "subtask", "!subtask", "P1 | P2", "today & @email", "@work | @office", "(today | overdue) & #Work", "all & 7 days", "!assigned", "Today & !#Work"'),
|
|
60
56
|
ids: z.string().optional().describe('Comma-separated list of task IDs'),
|
|
61
57
|
limit: z.number().optional().default(50),
|
|
62
58
|
},
|
|
63
59
|
method: 'GET',
|
|
64
60
|
path: '/tasks',
|
|
65
61
|
});
|
|
62
|
+
createApiHandler({
|
|
63
|
+
name: 'get_tasks_by_filter',
|
|
64
|
+
description: 'Get tasks from Todoist using filter language. Use for queries like "today", "overdue", "P1", date-based filters, label/project filters in query syntax',
|
|
65
|
+
schemaShape: {
|
|
66
|
+
query: z
|
|
67
|
+
.string()
|
|
68
|
+
.describe('Todoist filter query. Examples: "today", "overdue", "today | overdue", "no date", "no time", "P1 | P2", "7 days & @waiting", "created before: -365 days", "assigned to: person", "added by: me", "#Project & !assigned", "subtask", "!subtask", "today & @email", "@work | @office", "(today | overdue) & #Work", "all & 7 days", "!assigned", "search: keyword"'),
|
|
69
|
+
limit: z.number().optional().default(50),
|
|
70
|
+
},
|
|
71
|
+
method: 'GET',
|
|
72
|
+
path: '/tasks/filter',
|
|
73
|
+
});
|
|
66
74
|
createBatchApiHandler({
|
|
67
75
|
name: 'create_tasks',
|
|
68
76
|
description: 'Create new tasks in Todoist',
|
|
@@ -191,18 +199,20 @@ createSyncApiHandler({
|
|
|
191
199
|
return { valid: true };
|
|
192
200
|
},
|
|
193
201
|
});
|
|
194
|
-
createHandler('get_completed_tasks', 'Get completed tasks from Todoist with filtering options', {
|
|
195
|
-
project_id: z.string().optional().describe('Filter by specific project ID'),
|
|
196
|
-
section_id: z.string().optional().describe('Filter by specific section ID'),
|
|
197
|
-
parent_id: z.string().optional().describe('Filter by specific parent task ID'),
|
|
202
|
+
createHandler('get_completed_tasks', 'Get completed tasks from Todoist with filtering options. Date range limited to 3 months max.', {
|
|
198
203
|
since: z
|
|
199
204
|
.string()
|
|
200
|
-
.
|
|
201
|
-
.describe('Return tasks completed since this date (YYYY-MM-DD format)'),
|
|
205
|
+
.describe('Start of completion date range (ISO 8601 date-time, e.g. "2025-01-01T00:00:00Z")'),
|
|
202
206
|
until: z
|
|
207
|
+
.string()
|
|
208
|
+
.describe('End of completion date range (ISO 8601 date-time, e.g. "2025-03-31T23:59:59Z")'),
|
|
209
|
+
project_id: z.string().optional().describe('Filter by specific project ID'),
|
|
210
|
+
section_id: z.string().optional().describe('Filter by specific section ID'),
|
|
211
|
+
parent_id: z.string().optional().describe('Filter by specific parent task ID'),
|
|
212
|
+
cursor: z
|
|
203
213
|
.string()
|
|
204
214
|
.optional()
|
|
205
|
-
.describe('
|
|
215
|
+
.describe('Cursor for pagination (from next_cursor in previous response)'),
|
|
206
216
|
limit: z
|
|
207
217
|
.number()
|
|
208
218
|
.int()
|
|
@@ -211,8 +221,6 @@ createHandler('get_completed_tasks', 'Get completed tasks from Todoist with filt
|
|
|
211
221
|
.optional()
|
|
212
222
|
.default(50)
|
|
213
223
|
.describe('Number of tasks to return (max 200)'),
|
|
214
|
-
offset: z.number().int().min(0).optional().describe('Offset for pagination'),
|
|
215
|
-
annotation_type: z.string().optional().describe('Filter by annotation type'),
|
|
216
224
|
}, async (args) => {
|
|
217
225
|
const params = {};
|
|
218
226
|
Object.entries(args).forEach(([key, value]) => {
|
|
@@ -220,11 +228,5 @@ createHandler('get_completed_tasks', 'Get completed tasks from Todoist with filt
|
|
|
220
228
|
params[key] = typeof value === 'number' ? value.toString() : value;
|
|
221
229
|
}
|
|
222
230
|
});
|
|
223
|
-
|
|
224
|
-
return {
|
|
225
|
-
completed_tasks: response.items || [],
|
|
226
|
-
projects: response.projects || {},
|
|
227
|
-
sections: response.sections || {},
|
|
228
|
-
total: response.items?.length || 0,
|
|
229
|
-
};
|
|
231
|
+
return todoistApi.getCompletedTasks(params);
|
|
230
232
|
});
|
|
@@ -31,7 +31,7 @@ export declare class TodoistClient {
|
|
|
31
31
|
*/
|
|
32
32
|
sync(commands: Array<SyncCommand>): Promise<any>;
|
|
33
33
|
/**
|
|
34
|
-
* Get completed tasks
|
|
34
|
+
* Get completed tasks via REST API
|
|
35
35
|
* @param params - Query parameters for filtering completed tasks
|
|
36
36
|
* @returns API response data with completed tasks
|
|
37
37
|
*/
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
import { v4 as uuidv4 } from 'uuid';
|
|
3
3
|
import { log } from './helpers.js';
|
|
4
|
-
const API_BASE_URL = 'https://api.todoist.com/
|
|
5
|
-
const API_SYNC_BASE_URL = 'https://api.todoist.com/sync/v9';
|
|
4
|
+
const API_BASE_URL = 'https://api.todoist.com/api/v1';
|
|
6
5
|
export class TodoistClient {
|
|
7
6
|
apiToken;
|
|
8
7
|
constructor(apiToken) {
|
|
@@ -53,7 +52,12 @@ export class TodoistClient {
|
|
|
53
52
|
method: 'GET',
|
|
54
53
|
headers: this.getHeaders(),
|
|
55
54
|
});
|
|
56
|
-
|
|
55
|
+
const data = await this.handleResponse(response);
|
|
56
|
+
// API v1 wraps list responses in { results: [...] }
|
|
57
|
+
if (data && typeof data === 'object' && Array.isArray(data.results)) {
|
|
58
|
+
return data.results;
|
|
59
|
+
}
|
|
60
|
+
return data;
|
|
57
61
|
}
|
|
58
62
|
/**
|
|
59
63
|
* Make a POST request to Todoist API
|
|
@@ -91,7 +95,7 @@ export class TodoistClient {
|
|
|
91
95
|
* @returns API response data
|
|
92
96
|
*/
|
|
93
97
|
async sync(commands) {
|
|
94
|
-
const url = `${
|
|
98
|
+
const url = `${API_BASE_URL}/sync`;
|
|
95
99
|
log(`Making SYNC request to: ${url} with commands:`, JSON.stringify(commands, null, 2));
|
|
96
100
|
const response = await fetch(url, {
|
|
97
101
|
method: 'POST',
|
|
@@ -101,28 +105,11 @@ export class TodoistClient {
|
|
|
101
105
|
return this.handleResponse(response);
|
|
102
106
|
}
|
|
103
107
|
/**
|
|
104
|
-
* Get completed tasks
|
|
108
|
+
* Get completed tasks via REST API
|
|
105
109
|
* @param params - Query parameters for filtering completed tasks
|
|
106
110
|
* @returns API response data with completed tasks
|
|
107
111
|
*/
|
|
108
112
|
async getCompletedTasks(params = {}) {
|
|
109
|
-
|
|
110
|
-
log(`Making completed tasks request to: ${url} with params:`, JSON.stringify(params, null, 2));
|
|
111
|
-
const formData = new URLSearchParams();
|
|
112
|
-
for (const [key, value] of Object.entries(params)) {
|
|
113
|
-
if (value) {
|
|
114
|
-
formData.append(key, value);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
const response = await fetch(url, {
|
|
118
|
-
method: 'POST',
|
|
119
|
-
headers: {
|
|
120
|
-
Authorization: `Bearer ${this.apiToken}`,
|
|
121
|
-
'Content-Type': 'application/x-www-form-urlencoded',
|
|
122
|
-
'X-Request-Id': uuidv4(),
|
|
123
|
-
},
|
|
124
|
-
body: formData.toString(),
|
|
125
|
-
});
|
|
126
|
-
return this.handleResponse(response);
|
|
113
|
+
return this.get('/tasks/completed/by_completion_date', params);
|
|
127
114
|
}
|
|
128
115
|
}
|
package/dist/utils/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "1.
|
|
1
|
+
export declare const version = "1.3.1";
|
package/dist/utils/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Auto-generated file, do not edit
|
|
2
|
-
export const version = '1.
|
|
2
|
+
export const version = '1.3.1';
|
package/package.json
CHANGED