@kydycode/todoist-mcp-server-ext 0.1.0 → 0.2.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 +1 -5
- package/dist/index.js +318 -19
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
# Enhanced Todoist MCP Server Extended
|
|
2
|
-
[](https://smithery.ai/server/@kydycode/todoist-mcp-server-ext)
|
|
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
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.
|
|
7
7
|
|
|
8
|
-
<a href="https://glama.ai/mcp/servers/fhaif4fv1w">
|
|
9
|
-
<img width="380" height="200" src="https://glama.ai/mcp/servers/fhaif4fv1w/badge" alt="Todoist Server MCP server" />
|
|
10
|
-
</a>
|
|
11
|
-
|
|
12
8
|
## 🆕 Extended Version Features
|
|
13
9
|
|
|
14
10
|
### 🔧 **Technical Improvements**
|
package/dist/index.js
CHANGED
|
@@ -209,6 +209,104 @@ const REOPEN_TASK_TOOL = {
|
|
|
209
209
|
required: ["taskId"]
|
|
210
210
|
}
|
|
211
211
|
};
|
|
212
|
+
const MOVE_TASK_TOOL = {
|
|
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.",
|
|
215
|
+
inputSchema: {
|
|
216
|
+
type: "object",
|
|
217
|
+
properties: {
|
|
218
|
+
taskId: {
|
|
219
|
+
type: "string",
|
|
220
|
+
description: "The ID of the task to move."
|
|
221
|
+
},
|
|
222
|
+
projectId: {
|
|
223
|
+
type: "string",
|
|
224
|
+
description: "The ID of the destination project. (Optional, use only one of projectId, sectionId, parentId)"
|
|
225
|
+
},
|
|
226
|
+
sectionId: {
|
|
227
|
+
type: "string",
|
|
228
|
+
description: "The ID of the destination section. (Optional, use only one of projectId, sectionId, parentId)"
|
|
229
|
+
},
|
|
230
|
+
parentId: {
|
|
231
|
+
type: "string",
|
|
232
|
+
description: "The ID of the parent task to move this task under. (Optional, use only one of projectId, sectionId, parentId)"
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
required: ["taskId"]
|
|
236
|
+
// Note: Validation for providing exactly one of projectId, sectionId, or parentId
|
|
237
|
+
// is handled in the isMoveTaskArgs type guard and the tool handler.
|
|
238
|
+
// A more complex JSON schema with oneOf could enforce this, but client support varies.
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
// Label Management Tools
|
|
242
|
+
const CREATE_LABEL_TOOL = {
|
|
243
|
+
name: "todoist_create_label",
|
|
244
|
+
description: "Create a new label.",
|
|
245
|
+
inputSchema: {
|
|
246
|
+
type: "object",
|
|
247
|
+
properties: {
|
|
248
|
+
name: { type: "string", description: "The name of the label." },
|
|
249
|
+
color: { type: "string", description: "Label color name or code (e.g., 'berry_red', '#FF0000') (optional)." },
|
|
250
|
+
isFavorite: { type: "boolean", description: "Whether the label should be a favorite (optional)." },
|
|
251
|
+
order: { type: "number", description: "The order of the label in the list (optional)." }
|
|
252
|
+
},
|
|
253
|
+
required: ["name"]
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
const GET_LABEL_TOOL = {
|
|
257
|
+
name: "todoist_get_label",
|
|
258
|
+
description: "Get a specific label by its ID.",
|
|
259
|
+
inputSchema: {
|
|
260
|
+
type: "object",
|
|
261
|
+
properties: {
|
|
262
|
+
labelId: { type: "string", description: "The ID of the label to retrieve." }
|
|
263
|
+
},
|
|
264
|
+
required: ["labelId"]
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
const GET_LABELS_TOOL = {
|
|
268
|
+
name: "todoist_get_labels",
|
|
269
|
+
description: "Get all labels. Supports pagination.",
|
|
270
|
+
inputSchema: {
|
|
271
|
+
type: "object",
|
|
272
|
+
properties: {
|
|
273
|
+
cursor: {
|
|
274
|
+
type: "string",
|
|
275
|
+
description: "Pagination cursor for next page (optional)."
|
|
276
|
+
},
|
|
277
|
+
limit: {
|
|
278
|
+
type: "number",
|
|
279
|
+
description: "Maximum number of labels to return (default: 50) (optional)."
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
const UPDATE_LABEL_TOOL = {
|
|
285
|
+
name: "todoist_update_label",
|
|
286
|
+
description: "Update an existing label by its ID.",
|
|
287
|
+
inputSchema: {
|
|
288
|
+
type: "object",
|
|
289
|
+
properties: {
|
|
290
|
+
labelId: { type: "string", description: "The ID of the label to update." },
|
|
291
|
+
name: { type: "string", description: "New name for the label (optional)." },
|
|
292
|
+
color: { type: "string", description: "New color for the label (optional)." },
|
|
293
|
+
isFavorite: { type: "boolean", description: "New favorite status (optional)." },
|
|
294
|
+
order: { type: "number", description: "New order for the label (optional)." }
|
|
295
|
+
},
|
|
296
|
+
required: ["labelId"]
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
const DELETE_LABEL_TOOL = {
|
|
300
|
+
name: "todoist_delete_label",
|
|
301
|
+
description: "Delete a label by its ID.",
|
|
302
|
+
inputSchema: {
|
|
303
|
+
type: "object",
|
|
304
|
+
properties: {
|
|
305
|
+
labelId: { type: "string", description: "The ID of the label to delete." }
|
|
306
|
+
},
|
|
307
|
+
required: ["labelId"]
|
|
308
|
+
}
|
|
309
|
+
};
|
|
212
310
|
// Project Management Tools
|
|
213
311
|
const GET_PROJECTS_TOOL = {
|
|
214
312
|
name: "todoist_get_projects",
|
|
@@ -421,11 +519,38 @@ const server = new Server({
|
|
|
421
519
|
});
|
|
422
520
|
// Helper function to format task output
|
|
423
521
|
function formatTask(task) {
|
|
424
|
-
|
|
522
|
+
let taskDetails = `- ID: ${task.id}\n Content: ${task.content}`;
|
|
523
|
+
if (task.description)
|
|
524
|
+
taskDetails += `\n Description: ${task.description}`;
|
|
525
|
+
if (task.due)
|
|
526
|
+
taskDetails += `\n Due: ${task.due.string}`;
|
|
527
|
+
if (task.priority && task.priority > 1)
|
|
528
|
+
taskDetails += `\n Priority: ${task.priority}`;
|
|
529
|
+
if (task.labels && task.labels.length > 0)
|
|
530
|
+
taskDetails += `\n Labels: ${task.labels.join(', ')}`;
|
|
531
|
+
if (task.projectId)
|
|
532
|
+
taskDetails += `\n Project ID: ${task.projectId}`;
|
|
533
|
+
if (task.sectionId)
|
|
534
|
+
taskDetails += `\n Section ID: ${task.sectionId}`;
|
|
535
|
+
if (task.parentId)
|
|
536
|
+
taskDetails += `\n Parent ID: ${task.parentId}`;
|
|
537
|
+
if (task.url)
|
|
538
|
+
taskDetails += `\n URL: ${task.url}`;
|
|
539
|
+
if (task.commentCount > 0)
|
|
540
|
+
taskDetails += `\n Comments: ${task.commentCount}`;
|
|
541
|
+
if (task.createdAt)
|
|
542
|
+
taskDetails += `\n Created At: ${task.createdAt}`;
|
|
543
|
+
if (task.creatorId)
|
|
544
|
+
taskDetails += `\n Creator ID: ${task.creatorId}`;
|
|
545
|
+
return taskDetails;
|
|
425
546
|
}
|
|
426
547
|
// Helper function to format project output
|
|
427
548
|
function formatProject(project) {
|
|
428
|
-
return `- ${project.name}${project.color ? `\n Color: ${project.color}` : ''}${project.isFavorite ? `\n Favorite: Yes` : ''}${project.viewStyle ? `\n View: ${project.viewStyle}` : ''}${project.parentId ? `\n Parent: ${project.parentId}` : ''}`;
|
|
549
|
+
return `- ${project.name}${project.color ? `\n Color: ${project.color}` : ''}${project.isFavorite ? `\n Favorite: Yes` : ''}${project.viewStyle ? `\n View: ${project.viewStyle}` : ''}${project.parentId ? `\n Parent: ${project.parentId}` : ''}${project.id ? ` (ID: ${project.id})` : ''}`;
|
|
550
|
+
}
|
|
551
|
+
// Helper function to format label output
|
|
552
|
+
function formatLabel(label) {
|
|
553
|
+
return `- ${label.name} (ID: ${label.id})${label.color ? `\n Color: ${label.color}` : ''}${label.isFavorite ? `\n Favorite: Yes` : ''}${label.order ? `\n Order: ${label.order}` : ''}`;
|
|
429
554
|
}
|
|
430
555
|
// Type guards for arguments
|
|
431
556
|
function isCreateTaskArgs(args) {
|
|
@@ -456,6 +581,7 @@ function isUpdateTaskArgs(args) {
|
|
|
456
581
|
typeof args.taskId === "string");
|
|
457
582
|
}
|
|
458
583
|
function isProjectArgs(args) {
|
|
584
|
+
// Allows empty object or object with optional cursor/limit
|
|
459
585
|
return typeof args === "object" && args !== null;
|
|
460
586
|
}
|
|
461
587
|
function isProjectIdArgs(args) {
|
|
@@ -507,6 +633,39 @@ function isSearchTasksArgs(args) {
|
|
|
507
633
|
"query" in args &&
|
|
508
634
|
typeof args.query === "string");
|
|
509
635
|
}
|
|
636
|
+
function isMoveTaskArgs(args) {
|
|
637
|
+
if (typeof args !== 'object' || args === null || !('taskId' in args) || typeof args.taskId !== 'string') {
|
|
638
|
+
return false;
|
|
639
|
+
}
|
|
640
|
+
const { projectId, sectionId, parentId } = args;
|
|
641
|
+
const destinations = [projectId, sectionId, parentId];
|
|
642
|
+
const providedDestinations = destinations.filter(dest => dest !== undefined && dest !== null && String(dest).trim() !== '');
|
|
643
|
+
// Exactly one destination must be provided and be a non-empty string
|
|
644
|
+
return providedDestinations.length === 1 &&
|
|
645
|
+
providedDestinations.every(dest => typeof dest === 'string');
|
|
646
|
+
}
|
|
647
|
+
function isCreateLabelArgs(args) {
|
|
648
|
+
return (typeof args === "object" &&
|
|
649
|
+
args !== null &&
|
|
650
|
+
"name" in args &&
|
|
651
|
+
typeof args.name === "string");
|
|
652
|
+
}
|
|
653
|
+
function isLabelIdArgs(args) {
|
|
654
|
+
return (typeof args === "object" &&
|
|
655
|
+
args !== null &&
|
|
656
|
+
"labelId" in args &&
|
|
657
|
+
typeof args.labelId === "string");
|
|
658
|
+
}
|
|
659
|
+
// Type guard for get_labels which takes no arguments in this SDK version
|
|
660
|
+
function isGetLabelsArgs(args) {
|
|
661
|
+
return typeof args === "object" && args !== null;
|
|
662
|
+
}
|
|
663
|
+
function isUpdateLabelArgs(args) {
|
|
664
|
+
return (typeof args === "object" &&
|
|
665
|
+
args !== null &&
|
|
666
|
+
"labelId" in args &&
|
|
667
|
+
typeof args.labelId === "string");
|
|
668
|
+
}
|
|
510
669
|
// Tool handlers
|
|
511
670
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
512
671
|
tools: [
|
|
@@ -520,6 +679,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
520
679
|
COMPLETE_TASK_TOOL,
|
|
521
680
|
REOPEN_TASK_TOOL,
|
|
522
681
|
SEARCH_TASKS_TOOL,
|
|
682
|
+
MOVE_TASK_TOOL,
|
|
523
683
|
// Project tools
|
|
524
684
|
GET_PROJECTS_TOOL,
|
|
525
685
|
GET_PROJECT_TOOL,
|
|
@@ -531,6 +691,12 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
531
691
|
CREATE_SECTION_TOOL,
|
|
532
692
|
UPDATE_SECTION_TOOL,
|
|
533
693
|
DELETE_SECTION_TOOL,
|
|
694
|
+
// Label tools
|
|
695
|
+
CREATE_LABEL_TOOL,
|
|
696
|
+
GET_LABEL_TOOL,
|
|
697
|
+
GET_LABELS_TOOL,
|
|
698
|
+
UPDATE_LABEL_TOOL,
|
|
699
|
+
DELETE_LABEL_TOOL,
|
|
534
700
|
],
|
|
535
701
|
}));
|
|
536
702
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
@@ -707,26 +873,47 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
707
873
|
if (!isSearchTasksArgs(args)) {
|
|
708
874
|
throw new Error("Invalid arguments for todoist_search_tasks");
|
|
709
875
|
}
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
const
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
876
|
+
// Prepare arguments for getTasksByFilter
|
|
877
|
+
// Prepend "search: " to the query for more robust keyword searching with Todoist API
|
|
878
|
+
const searchQuery = args.query.startsWith("search:") ? args.query : `search: ${args.query}`;
|
|
879
|
+
const filterArgs = { query: searchQuery };
|
|
880
|
+
if (args.limit)
|
|
881
|
+
filterArgs.limit = args.limit;
|
|
882
|
+
// Note: args.projectId is not directly used by getTasksByFilter unless incorporated into the query string.
|
|
883
|
+
// For example: `search: ${args.query} & #ProjectName` or `search: ${args.query} & ##ProjectID`
|
|
884
|
+
const tasksResponse = await todoistClient.getTasksByFilter(filterArgs);
|
|
885
|
+
const matchingTasksData = tasksResponse.results || [];
|
|
886
|
+
if (matchingTasksData.length === 0) {
|
|
717
887
|
return {
|
|
718
888
|
content: [{
|
|
719
889
|
type: "text",
|
|
720
|
-
text: `No tasks found matching "${args.query}"`
|
|
890
|
+
text: `No tasks found matching the filter query "${args.query}"`
|
|
721
891
|
}],
|
|
722
892
|
isError: false,
|
|
723
893
|
};
|
|
724
894
|
}
|
|
725
|
-
|
|
895
|
+
// Asynchronously format tasks and fetch project names if necessary
|
|
896
|
+
const formattedTaskList = await Promise.all(matchingTasksData.map(async (task) => {
|
|
897
|
+
let taskDisplay = formatTask(task); // formatTask now includes Project ID
|
|
898
|
+
if (task.projectId) {
|
|
899
|
+
try {
|
|
900
|
+
const project = await todoistClient.getProject(task.projectId);
|
|
901
|
+
taskDisplay += `\n Project Name: ${project.name}`;
|
|
902
|
+
}
|
|
903
|
+
catch (projectError) {
|
|
904
|
+
// Silently ignore project fetch errors for search, or log them
|
|
905
|
+
// taskDisplay += `\n Project Name: (Error fetching project details)`;
|
|
906
|
+
console.error(`Error fetching project ${task.projectId} for search result: ${projectError.message}`);
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
return taskDisplay;
|
|
910
|
+
}));
|
|
911
|
+
const taskListString = formattedTaskList.join('\n\n');
|
|
912
|
+
const nextCursorMessage = tasksResponse.nextCursor ? `\n\nNext cursor for more results: ${tasksResponse.nextCursor}` : '';
|
|
726
913
|
return {
|
|
727
914
|
content: [{
|
|
728
915
|
type: "text",
|
|
729
|
-
text: `Found ${
|
|
916
|
+
text: `Found ${matchingTasksData.length} task(s) matching "${args.query}":\n\n${taskListString}${nextCursorMessage}`
|
|
730
917
|
}],
|
|
731
918
|
isError: false,
|
|
732
919
|
};
|
|
@@ -741,16 +928,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
741
928
|
params.cursor = args.cursor;
|
|
742
929
|
if (args.limit)
|
|
743
930
|
params.limit = args.limit;
|
|
744
|
-
|
|
745
|
-
const
|
|
746
|
-
|
|
747
|
-
const projectList = Array.isArray(projects)
|
|
748
|
-
? projects.map(formatProject).join('\n\n')
|
|
749
|
-
: 'No projects found';
|
|
931
|
+
const projectsResponse = await todoistClient.getProjects(params);
|
|
932
|
+
const projectList = projectsResponse.results?.map(formatProject).join('\n\n') || 'No projects found';
|
|
933
|
+
const nextCursor = projectsResponse.nextCursor ? `\n\nNext cursor: ${projectsResponse.nextCursor}` : '';
|
|
750
934
|
return {
|
|
751
935
|
content: [{
|
|
752
936
|
type: "text",
|
|
753
|
-
text: `Projects:\n${projectList}`
|
|
937
|
+
text: `Projects:\n${projectList}${nextCursor}`
|
|
754
938
|
}],
|
|
755
939
|
isError: false,
|
|
756
940
|
};
|
|
@@ -890,6 +1074,121 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
890
1074
|
isError: false,
|
|
891
1075
|
};
|
|
892
1076
|
}
|
|
1077
|
+
// Label operations
|
|
1078
|
+
if (name === "todoist_create_label") {
|
|
1079
|
+
if (!isCreateLabelArgs(args)) {
|
|
1080
|
+
return { content: [{ type: "text", text: "Invalid arguments for create_label" }], isError: true };
|
|
1081
|
+
}
|
|
1082
|
+
try {
|
|
1083
|
+
const label = await todoistClient.addLabel(args);
|
|
1084
|
+
return {
|
|
1085
|
+
content: [{ type: "text", text: `Label created:\n${formatLabel(label)}` }],
|
|
1086
|
+
isError: false
|
|
1087
|
+
};
|
|
1088
|
+
}
|
|
1089
|
+
catch (error) {
|
|
1090
|
+
return { content: [{ type: "text", text: `Error creating label: ${error.message}` }], isError: true };
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
if (name === "todoist_get_label") {
|
|
1094
|
+
if (!isLabelIdArgs(args)) {
|
|
1095
|
+
return { content: [{ type: "text", text: "Invalid arguments for get_label" }], isError: true };
|
|
1096
|
+
}
|
|
1097
|
+
try {
|
|
1098
|
+
const label = await todoistClient.getLabel(args.labelId);
|
|
1099
|
+
return {
|
|
1100
|
+
content: [{ type: "text", text: `Label details:\n${formatLabel(label)}` }],
|
|
1101
|
+
isError: false
|
|
1102
|
+
};
|
|
1103
|
+
}
|
|
1104
|
+
catch (error) {
|
|
1105
|
+
return { content: [{ type: "text", text: `Error getting label: ${error.message}` }], isError: true };
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
if (name === "todoist_get_labels") {
|
|
1109
|
+
if (!isGetLabelsArgs(args)) {
|
|
1110
|
+
return { content: [{ type: "text", text: "Invalid arguments for get_labels. This tool takes an optional cursor and limit." }], isError: true };
|
|
1111
|
+
}
|
|
1112
|
+
try {
|
|
1113
|
+
const params = {};
|
|
1114
|
+
if (args.cursor)
|
|
1115
|
+
params.cursor = args.cursor;
|
|
1116
|
+
if (args.limit)
|
|
1117
|
+
params.limit = args.limit;
|
|
1118
|
+
const labelsResponse = await todoistClient.getLabels(params);
|
|
1119
|
+
const labelList = labelsResponse.results?.map(formatLabel).join('\n\n') || 'No labels found';
|
|
1120
|
+
const nextCursor = labelsResponse.nextCursor ? `\n\nNext cursor for more labels: ${labelsResponse.nextCursor}` : '';
|
|
1121
|
+
return {
|
|
1122
|
+
content: [{
|
|
1123
|
+
type: "text",
|
|
1124
|
+
text: `Labels:\n${labelList}${nextCursor}`
|
|
1125
|
+
}],
|
|
1126
|
+
isError: false
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1129
|
+
catch (error) {
|
|
1130
|
+
return { content: [{ type: "text", text: `Error getting labels: ${error.message}` }], isError: true };
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
if (name === "todoist_update_label") {
|
|
1134
|
+
if (!isUpdateLabelArgs(args)) {
|
|
1135
|
+
return { content: [{ type: "text", text: "Invalid arguments for update_label" }], isError: true };
|
|
1136
|
+
}
|
|
1137
|
+
try {
|
|
1138
|
+
const { labelId, ...updateArgs } = args;
|
|
1139
|
+
const updatedLabel = await todoistClient.updateLabel(labelId, updateArgs);
|
|
1140
|
+
return {
|
|
1141
|
+
content: [{ type: "text", text: `Label updated:\n${formatLabel(updatedLabel)}` }],
|
|
1142
|
+
isError: false
|
|
1143
|
+
};
|
|
1144
|
+
}
|
|
1145
|
+
catch (error) {
|
|
1146
|
+
return { content: [{ type: "text", text: `Error updating label: ${error.message}` }], isError: true };
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
if (name === "todoist_delete_label") {
|
|
1150
|
+
if (!isLabelIdArgs(args)) {
|
|
1151
|
+
return { content: [{ type: "text", text: "Invalid arguments for delete_label" }], isError: true };
|
|
1152
|
+
}
|
|
1153
|
+
try {
|
|
1154
|
+
await todoistClient.deleteLabel(args.labelId);
|
|
1155
|
+
return {
|
|
1156
|
+
content: [{ type: "text", text: `Label ${args.labelId} deleted.` }],
|
|
1157
|
+
isError: false
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
catch (error) {
|
|
1161
|
+
return { content: [{ type: "text", text: `Error deleting label: ${error.message}` }], isError: true };
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
// Move task operations
|
|
1165
|
+
if (name === "todoist_move_task") {
|
|
1166
|
+
if (!isMoveTaskArgs(args)) {
|
|
1167
|
+
return { content: [{ type: "text", text: "Invalid arguments for move_task. Provide taskId and exactly one of: projectId, sectionId, or parentId (must be a non-empty string)." }], isError: true };
|
|
1168
|
+
}
|
|
1169
|
+
try {
|
|
1170
|
+
const moveArgs = {};
|
|
1171
|
+
if (args.projectId)
|
|
1172
|
+
moveArgs.projectId = args.projectId;
|
|
1173
|
+
else if (args.sectionId)
|
|
1174
|
+
moveArgs.sectionId = args.sectionId;
|
|
1175
|
+
else if (args.parentId)
|
|
1176
|
+
moveArgs.parentId = args.parentId;
|
|
1177
|
+
// Use moveTasks from SDK v4+
|
|
1178
|
+
await todoistClient.moveTasks([args.taskId], moveArgs); // Cast to any for MoveTaskArgs as it expects RequireExactlyOne
|
|
1179
|
+
const movedTask = await todoistClient.getTask(args.taskId);
|
|
1180
|
+
return {
|
|
1181
|
+
content: [{
|
|
1182
|
+
type: "text",
|
|
1183
|
+
text: `Task ${args.taskId} moved successfully.\nNew details:\n${formatTask(movedTask)}`
|
|
1184
|
+
}],
|
|
1185
|
+
isError: false
|
|
1186
|
+
};
|
|
1187
|
+
}
|
|
1188
|
+
catch (error) {
|
|
1189
|
+
return { content: [{ type: "text", text: `Error moving task: ${error.message}` }], isError: true };
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
893
1192
|
return {
|
|
894
1193
|
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
895
1194
|
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.2.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",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
},
|
|
38
38
|
"homepage": "https://github.com/kydycode/todoist-mcp-server-ext#readme",
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@doist/todoist-api-typescript": "^
|
|
40
|
+
"@doist/todoist-api-typescript": "^4.0.4",
|
|
41
41
|
"@modelcontextprotocol/sdk": "0.5.0",
|
|
42
42
|
"zod": "^3.22.0"
|
|
43
43
|
},
|