@kydycode/todoist-mcp-server-ext 0.2.0 → 0.4.0
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 +129 -100
- package/dist/index.js +402 -8
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -3,68 +3,99 @@
|
|
|
3
3
|
|
|
4
4
|
> **Extended Version** - Forked and enhanced by [kydycode](https://github.com/kydycode) from the original [@abhiz123/todoist-mcp-server](https://github.com/abhiz123/todoist-mcp-server)
|
|
5
5
|
|
|
6
|
-
A comprehensive MCP (Model Context Protocol) server implementation that provides full integration between Claude and Todoist. This **extended version** includes additional features, improved compatibility, and enhanced functionality using the complete Todoist API with the latest MCP SDK.
|
|
6
|
+
A comprehensive MCP (Model Context Protocol) server implementation that provides full integration between Claude and Todoist. This **extended version** includes additional features, improved compatibility, and enhanced functionality using the complete Todoist API with the latest MCP SDK (`@doist/todoist-api-typescript@4.0.4`).
|
|
7
7
|
|
|
8
8
|
## 🆕 Extended Version Features
|
|
9
9
|
|
|
10
10
|
### 🔧 **Technical Improvements**
|
|
11
|
-
* **Updated
|
|
12
|
-
* **
|
|
13
|
-
* **
|
|
14
|
-
* **
|
|
15
|
-
* **
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
* **
|
|
20
|
-
* **
|
|
21
|
-
* **
|
|
22
|
-
* **Task
|
|
23
|
-
* **Task
|
|
24
|
-
* **
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
* **
|
|
29
|
-
* **
|
|
30
|
-
* **
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
* **
|
|
35
|
-
* **
|
|
36
|
-
|
|
37
|
-
|
|
11
|
+
* **Updated Todoist SDK**: Now using `@doist/todoist-api-typescript@4.0.4`.
|
|
12
|
+
* **Updated MCP SDK Compatibility**: Compatible with MCP SDK 0.5.0.
|
|
13
|
+
* **Enhanced Error Handling**: Comprehensive error handling with detailed error messages.
|
|
14
|
+
* **Improved TypeScript Support**: Better type safety and compatibility.
|
|
15
|
+
* **Optimized API Usage**: Efficient use of Todoist API, including `getTasksByFilter` for robust search and `moveTasks` for semantic task movement.
|
|
16
|
+
* **Better Response Formatting**: Enhanced task, project, and label formatting for better readability, including project names in search results.
|
|
17
|
+
|
|
18
|
+
### ✨ **Enhanced Task Management (10 Tools)**
|
|
19
|
+
* **Direct ID-based Operations**: Efficient task operations using task IDs.
|
|
20
|
+
* **Comprehensive Task Creation**: Support for subtasks, labels, projects, sections, priorities.
|
|
21
|
+
* **Quick Add Integration**: Natural language task creation using Todoist's Quick Add.
|
|
22
|
+
* **Advanced Task Search**: Robust keyword search using Todoist's filter engine (`search: your query`).
|
|
23
|
+
* **Task Movement Capabilities**: Move tasks between projects, sections, or make them subtasks.
|
|
24
|
+
* **Task State Management**: Complete, reopen, and manage task lifecycle.
|
|
25
|
+
* **Detailed Task Output**: Search and get-task operations return more task details.
|
|
26
|
+
|
|
27
|
+
### 🗂️ **Complete Project Management (5 Tools)**
|
|
28
|
+
* **Full Project CRUD**: Create, read, update, delete projects with all properties.
|
|
29
|
+
* **Sub-project Support**: Create hierarchical project structures.
|
|
30
|
+
* **Project Customization**: Set colors, favorites, view styles (list/board).
|
|
31
|
+
* **Enhanced Project Listing**: Improved project retrieval with pagination and detailed formatting.
|
|
32
|
+
|
|
33
|
+
### 📋 **Section Management (4 Tools)**
|
|
34
|
+
* **Complete Section Operations**: Create, read, update, delete sections.
|
|
35
|
+
* **Project-specific Sections**: Filter and manage sections within projects.
|
|
36
|
+
* **Section Organization**: Proper ordering and structure management.
|
|
37
|
+
|
|
38
|
+
### 🏷️ **Label Management (5 Tools)**
|
|
39
|
+
* **Full Label CRUD**: Create, read, update, delete labels.
|
|
40
|
+
* **Label Customization**: Set names, colors, favorites, order.
|
|
41
|
+
* **Paginated Label Listing**: Efficiently retrieve all labels.
|
|
42
|
+
|
|
43
|
+
### 💬 **Comment Management (5 Tools)**
|
|
44
|
+
* **Complete Comment CRUD**: Create, read, update, delete comments on tasks and projects.
|
|
45
|
+
* **Attachment Support**: Add file attachments to comments with metadata.
|
|
46
|
+
* **Flexible Targeting**: Comments can be attached to either tasks or projects.
|
|
47
|
+
* **Paginated Comment Retrieval**: Efficiently browse through comment threads.
|
|
48
|
+
|
|
49
|
+
## 🛠️ Available Tools (Total 29)
|
|
38
50
|
|
|
39
51
|
### Task Operations (10 tools)
|
|
40
|
-
| Tool
|
|
41
|
-
|
|
42
|
-
| `todoist_create_task`
|
|
43
|
-
| `todoist_quick_add_task`
|
|
44
|
-
| `todoist_get_tasks`
|
|
45
|
-
| `todoist_get_task`
|
|
46
|
-
| `todoist_update_task`
|
|
47
|
-
| `todoist_delete_task`
|
|
48
|
-
| `todoist_complete_task`
|
|
49
|
-
| `todoist_reopen_task`
|
|
50
|
-
| `todoist_search_tasks`
|
|
52
|
+
| Tool | Description |
|
|
53
|
+
|---------------------------|-------------------------------------------------------------------------------------|
|
|
54
|
+
| `todoist_create_task` | Create tasks with full options (subtasks, labels, projects, sections, priorities). |
|
|
55
|
+
| `todoist_quick_add_task` | Natural language task creation using Todoist's Quick Add syntax. |
|
|
56
|
+
| `todoist_get_tasks` | Retrieve tasks with filtering (project, section, parent, label, IDs) and pagination. |
|
|
57
|
+
| `todoist_get_task` | Get a specific task by its ID, with detailed information. |
|
|
58
|
+
| `todoist_update_task` | Update task properties (content, description, due date, priority, labels). |
|
|
59
|
+
| `todoist_delete_task` | Delete task by ID. |
|
|
60
|
+
| `todoist_complete_task` | Mark task complete. |
|
|
61
|
+
| `todoist_reopen_task` | Reopen completed task. |
|
|
62
|
+
| `todoist_search_tasks` | Search tasks using Todoist's filter engine (e.g., `search: keyword`). |
|
|
63
|
+
| `todoist_move_task` | Move a task to a different project, section, or make it a subtask. |
|
|
51
64
|
|
|
52
65
|
### Project Operations (5 tools)
|
|
53
|
-
| Tool
|
|
54
|
-
|
|
55
|
-
| `todoist_get_projects`
|
|
56
|
-
| `todoist_get_project`
|
|
57
|
-
| `todoist_create_project`
|
|
58
|
-
| `todoist_update_project`
|
|
59
|
-
| `todoist_delete_project`
|
|
66
|
+
| Tool | Description |
|
|
67
|
+
|----------------------------|-------------------------------------------------------------------------------|
|
|
68
|
+
| `todoist_get_projects` | List all active projects with pagination support. |
|
|
69
|
+
| `todoist_get_project` | Get a specific project by its ID. |
|
|
70
|
+
| `todoist_create_project` | Create new project (name, color, favorite, view style, sub-projects). |
|
|
71
|
+
| `todoist_update_project` | Update project properties. |
|
|
72
|
+
| `todoist_delete_project` | Delete project by ID. |
|
|
60
73
|
|
|
61
74
|
### Section Operations (4 tools)
|
|
62
|
-
| Tool
|
|
63
|
-
|
|
64
|
-
| `todoist_get_sections`
|
|
65
|
-
| `todoist_create_section`
|
|
66
|
-
| `todoist_update_section`
|
|
67
|
-
| `todoist_delete_section`
|
|
75
|
+
| Tool | Description |
|
|
76
|
+
|----------------------------|-----------------------------------------------------------------|
|
|
77
|
+
| `todoist_get_sections` | List sections (all sections or project-specific). |
|
|
78
|
+
| `todoist_create_section` | Create section in project (name, project, ordering). |
|
|
79
|
+
| `todoist_update_section` | Update section name. |
|
|
80
|
+
| `todoist_delete_section` | Delete section by ID. |
|
|
81
|
+
|
|
82
|
+
### Label Operations (5 tools)
|
|
83
|
+
| Tool | Description |
|
|
84
|
+
|--------------------------|--------------------------------------------------------------------|
|
|
85
|
+
| `todoist_create_label` | Create a new label (name, color, favorite, order). |
|
|
86
|
+
| `todoist_get_label` | Get a specific label by its ID. |
|
|
87
|
+
| `todoist_get_labels` | List all labels with pagination support. |
|
|
88
|
+
| `todoist_update_label` | Update an existing label by its ID (name, color, favorite, order). |
|
|
89
|
+
| `todoist_delete_label` | Delete a label by its ID. |
|
|
90
|
+
|
|
91
|
+
### Comment Operations (5 tools)
|
|
92
|
+
| Tool | Description |
|
|
93
|
+
|----------------------------|-----------------------------------------------------------------|
|
|
94
|
+
| `todoist_create_comment` | Create a new comment on a task or project (with attachments). |
|
|
95
|
+
| `todoist_get_comment` | Get a specific comment by its ID. |
|
|
96
|
+
| `todoist_get_comments` | Get comments for a task or project with pagination support. |
|
|
97
|
+
| `todoist_update_comment` | Update an existing comment by its ID. |
|
|
98
|
+
| `todoist_delete_comment` | Delete a comment by its ID. |
|
|
68
99
|
|
|
69
100
|
## 🚀 Installation & Setup
|
|
70
101
|
|
|
@@ -97,7 +128,7 @@ Add to your `claude_desktop_config.json`:
|
|
|
97
128
|
"mcpServers": {
|
|
98
129
|
"todoist-mcp-server": {
|
|
99
130
|
"command": "node",
|
|
100
|
-
"args": ["/path/to/todoist-mcp-server-ext/dist/index.js"],
|
|
131
|
+
"args": ["/path/to/your/todoist-mcp-server-ext/dist/index.js"],
|
|
101
132
|
"env": {
|
|
102
133
|
"TODOIST_API_TOKEN": "your_api_token_here"
|
|
103
134
|
}
|
|
@@ -106,13 +137,13 @@ Add to your `claude_desktop_config.json`:
|
|
|
106
137
|
}
|
|
107
138
|
```
|
|
108
139
|
|
|
109
|
-
#### Option 2: Run via npm (
|
|
140
|
+
#### Option 2: Run via npm/npx (recommended for published version)
|
|
110
141
|
```json
|
|
111
142
|
{
|
|
112
143
|
"mcpServers": {
|
|
113
144
|
"todoist-mcp-server": {
|
|
114
145
|
"command": "npx",
|
|
115
|
-
"args": ["-y", "@kydycode/todoist-mcp-server-ext"],
|
|
146
|
+
"args": ["-y", "@kydycode/todoist-mcp-server-ext@latest"],
|
|
116
147
|
"env": {
|
|
117
148
|
"TODOIST_API_TOKEN": "your_api_token_here"
|
|
118
149
|
}
|
|
@@ -124,7 +155,7 @@ Add to your `claude_desktop_config.json`:
|
|
|
124
155
|
#### Option 3: Install globally first
|
|
125
156
|
```bash
|
|
126
157
|
# Install the extended version globally
|
|
127
|
-
npm install -g @kydycode/todoist-mcp-server-ext
|
|
158
|
+
npm install -g @kydycode/todoist-mcp-server-ext@latest
|
|
128
159
|
|
|
129
160
|
# Then use in Claude Desktop config
|
|
130
161
|
{
|
|
@@ -141,53 +172,44 @@ npm install -g @kydycode/todoist-mcp-server-ext
|
|
|
141
172
|
|
|
142
173
|
## 📖 Usage Examples
|
|
143
174
|
|
|
144
|
-
### 🎯 Advanced Task Creation
|
|
175
|
+
### 🎯 Advanced Task Creation & Management
|
|
145
176
|
```
|
|
146
|
-
"Create task 'Team Meeting
|
|
147
|
-
"Add
|
|
177
|
+
"Create task 'Team Meeting @Tomorrow #Work p1'"
|
|
178
|
+
"Add task 'Fix critical bug +KydyCode @DevProject L:Urgent L:Backend'"
|
|
148
179
|
"Quick add: 'Buy milk tomorrow at 2pm #shopping !p1'"
|
|
149
|
-
"
|
|
180
|
+
"Move task with ID {task_id} to project {project_id}"
|
|
181
|
+
"Search tasks: search: API deployment"
|
|
150
182
|
```
|
|
151
183
|
|
|
152
|
-
###
|
|
153
|
-
```
|
|
154
|
-
"Get all tasks in project 'Work'"
|
|
155
|
-
"Search for tasks containing 'meeting'"
|
|
156
|
-
"Show task details for specific task ID"
|
|
157
|
-
"Update task priority to urgent"
|
|
158
|
-
"Complete task and then reopen it"
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
### 🗂️ Project Organization
|
|
184
|
+
### 🗂️ Project, Section, Label, and Comment Management
|
|
162
185
|
```
|
|
163
186
|
"List all my projects"
|
|
164
|
-
"Create project '
|
|
165
|
-
"
|
|
166
|
-
"
|
|
167
|
-
"
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
```
|
|
172
|
-
"Create section 'In Progress' in Development project"
|
|
173
|
-
"List all sections in Work project"
|
|
174
|
-
"Update section name to 'Completed'"
|
|
175
|
-
"Delete empty section"
|
|
187
|
+
"Create project 'Q2 Planning' color:blue favorite:true view:board"
|
|
188
|
+
"Get sections for project {project_id}"
|
|
189
|
+
"Create label 'HighPriority' color:red isFavorite:true"
|
|
190
|
+
"List all labels"
|
|
191
|
+
"Add comment 'Great progress on this task!' to task {task_id}"
|
|
192
|
+
"Get all comments for project {project_id}"
|
|
193
|
+
"Update comment {comment_id} with new content"
|
|
176
194
|
```
|
|
177
195
|
|
|
178
196
|
## 🆚 Extended vs Original Comparison
|
|
179
197
|
|
|
180
|
-
| Feature
|
|
181
|
-
|
|
182
|
-
| **
|
|
183
|
-
| **
|
|
184
|
-
| **
|
|
185
|
-
| **
|
|
186
|
-
| **
|
|
187
|
-
| **
|
|
188
|
-
| **
|
|
189
|
-
| **
|
|
190
|
-
| **
|
|
198
|
+
| Feature | Original | Extended Version (`@kydycode/todoist-mcp-server-ext`) |
|
|
199
|
+
|-----------------------------|--------------------------------------|-------------------------------------------------------|
|
|
200
|
+
| **Todoist SDK Version** | Older | ✅ `@doist/todoist-api-typescript@4.0.4` |
|
|
201
|
+
| **MCP SDK Compatibility** | Older version | ✅ Latest MCP SDK 0.5.0+ |
|
|
202
|
+
| **Error Handling** | Basic | ✅ Comprehensive with detailed messages |
|
|
203
|
+
| **TypeScript Support** | Limited | ✅ Full type safety |
|
|
204
|
+
| **Task Operations** | Search-based, limited features | ✅ 10 Tools: Direct ID-based, `moveTasks`, robust search, QuickAdd, full CRUD-like ops |
|
|
205
|
+
| **Project Management** | Limited | ✅ 5 Tools: Full CRUD operations, sub-projects, pagination |
|
|
206
|
+
| **Section Management** | Basic | ✅ 4 Tools: Complete section operations |
|
|
207
|
+
| **Label Management** | Not Available | ✅ 5 Tools: Full CRUD operations, pagination |
|
|
208
|
+
| **Comment Management** | Not Available | ✅ 5 Tools: Full CRUD operations, attachments, pagination |
|
|
209
|
+
| **API Parameter Handling** | Inconsistent | ✅ Proper parameter validation |
|
|
210
|
+
| **Response Formatting** | Basic | ✅ Enhanced readability, more details |
|
|
211
|
+
| **Build System** | Issues | ✅ Clean compilation |
|
|
212
|
+
| **Search Functionality** | Basic local filter | ✅ Robust `getTasksByFilter` (Todoist engine) |
|
|
191
213
|
|
|
192
214
|
## 🔧 Development
|
|
193
215
|
|
|
@@ -195,9 +217,13 @@ npm install -g @kydycode/todoist-mcp-server-ext
|
|
|
195
217
|
```
|
|
196
218
|
src/
|
|
197
219
|
├── index.ts # Main server implementation with all tools
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
220
|
+
package.json # Dependencies and scripts
|
|
221
|
+
tsconfig.json # TypeScript configuration
|
|
222
|
+
README.md # This file
|
|
223
|
+
local-instructions.md # Personal publishing guide
|
|
224
|
+
LICENSE
|
|
225
|
+
.gitignore
|
|
226
|
+
dist/ # Compiled JavaScript output (after `npm run build`)
|
|
201
227
|
├── index.js
|
|
202
228
|
└── index.d.ts
|
|
203
229
|
```
|
|
@@ -211,14 +237,16 @@ npm install
|
|
|
211
237
|
npm run build
|
|
212
238
|
|
|
213
239
|
# Test the server (requires TODOIST_API_TOKEN)
|
|
214
|
-
|
|
240
|
+
# Example: Set token and pipe a list tools request
|
|
241
|
+
export TODOIST_API_TOKEN="your_actual_todoist_api_token"
|
|
242
|
+
echo '{"jsonrpc": "2.0", "method": "tools/list", "id": 1}' | node dist/index.js
|
|
215
243
|
```
|
|
216
244
|
|
|
217
245
|
### Development Scripts
|
|
218
246
|
```bash
|
|
219
|
-
npm run build # Compile TypeScript
|
|
220
|
-
npm run watch # Watch for changes and rebuild
|
|
221
|
-
npm run prepare # Pre-publish build
|
|
247
|
+
npm run build # Compile TypeScript and make output executable
|
|
248
|
+
npm run watch # Watch for changes and rebuild (doesn't make output executable)
|
|
249
|
+
npm run prepare # Pre-publish build (runs build)
|
|
222
250
|
```
|
|
223
251
|
|
|
224
252
|
## 🤝 Contributing
|
|
@@ -251,6 +279,7 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
|
|
|
251
279
|
## 🔗 Related Links
|
|
252
280
|
|
|
253
281
|
- **Extended Repository**: [kydycode/todoist-mcp-server-ext](https://github.com/kydycode/todoist-mcp-server-ext)
|
|
282
|
+
- **NPM Package**: [`@kydycode/todoist-mcp-server-ext`](https://www.npmjs.com/package/@kydycode/todoist-mcp-server-ext)
|
|
254
283
|
- **Original Repository**: [abhiz123/todoist-mcp-server](https://github.com/abhiz123/todoist-mcp-server)
|
|
255
284
|
- [Todoist API Documentation](https://developer.todoist.com/)
|
|
256
285
|
- [Model Context Protocol](https://modelcontextprotocol.io/)
|
package/dist/index.js
CHANGED
|
@@ -211,7 +211,7 @@ const REOPEN_TASK_TOOL = {
|
|
|
211
211
|
};
|
|
212
212
|
const MOVE_TASK_TOOL = {
|
|
213
213
|
name: "todoist_move_task",
|
|
214
|
-
description: "Move a task to a different project, section, or make it a subtask of another task. Provide the taskId and exactly one of: projectId, sectionId, or parentId.",
|
|
214
|
+
description: "Move a single task (and its subtasks, if any) to a different project, section, or make it a subtask of another task. Provide the taskId and exactly one of: projectId, sectionId, or parentId.",
|
|
215
215
|
inputSchema: {
|
|
216
216
|
type: "object",
|
|
217
217
|
properties: {
|
|
@@ -238,6 +238,36 @@ const MOVE_TASK_TOOL = {
|
|
|
238
238
|
// A more complex JSON schema with oneOf could enforce this, but client support varies.
|
|
239
239
|
}
|
|
240
240
|
};
|
|
241
|
+
const BULK_MOVE_TASKS_TOOL = {
|
|
242
|
+
name: "todoist_bulk_move_tasks",
|
|
243
|
+
description: "Move multiple tasks (and their respective subtasks, if any; e.g., up to 10-20 parent tasks for best performance) to a different project, section, or make them subtasks of another task. Provide an array of taskIds and exactly one destination (projectId, sectionId, or parentId).",
|
|
244
|
+
inputSchema: {
|
|
245
|
+
type: "object",
|
|
246
|
+
properties: {
|
|
247
|
+
taskIds: {
|
|
248
|
+
type: "array",
|
|
249
|
+
items: { type: "string" },
|
|
250
|
+
description: "An array of task IDs to move.",
|
|
251
|
+
minItems: 1 // Ensure at least one task ID is provided
|
|
252
|
+
},
|
|
253
|
+
projectId: {
|
|
254
|
+
type: "string",
|
|
255
|
+
description: "The ID of the destination project. (Optional, use only one of projectId, sectionId, parentId)"
|
|
256
|
+
},
|
|
257
|
+
sectionId: {
|
|
258
|
+
type: "string",
|
|
259
|
+
description: "The ID of the destination section. (Optional, use only one of projectId, sectionId, parentId)"
|
|
260
|
+
},
|
|
261
|
+
parentId: {
|
|
262
|
+
type: "string",
|
|
263
|
+
description: "The ID of the parent task to move these tasks under. (Optional, use only one of projectId, sectionId, parentId)"
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
required: ["taskIds"]
|
|
267
|
+
// Note: Validation for providing exactly one of projectId, sectionId, or parentId
|
|
268
|
+
// is handled in the isBulkMoveTasksArgs type guard and the tool handler.
|
|
269
|
+
}
|
|
270
|
+
};
|
|
241
271
|
// Label Management Tools
|
|
242
272
|
const CREATE_LABEL_TOOL = {
|
|
243
273
|
name: "todoist_create_label",
|
|
@@ -419,13 +449,21 @@ const DELETE_PROJECT_TOOL = {
|
|
|
419
449
|
// Section Management Tools
|
|
420
450
|
const GET_SECTIONS_TOOL = {
|
|
421
451
|
name: "todoist_get_sections",
|
|
422
|
-
description: "Get all sections or sections for a specific project",
|
|
452
|
+
description: "Get all sections, or sections for a specific project. Supports pagination.",
|
|
423
453
|
inputSchema: {
|
|
424
454
|
type: "object",
|
|
425
455
|
properties: {
|
|
426
456
|
projectId: {
|
|
427
457
|
type: "string",
|
|
428
|
-
description: "Filter sections by project ID (optional)"
|
|
458
|
+
description: "Filter sections by project ID (optional)."
|
|
459
|
+
},
|
|
460
|
+
cursor: {
|
|
461
|
+
type: "string",
|
|
462
|
+
description: "Pagination cursor for next page (optional)."
|
|
463
|
+
},
|
|
464
|
+
limit: {
|
|
465
|
+
type: "number",
|
|
466
|
+
description: "Maximum number of sections to return (default: 50) (optional)."
|
|
429
467
|
}
|
|
430
468
|
}
|
|
431
469
|
}
|
|
@@ -508,6 +546,110 @@ const SEARCH_TASKS_TOOL = {
|
|
|
508
546
|
required: ["query"]
|
|
509
547
|
}
|
|
510
548
|
};
|
|
549
|
+
// Comment Management Tools
|
|
550
|
+
const CREATE_COMMENT_TOOL = {
|
|
551
|
+
name: "todoist_create_comment",
|
|
552
|
+
description: "Create a new comment on a task or project",
|
|
553
|
+
inputSchema: {
|
|
554
|
+
type: "object",
|
|
555
|
+
properties: {
|
|
556
|
+
content: {
|
|
557
|
+
type: "string",
|
|
558
|
+
description: "The content/text of the comment"
|
|
559
|
+
},
|
|
560
|
+
taskId: {
|
|
561
|
+
type: "string",
|
|
562
|
+
description: "Task ID to add comment to (provide either taskId or projectId, not both)"
|
|
563
|
+
},
|
|
564
|
+
projectId: {
|
|
565
|
+
type: "string",
|
|
566
|
+
description: "Project ID to add comment to (provide either taskId or projectId, not both)"
|
|
567
|
+
},
|
|
568
|
+
attachment: {
|
|
569
|
+
type: "object",
|
|
570
|
+
description: "Optional file attachment (optional)",
|
|
571
|
+
properties: {
|
|
572
|
+
fileName: { type: "string" },
|
|
573
|
+
fileType: { type: "string" },
|
|
574
|
+
fileUrl: { type: "string" },
|
|
575
|
+
resourceType: { type: "string" }
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
},
|
|
579
|
+
required: ["content"]
|
|
580
|
+
}
|
|
581
|
+
};
|
|
582
|
+
const GET_COMMENT_TOOL = {
|
|
583
|
+
name: "todoist_get_comment",
|
|
584
|
+
description: "Get a specific comment by its ID",
|
|
585
|
+
inputSchema: {
|
|
586
|
+
type: "object",
|
|
587
|
+
properties: {
|
|
588
|
+
commentId: {
|
|
589
|
+
type: "string",
|
|
590
|
+
description: "The ID of the comment to retrieve"
|
|
591
|
+
}
|
|
592
|
+
},
|
|
593
|
+
required: ["commentId"]
|
|
594
|
+
}
|
|
595
|
+
};
|
|
596
|
+
const GET_COMMENTS_TOOL = {
|
|
597
|
+
name: "todoist_get_comments",
|
|
598
|
+
description: "Get comments for a task or project with pagination support",
|
|
599
|
+
inputSchema: {
|
|
600
|
+
type: "object",
|
|
601
|
+
properties: {
|
|
602
|
+
taskId: {
|
|
603
|
+
type: "string",
|
|
604
|
+
description: "Task ID to get comments for (provide either taskId or projectId, not both)"
|
|
605
|
+
},
|
|
606
|
+
projectId: {
|
|
607
|
+
type: "string",
|
|
608
|
+
description: "Project ID to get comments for (provide either taskId or projectId, not both)"
|
|
609
|
+
},
|
|
610
|
+
cursor: {
|
|
611
|
+
type: "string",
|
|
612
|
+
description: "Pagination cursor for next page (optional)"
|
|
613
|
+
},
|
|
614
|
+
limit: {
|
|
615
|
+
type: "number",
|
|
616
|
+
description: "Maximum number of comments to return (optional)"
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
};
|
|
621
|
+
const UPDATE_COMMENT_TOOL = {
|
|
622
|
+
name: "todoist_update_comment",
|
|
623
|
+
description: "Update an existing comment by its ID",
|
|
624
|
+
inputSchema: {
|
|
625
|
+
type: "object",
|
|
626
|
+
properties: {
|
|
627
|
+
commentId: {
|
|
628
|
+
type: "string",
|
|
629
|
+
description: "The ID of the comment to update"
|
|
630
|
+
},
|
|
631
|
+
content: {
|
|
632
|
+
type: "string",
|
|
633
|
+
description: "New content/text for the comment"
|
|
634
|
+
}
|
|
635
|
+
},
|
|
636
|
+
required: ["commentId", "content"]
|
|
637
|
+
}
|
|
638
|
+
};
|
|
639
|
+
const DELETE_COMMENT_TOOL = {
|
|
640
|
+
name: "todoist_delete_comment",
|
|
641
|
+
description: "Delete a comment by its ID",
|
|
642
|
+
inputSchema: {
|
|
643
|
+
type: "object",
|
|
644
|
+
properties: {
|
|
645
|
+
commentId: {
|
|
646
|
+
type: "string",
|
|
647
|
+
description: "The ID of the comment to delete"
|
|
648
|
+
}
|
|
649
|
+
},
|
|
650
|
+
required: ["commentId"]
|
|
651
|
+
}
|
|
652
|
+
};
|
|
511
653
|
// Server implementation
|
|
512
654
|
const server = new Server({
|
|
513
655
|
name: "todoist-mcp-server-enhanced",
|
|
@@ -552,6 +694,22 @@ function formatProject(project) {
|
|
|
552
694
|
function formatLabel(label) {
|
|
553
695
|
return `- ${label.name} (ID: ${label.id})${label.color ? `\n Color: ${label.color}` : ''}${label.isFavorite ? `\n Favorite: Yes` : ''}${label.order ? `\n Order: ${label.order}` : ''}`;
|
|
554
696
|
}
|
|
697
|
+
// Helper function to format comment output
|
|
698
|
+
function formatComment(comment) {
|
|
699
|
+
let commentDetails = `- ID: ${comment.id}\n Content: ${comment.content}`;
|
|
700
|
+
if (comment.postedAt)
|
|
701
|
+
commentDetails += `\n Posted At: ${comment.postedAt}`;
|
|
702
|
+
if (comment.taskId)
|
|
703
|
+
commentDetails += `\n Task ID: ${comment.taskId}`;
|
|
704
|
+
if (comment.projectId)
|
|
705
|
+
commentDetails += `\n Project ID: ${comment.projectId}`;
|
|
706
|
+
if (comment.attachment) {
|
|
707
|
+
commentDetails += `\n Attachment: ${comment.attachment.fileName || 'File'} (${comment.attachment.fileType})`;
|
|
708
|
+
if (comment.attachment.fileUrl)
|
|
709
|
+
commentDetails += `\n File URL: ${comment.attachment.fileUrl}`;
|
|
710
|
+
}
|
|
711
|
+
return commentDetails;
|
|
712
|
+
}
|
|
555
713
|
// Type guards for arguments
|
|
556
714
|
function isCreateTaskArgs(args) {
|
|
557
715
|
return (typeof args === "object" &&
|
|
@@ -603,6 +761,7 @@ function isUpdateProjectArgs(args) {
|
|
|
603
761
|
typeof args.projectId === "string");
|
|
604
762
|
}
|
|
605
763
|
function isSectionArgs(args) {
|
|
764
|
+
// Allows empty object or object with optional projectId, cursor, limit
|
|
606
765
|
return typeof args === "object" && args !== null;
|
|
607
766
|
}
|
|
608
767
|
function isCreateSectionArgs(args) {
|
|
@@ -644,6 +803,21 @@ function isMoveTaskArgs(args) {
|
|
|
644
803
|
return providedDestinations.length === 1 &&
|
|
645
804
|
providedDestinations.every(dest => typeof dest === 'string');
|
|
646
805
|
}
|
|
806
|
+
function isBulkMoveTasksArgs(args) {
|
|
807
|
+
if (typeof args !== 'object' ||
|
|
808
|
+
args === null ||
|
|
809
|
+
!('taskIds' in args) ||
|
|
810
|
+
!Array.isArray(args.taskIds) ||
|
|
811
|
+
args.taskIds.length === 0 ||
|
|
812
|
+
!args.taskIds.every((id) => typeof id === 'string')) {
|
|
813
|
+
return false;
|
|
814
|
+
}
|
|
815
|
+
const { projectId, sectionId, parentId } = args;
|
|
816
|
+
const destinations = [projectId, sectionId, parentId];
|
|
817
|
+
const providedDestinations = destinations.filter(dest => dest !== undefined && dest !== null && String(dest).trim() !== '');
|
|
818
|
+
return providedDestinations.length === 1 &&
|
|
819
|
+
providedDestinations.every(dest => typeof dest === 'string');
|
|
820
|
+
}
|
|
647
821
|
function isCreateLabelArgs(args) {
|
|
648
822
|
return (typeof args === "object" &&
|
|
649
823
|
args !== null &&
|
|
@@ -666,6 +840,42 @@ function isUpdateLabelArgs(args) {
|
|
|
666
840
|
"labelId" in args &&
|
|
667
841
|
typeof args.labelId === "string");
|
|
668
842
|
}
|
|
843
|
+
function isCreateCommentArgs(args) {
|
|
844
|
+
if (typeof args !== "object" || args === null || !("content" in args) || typeof args.content !== "string") {
|
|
845
|
+
return false;
|
|
846
|
+
}
|
|
847
|
+
const { taskId, projectId } = args;
|
|
848
|
+
const targets = [taskId, projectId];
|
|
849
|
+
const providedTargets = targets.filter(target => target !== undefined && target !== null && String(target).trim() !== '');
|
|
850
|
+
// Exactly one target must be provided and be a non-empty string
|
|
851
|
+
return providedTargets.length === 1 &&
|
|
852
|
+
providedTargets.every(target => typeof target === 'string');
|
|
853
|
+
}
|
|
854
|
+
function isCommentIdArgs(args) {
|
|
855
|
+
return (typeof args === "object" &&
|
|
856
|
+
args !== null &&
|
|
857
|
+
"commentId" in args &&
|
|
858
|
+
typeof args.commentId === "string");
|
|
859
|
+
}
|
|
860
|
+
function isCommentsArgs(args) {
|
|
861
|
+
if (typeof args !== "object" || args === null) {
|
|
862
|
+
return false;
|
|
863
|
+
}
|
|
864
|
+
const { taskId, projectId } = args;
|
|
865
|
+
const targets = [taskId, projectId];
|
|
866
|
+
const providedTargets = targets.filter(target => target !== undefined && target !== null && String(target).trim() !== '');
|
|
867
|
+
// Exactly one target must be provided and be a non-empty string, or no targets (for all comments)
|
|
868
|
+
return providedTargets.length <= 1 &&
|
|
869
|
+
providedTargets.every(target => typeof target === 'string');
|
|
870
|
+
}
|
|
871
|
+
function isUpdateCommentArgs(args) {
|
|
872
|
+
return (typeof args === "object" &&
|
|
873
|
+
args !== null &&
|
|
874
|
+
"commentId" in args &&
|
|
875
|
+
"content" in args &&
|
|
876
|
+
typeof args.commentId === "string" &&
|
|
877
|
+
typeof args.content === "string");
|
|
878
|
+
}
|
|
669
879
|
// Tool handlers
|
|
670
880
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
671
881
|
tools: [
|
|
@@ -680,6 +890,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
680
890
|
REOPEN_TASK_TOOL,
|
|
681
891
|
SEARCH_TASKS_TOOL,
|
|
682
892
|
MOVE_TASK_TOOL,
|
|
893
|
+
BULK_MOVE_TASKS_TOOL,
|
|
683
894
|
// Project tools
|
|
684
895
|
GET_PROJECTS_TOOL,
|
|
685
896
|
GET_PROJECT_TOOL,
|
|
@@ -697,6 +908,12 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
697
908
|
GET_LABELS_TOOL,
|
|
698
909
|
UPDATE_LABEL_TOOL,
|
|
699
910
|
DELETE_LABEL_TOOL,
|
|
911
|
+
// Comment tools
|
|
912
|
+
CREATE_COMMENT_TOOL,
|
|
913
|
+
GET_COMMENT_TOOL,
|
|
914
|
+
GET_COMMENTS_TOOL,
|
|
915
|
+
UPDATE_COMMENT_TOOL,
|
|
916
|
+
DELETE_COMMENT_TOOL,
|
|
700
917
|
],
|
|
701
918
|
}));
|
|
702
919
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
@@ -1017,14 +1234,17 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1017
1234
|
const params = {};
|
|
1018
1235
|
if (args.projectId)
|
|
1019
1236
|
params.projectId = args.projectId;
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1237
|
+
if (args.cursor)
|
|
1238
|
+
params.cursor = args.cursor;
|
|
1239
|
+
if (args.limit)
|
|
1240
|
+
params.limit = args.limit;
|
|
1241
|
+
const sectionsResponse = await todoistClient.getSections(params);
|
|
1242
|
+
const sectionList = sectionsResponse.results?.map((section) => `- ${section.name} (ID: ${section.id}, Project ID: ${section.projectId})`).join('\n') || 'No sections found';
|
|
1243
|
+
const nextCursorMessage = sectionsResponse.nextCursor ? `\n\nNext cursor for more sections: ${sectionsResponse.nextCursor}` : '';
|
|
1024
1244
|
return {
|
|
1025
1245
|
content: [{
|
|
1026
1246
|
type: "text",
|
|
1027
|
-
text: `Sections:\n${sectionList
|
|
1247
|
+
text: `Sections:\n${sectionList}${nextCursorMessage}`
|
|
1028
1248
|
}],
|
|
1029
1249
|
isError: false,
|
|
1030
1250
|
};
|
|
@@ -1189,6 +1409,180 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1189
1409
|
return { content: [{ type: "text", text: `Error moving task: ${error.message}` }], isError: true };
|
|
1190
1410
|
}
|
|
1191
1411
|
}
|
|
1412
|
+
if (name === "todoist_bulk_move_tasks") {
|
|
1413
|
+
if (!isBulkMoveTasksArgs(args)) {
|
|
1414
|
+
return { content: [{ type: "text", text: "Invalid arguments for bulk_move_tasks. Provide a non-empty array of taskIds and exactly one of: projectId, sectionId, or parentId (must be a non-empty string)." }], isError: true };
|
|
1415
|
+
}
|
|
1416
|
+
try {
|
|
1417
|
+
const moveArgs = {};
|
|
1418
|
+
if (args.projectId)
|
|
1419
|
+
moveArgs.projectId = args.projectId;
|
|
1420
|
+
else if (args.sectionId)
|
|
1421
|
+
moveArgs.sectionId = args.sectionId;
|
|
1422
|
+
else if (args.parentId)
|
|
1423
|
+
moveArgs.parentId = args.parentId;
|
|
1424
|
+
console.error(`[DEBUG] todoist_bulk_move_tasks: Attempting to move ${args.taskIds.length} task(s) individually. Destination args: ${JSON.stringify(moveArgs)}`);
|
|
1425
|
+
const results = {
|
|
1426
|
+
succeeded: [],
|
|
1427
|
+
failed: [],
|
|
1428
|
+
};
|
|
1429
|
+
for (const taskId of args.taskIds) {
|
|
1430
|
+
try {
|
|
1431
|
+
console.error(`[DEBUG] Moving task ${taskId} to: ${JSON.stringify(moveArgs)}`);
|
|
1432
|
+
const individualMoveResult = await todoistClient.moveTasks([taskId], moveArgs);
|
|
1433
|
+
// Check if the API returned the task and if its properties reflect the move
|
|
1434
|
+
// For simplicity, we assume if no error is thrown, it was accepted by the API.
|
|
1435
|
+
// A more robust check would be to fetch the task again and verify its sectionId/projectId.
|
|
1436
|
+
if (individualMoveResult && individualMoveResult.length > 0 && individualMoveResult[0].id === taskId) {
|
|
1437
|
+
console.error(`[DEBUG] Task ${taskId} processed by API. Result: ${JSON.stringify(individualMoveResult[0])}`);
|
|
1438
|
+
// Further check if sectionId or projectId in individualMoveResult[0] matches moveArgs
|
|
1439
|
+
const movedTaskDetails = individualMoveResult[0];
|
|
1440
|
+
let successfulMove = false;
|
|
1441
|
+
if (moveArgs.sectionId && movedTaskDetails.sectionId === moveArgs.sectionId)
|
|
1442
|
+
successfulMove = true;
|
|
1443
|
+
else if (moveArgs.projectId && movedTaskDetails.projectId === moveArgs.projectId)
|
|
1444
|
+
successfulMove = true;
|
|
1445
|
+
else if (moveArgs.parentId && movedTaskDetails.parentId === moveArgs.parentId)
|
|
1446
|
+
successfulMove = true;
|
|
1447
|
+
// If the API doesn't reflect the change immediately in the returned object, we might still count it as succeeded based on no error.
|
|
1448
|
+
// For now, we count as success if API call didn't throw and returned our task.
|
|
1449
|
+
if (successfulMove) {
|
|
1450
|
+
results.succeeded.push(taskId);
|
|
1451
|
+
}
|
|
1452
|
+
else {
|
|
1453
|
+
// This case means API processed it but didn't reflect the change in the returned object, or it was already there.
|
|
1454
|
+
// Could be a race condition or API behavior. We'll count it as attempted but not fully confirmed by response.
|
|
1455
|
+
console.warn(`[DEBUG] Task ${taskId} processed, but move not immediately confirmed in API response object. Counting as succeeded based on no error.`);
|
|
1456
|
+
results.succeeded.push(taskId); // Tentatively count as success
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
else {
|
|
1460
|
+
// API call succeeded but didn't return our task, or returned empty array
|
|
1461
|
+
console.warn(`[DEBUG] Task ${taskId} move API call succeeded but task not found in response or empty response.`);
|
|
1462
|
+
results.succeeded.push(taskId); // Tentatively count as success if API didn't error
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
catch (taskError) {
|
|
1466
|
+
console.error(`[DEBUG] Failed to move task ${taskId}: ${taskError.message}`);
|
|
1467
|
+
results.failed.push({ id: taskId, error: taskError.message });
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
let summaryMessage = `Bulk move attempt complete for ${args.taskIds.length} task(s). `;
|
|
1471
|
+
summaryMessage += `Succeeded: ${results.succeeded.length}. `;
|
|
1472
|
+
if (results.succeeded.length > 0)
|
|
1473
|
+
summaryMessage += `Moved IDs: ${results.succeeded.join(", ")}. `;
|
|
1474
|
+
summaryMessage += `Failed: ${results.failed.length}.`;
|
|
1475
|
+
if (results.failed.length > 0) {
|
|
1476
|
+
summaryMessage += ` Failed IDs: ${results.failed.map(f => `${f.id} (${f.error})`).join("; ")}`;
|
|
1477
|
+
}
|
|
1478
|
+
return {
|
|
1479
|
+
content: [{ type: "text", text: summaryMessage }],
|
|
1480
|
+
isError: results.failed.length > 0 && results.succeeded.length === 0, // Overall error if all fails
|
|
1481
|
+
};
|
|
1482
|
+
}
|
|
1483
|
+
catch (error) {
|
|
1484
|
+
console.error(`[DEBUG] todoist_bulk_move_tasks: Outer error caught: ${error.message}`, error);
|
|
1485
|
+
return { content: [{ type: "text", text: `Error in bulk moving tasks: ${error.message}` }], isError: true };
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
// Comment operations
|
|
1489
|
+
if (name === "todoist_create_comment") {
|
|
1490
|
+
if (!isCreateCommentArgs(args)) {
|
|
1491
|
+
return { content: [{ type: "text", text: "Invalid arguments for create_comment" }], isError: true };
|
|
1492
|
+
}
|
|
1493
|
+
try {
|
|
1494
|
+
const commentData = { content: args.content };
|
|
1495
|
+
if (args.taskId)
|
|
1496
|
+
commentData.taskId = args.taskId;
|
|
1497
|
+
if (args.projectId)
|
|
1498
|
+
commentData.projectId = args.projectId;
|
|
1499
|
+
if (args.attachment)
|
|
1500
|
+
commentData.attachment = args.attachment;
|
|
1501
|
+
const comment = await todoistClient.addComment(commentData);
|
|
1502
|
+
return {
|
|
1503
|
+
content: [{ type: "text", text: `Comment created:\n${formatComment(comment)}` }],
|
|
1504
|
+
isError: false
|
|
1505
|
+
};
|
|
1506
|
+
}
|
|
1507
|
+
catch (error) {
|
|
1508
|
+
return { content: [{ type: "text", text: `Error creating comment: ${error.message}` }], isError: true };
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
if (name === "todoist_get_comment") {
|
|
1512
|
+
if (!isCommentIdArgs(args)) {
|
|
1513
|
+
return { content: [{ type: "text", text: "Invalid arguments for get_comment" }], isError: true };
|
|
1514
|
+
}
|
|
1515
|
+
try {
|
|
1516
|
+
const comment = await todoistClient.getComment(args.commentId);
|
|
1517
|
+
return {
|
|
1518
|
+
content: [{ type: "text", text: `Comment details:\n${formatComment(comment)}` }],
|
|
1519
|
+
isError: false
|
|
1520
|
+
};
|
|
1521
|
+
}
|
|
1522
|
+
catch (error) {
|
|
1523
|
+
return { content: [{ type: "text", text: `Error getting comment: ${error.message}` }], isError: true };
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
if (name === "todoist_get_comments") {
|
|
1527
|
+
if (!isCommentsArgs(args)) {
|
|
1528
|
+
return { content: [{ type: "text", text: "Invalid arguments for get_comments. Provide either taskId or projectId, not both." }], isError: true };
|
|
1529
|
+
}
|
|
1530
|
+
try {
|
|
1531
|
+
const params = {};
|
|
1532
|
+
if (args.taskId)
|
|
1533
|
+
params.taskId = args.taskId;
|
|
1534
|
+
if (args.projectId)
|
|
1535
|
+
params.projectId = args.projectId;
|
|
1536
|
+
if (args.cursor)
|
|
1537
|
+
params.cursor = args.cursor;
|
|
1538
|
+
if (args.limit)
|
|
1539
|
+
params.limit = args.limit;
|
|
1540
|
+
const commentsResponse = await todoistClient.getComments(params);
|
|
1541
|
+
const commentList = commentsResponse.results?.map(formatComment).join('\n\n') || 'No comments found';
|
|
1542
|
+
const nextCursor = commentsResponse.nextCursor ? `\n\nNext cursor for more comments: ${commentsResponse.nextCursor}` : '';
|
|
1543
|
+
return {
|
|
1544
|
+
content: [{
|
|
1545
|
+
type: "text",
|
|
1546
|
+
text: `Comments:\n${commentList}${nextCursor}`
|
|
1547
|
+
}],
|
|
1548
|
+
isError: false
|
|
1549
|
+
};
|
|
1550
|
+
}
|
|
1551
|
+
catch (error) {
|
|
1552
|
+
return { content: [{ type: "text", text: `Error getting comments: ${error.message}` }], isError: true };
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
if (name === "todoist_update_comment") {
|
|
1556
|
+
if (!isUpdateCommentArgs(args)) {
|
|
1557
|
+
return { content: [{ type: "text", text: "Invalid arguments for update_comment" }], isError: true };
|
|
1558
|
+
}
|
|
1559
|
+
try {
|
|
1560
|
+
const { commentId, ...updateArgs } = args;
|
|
1561
|
+
const updatedComment = await todoistClient.updateComment(commentId, updateArgs);
|
|
1562
|
+
return {
|
|
1563
|
+
content: [{ type: "text", text: `Comment updated:\n${formatComment(updatedComment)}` }],
|
|
1564
|
+
isError: false
|
|
1565
|
+
};
|
|
1566
|
+
}
|
|
1567
|
+
catch (error) {
|
|
1568
|
+
return { content: [{ type: "text", text: `Error updating comment: ${error.message}` }], isError: true };
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
if (name === "todoist_delete_comment") {
|
|
1572
|
+
if (!isCommentIdArgs(args)) {
|
|
1573
|
+
return { content: [{ type: "text", text: "Invalid arguments for delete_comment" }], isError: true };
|
|
1574
|
+
}
|
|
1575
|
+
try {
|
|
1576
|
+
await todoistClient.deleteComment(args.commentId);
|
|
1577
|
+
return {
|
|
1578
|
+
content: [{ type: "text", text: `Comment ${args.commentId} deleted.` }],
|
|
1579
|
+
isError: false
|
|
1580
|
+
};
|
|
1581
|
+
}
|
|
1582
|
+
catch (error) {
|
|
1583
|
+
return { content: [{ type: "text", text: `Error deleting comment: ${error.message}` }], isError: true };
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1192
1586
|
return {
|
|
1193
1587
|
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
1194
1588
|
isError: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kydycode/todoist-mcp-server-ext",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Extended MCP server for Todoist API integration with enhanced features and improved compatibility",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -46,4 +46,4 @@
|
|
|
46
46
|
"shx": "^0.3.4",
|
|
47
47
|
"typescript": "^5.7.2"
|
|
48
48
|
}
|
|
49
|
-
}
|
|
49
|
+
}
|