@taazkareem/clickup-mcp-server 0.4.70 → 0.4.72
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 +35 -18
- package/build/config.js +7 -4
- package/build/index.js +1 -1
- package/build/mcp-tools.js +64 -0
- package/build/server.js +4 -4
- package/build/server.log +34 -211
- package/build/services/clickup/bulk.js +132 -101
- package/build/services/clickup/task.js +40 -239
- package/build/tools/bulk-tasks.js +36 -0
- package/build/tools/task.js +616 -539
- package/build/tools/utils.js +8 -147
- package/build/utils/concurrency-utils.js +245 -0
- package/build/utils/date-utils.js +152 -0
- package/build/utils/params-utils.js +39 -0
- package/build/utils/resolver-utils.js +66 -0
- package/build/utils/sponsor-utils.js +49 -0
- package/package.json +1 -1
package/build/tools/task.js
CHANGED
|
@@ -6,10 +6,12 @@
|
|
|
6
6
|
* retrieving task details.
|
|
7
7
|
*/
|
|
8
8
|
import { clickUpServices } from '../services/shared.js';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { parseDueDate, formatDueDate, enhanceResponseWithSponsor, resolveListId, resolveTaskId } from './utils.js';
|
|
10
|
+
import { BulkService } from '../services/clickup/bulk.js';
|
|
11
11
|
// Use shared services instance
|
|
12
12
|
const { task: taskService, workspace: workspaceService } = clickUpServices;
|
|
13
|
+
// Create a bulk service instance that uses the task service
|
|
14
|
+
const bulkService = new BulkService(taskService);
|
|
13
15
|
/**
|
|
14
16
|
* Tool definition for creating a single task
|
|
15
17
|
*/
|
|
@@ -49,7 +51,7 @@ export const createTaskTool = {
|
|
|
49
51
|
},
|
|
50
52
|
dueDate: {
|
|
51
53
|
type: "string",
|
|
52
|
-
description: "Due date of the task
|
|
54
|
+
description: "Due date of the task. Supports both Unix timestamps (in milliseconds) and natural language expressions like '1 hour from now', 'tomorrow', 'next week', or '3 days from now'."
|
|
53
55
|
}
|
|
54
56
|
},
|
|
55
57
|
required: ["name"]
|
|
@@ -146,7 +148,7 @@ export const updateTaskTool = {
|
|
|
146
148
|
},
|
|
147
149
|
dueDate: {
|
|
148
150
|
type: "string",
|
|
149
|
-
description: "New due date
|
|
151
|
+
description: "New due date. Supports both Unix timestamps (in milliseconds) and natural language expressions like '1 hour from now', 'tomorrow', 'next week', or '3 days from now'."
|
|
150
152
|
}
|
|
151
153
|
},
|
|
152
154
|
required: []
|
|
@@ -588,218 +590,6 @@ export const deleteTaskTool = {
|
|
|
588
590
|
}
|
|
589
591
|
}
|
|
590
592
|
};
|
|
591
|
-
/**
|
|
592
|
-
* Tool definition for deleting multiple tasks
|
|
593
|
-
*/
|
|
594
|
-
export const deleteBulkTasksTool = {
|
|
595
|
-
name: "delete_bulk_tasks",
|
|
596
|
-
description: "\u26a0\ufe0f PERMANENTLY DELETE multiple tasks. This action cannot be undone. For each task, you MUST provide either:\n1. taskId alone (preferred and safest)\n2. taskName + listName (use with caution)",
|
|
597
|
-
inputSchema: {
|
|
598
|
-
type: "object",
|
|
599
|
-
properties: {
|
|
600
|
-
tasks: {
|
|
601
|
-
type: "array",
|
|
602
|
-
description: "Array of tasks to delete",
|
|
603
|
-
items: {
|
|
604
|
-
type: "object",
|
|
605
|
-
properties: {
|
|
606
|
-
taskId: {
|
|
607
|
-
type: "string",
|
|
608
|
-
description: "Task ID (preferred). Use instead of taskName if available."
|
|
609
|
-
},
|
|
610
|
-
taskName: {
|
|
611
|
-
type: "string",
|
|
612
|
-
description: "Task name. Requires listName when used."
|
|
613
|
-
},
|
|
614
|
-
listName: {
|
|
615
|
-
type: "string",
|
|
616
|
-
description: "REQUIRED with taskName: List containing the task."
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
},
|
|
622
|
-
required: ["tasks"]
|
|
623
|
-
}
|
|
624
|
-
};
|
|
625
|
-
/**
|
|
626
|
-
* Tool definition for creating multiple tasks at once
|
|
627
|
-
*/
|
|
628
|
-
export const createBulkTasksTool = {
|
|
629
|
-
name: "create_bulk_tasks",
|
|
630
|
-
description: "Create multiple tasks in a list efficiently. You MUST provide:\n1. An array of tasks with required properties\n2. Either listId or listName to specify the target list\n\nOptional: Configure batch size and concurrency for performance.",
|
|
631
|
-
inputSchema: {
|
|
632
|
-
type: "object",
|
|
633
|
-
properties: {
|
|
634
|
-
listId: {
|
|
635
|
-
type: "string",
|
|
636
|
-
description: "ID of list for new tasks (preferred). Use this instead of listName if you have it."
|
|
637
|
-
},
|
|
638
|
-
listName: {
|
|
639
|
-
type: "string",
|
|
640
|
-
description: "Name of list for new tasks. Only use if you don't have listId."
|
|
641
|
-
},
|
|
642
|
-
tasks: {
|
|
643
|
-
type: "array",
|
|
644
|
-
description: "Array of tasks to create. Each task must have at least a name.",
|
|
645
|
-
items: {
|
|
646
|
-
type: "object",
|
|
647
|
-
properties: {
|
|
648
|
-
name: {
|
|
649
|
-
type: "string",
|
|
650
|
-
description: "Task name with emoji prefix"
|
|
651
|
-
},
|
|
652
|
-
description: {
|
|
653
|
-
type: "string",
|
|
654
|
-
description: "Plain text description"
|
|
655
|
-
},
|
|
656
|
-
markdown_description: {
|
|
657
|
-
type: "string",
|
|
658
|
-
description: "Markdown description (overrides plain text)"
|
|
659
|
-
},
|
|
660
|
-
status: {
|
|
661
|
-
type: "string",
|
|
662
|
-
description: "Task status (uses list default if omitted)"
|
|
663
|
-
},
|
|
664
|
-
priority: {
|
|
665
|
-
type: "number",
|
|
666
|
-
description: "Priority 1-4 (1=urgent, 4=low)"
|
|
667
|
-
},
|
|
668
|
-
dueDate: {
|
|
669
|
-
type: "string",
|
|
670
|
-
description: "Due date (Unix timestamp ms)"
|
|
671
|
-
}
|
|
672
|
-
},
|
|
673
|
-
required: ["name"]
|
|
674
|
-
}
|
|
675
|
-
},
|
|
676
|
-
options: {
|
|
677
|
-
type: "object",
|
|
678
|
-
description: "Optional processing settings",
|
|
679
|
-
properties: {
|
|
680
|
-
batchSize: {
|
|
681
|
-
type: "number",
|
|
682
|
-
description: "Tasks per batch (default: 10)"
|
|
683
|
-
},
|
|
684
|
-
concurrency: {
|
|
685
|
-
type: "number",
|
|
686
|
-
description: "Parallel operations (default: 1)"
|
|
687
|
-
},
|
|
688
|
-
continueOnError: {
|
|
689
|
-
type: "boolean",
|
|
690
|
-
description: "Continue if some tasks fail"
|
|
691
|
-
},
|
|
692
|
-
retryCount: {
|
|
693
|
-
type: "number",
|
|
694
|
-
description: "Retry attempts for failures"
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
},
|
|
699
|
-
required: ["tasks"]
|
|
700
|
-
}
|
|
701
|
-
};
|
|
702
|
-
/**
|
|
703
|
-
* Tool definition for updating multiple tasks
|
|
704
|
-
*/
|
|
705
|
-
export const updateBulkTasksTool = {
|
|
706
|
-
name: "update_bulk_tasks",
|
|
707
|
-
description: "Update multiple tasks efficiently. For each task, you MUST provide either:\n1. taskId alone (preferred)\n2. taskName + listName\n\nOnly specified fields will be updated for each task.",
|
|
708
|
-
inputSchema: {
|
|
709
|
-
type: "object",
|
|
710
|
-
properties: {
|
|
711
|
-
tasks: {
|
|
712
|
-
type: "array",
|
|
713
|
-
description: "Array of tasks to update",
|
|
714
|
-
items: {
|
|
715
|
-
type: "object",
|
|
716
|
-
properties: {
|
|
717
|
-
taskId: {
|
|
718
|
-
type: "string",
|
|
719
|
-
description: "Task ID (preferred). Use instead of taskName if available."
|
|
720
|
-
},
|
|
721
|
-
taskName: {
|
|
722
|
-
type: "string",
|
|
723
|
-
description: "Task name. Requires listName when used."
|
|
724
|
-
},
|
|
725
|
-
listName: {
|
|
726
|
-
type: "string",
|
|
727
|
-
description: "REQUIRED with taskName: List containing the task."
|
|
728
|
-
},
|
|
729
|
-
name: {
|
|
730
|
-
type: "string",
|
|
731
|
-
description: "New name with emoji prefix"
|
|
732
|
-
},
|
|
733
|
-
description: {
|
|
734
|
-
type: "string",
|
|
735
|
-
description: "New plain text description"
|
|
736
|
-
},
|
|
737
|
-
markdown_description: {
|
|
738
|
-
type: "string",
|
|
739
|
-
description: "New markdown description"
|
|
740
|
-
},
|
|
741
|
-
status: {
|
|
742
|
-
type: "string",
|
|
743
|
-
description: "New status"
|
|
744
|
-
},
|
|
745
|
-
priority: {
|
|
746
|
-
type: ["number", "null"],
|
|
747
|
-
enum: [1, 2, 3, 4, null],
|
|
748
|
-
description: "New priority (1-4 or null)"
|
|
749
|
-
},
|
|
750
|
-
dueDate: {
|
|
751
|
-
type: "string",
|
|
752
|
-
description: "New due date (Unix timestamp in milliseconds)"
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
},
|
|
758
|
-
required: ["tasks"]
|
|
759
|
-
}
|
|
760
|
-
};
|
|
761
|
-
/**
|
|
762
|
-
* Tool definition for moving multiple tasks
|
|
763
|
-
*/
|
|
764
|
-
export const moveBulkTasksTool = {
|
|
765
|
-
name: "move_bulk_tasks",
|
|
766
|
-
description: "Move multiple tasks to a different list efficiently. For each task, you MUST provide either:\n1. taskId alone (preferred)\n2. taskName + listName\n\nWARNING: Task statuses may reset if target list has different status options.",
|
|
767
|
-
inputSchema: {
|
|
768
|
-
type: "object",
|
|
769
|
-
properties: {
|
|
770
|
-
tasks: {
|
|
771
|
-
type: "array",
|
|
772
|
-
description: "Array of tasks to move",
|
|
773
|
-
items: {
|
|
774
|
-
type: "object",
|
|
775
|
-
properties: {
|
|
776
|
-
taskId: {
|
|
777
|
-
type: "string",
|
|
778
|
-
description: "Task ID (preferred). Use instead of taskName if available."
|
|
779
|
-
},
|
|
780
|
-
taskName: {
|
|
781
|
-
type: "string",
|
|
782
|
-
description: "Task name. Requires listName when used."
|
|
783
|
-
},
|
|
784
|
-
listName: {
|
|
785
|
-
type: "string",
|
|
786
|
-
description: "REQUIRED with taskName: List containing the task."
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
},
|
|
791
|
-
targetListId: {
|
|
792
|
-
type: "string",
|
|
793
|
-
description: "ID of destination list (preferred). Use instead of targetListName if available."
|
|
794
|
-
},
|
|
795
|
-
targetListName: {
|
|
796
|
-
type: "string",
|
|
797
|
-
description: "Name of destination list. Only use if you don't have targetListId."
|
|
798
|
-
}
|
|
799
|
-
},
|
|
800
|
-
required: ["tasks"]
|
|
801
|
-
}
|
|
802
|
-
};
|
|
803
593
|
/**
|
|
804
594
|
* Tool definition for getting task comments
|
|
805
595
|
*/
|
|
@@ -907,78 +697,19 @@ export const getTaskCommentsTool = {
|
|
|
907
697
|
};
|
|
908
698
|
}
|
|
909
699
|
};
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
if (!
|
|
915
|
-
throw new Error("
|
|
916
|
-
}
|
|
917
|
-
const results = {
|
|
918
|
-
total: tasks.length,
|
|
919
|
-
successful: 0,
|
|
920
|
-
failed: 0,
|
|
921
|
-
failures: []
|
|
922
|
-
};
|
|
923
|
-
for (const task of tasks) {
|
|
924
|
-
try {
|
|
925
|
-
let taskId = task.taskId;
|
|
926
|
-
if (!taskId && task.taskName) {
|
|
927
|
-
if (!task.listName) {
|
|
928
|
-
throw new Error(`List name is required when using task name for task "${task.taskName}"`);
|
|
929
|
-
}
|
|
930
|
-
const listInfo = await findListIDByName(workspaceService, task.listName);
|
|
931
|
-
if (!listInfo) {
|
|
932
|
-
throw new Error(`List "${task.listName}" not found`);
|
|
933
|
-
}
|
|
934
|
-
const taskList = await taskService.getTasks(listInfo.id);
|
|
935
|
-
const foundTask = taskList.find(t => t.name.toLowerCase() === task.taskName.toLowerCase());
|
|
936
|
-
if (!foundTask) {
|
|
937
|
-
throw new Error(`Task "${task.taskName}" not found in list "${task.listName}"`);
|
|
938
|
-
}
|
|
939
|
-
taskId = foundTask.id;
|
|
940
|
-
}
|
|
941
|
-
if (!taskId) {
|
|
942
|
-
throw new Error("Either taskId or taskName must be provided");
|
|
943
|
-
}
|
|
944
|
-
await taskService.updateTask(taskId, {
|
|
945
|
-
name: task.name,
|
|
946
|
-
description: task.description,
|
|
947
|
-
markdown_description: task.markdown_description,
|
|
948
|
-
status: task.status,
|
|
949
|
-
priority: task.priority,
|
|
950
|
-
due_date: task.dueDate ? parseDueDate(task.dueDate) : undefined
|
|
951
|
-
});
|
|
952
|
-
results.successful++;
|
|
953
|
-
}
|
|
954
|
-
catch (error) {
|
|
955
|
-
results.failed++;
|
|
956
|
-
results.failures.push({
|
|
957
|
-
task: task.taskId || task.taskName,
|
|
958
|
-
error: error.message
|
|
959
|
-
});
|
|
960
|
-
}
|
|
961
|
-
}
|
|
962
|
-
return {
|
|
963
|
-
content: [{
|
|
964
|
-
type: "text",
|
|
965
|
-
text: JSON.stringify(results, null, 2)
|
|
966
|
-
}]
|
|
967
|
-
};
|
|
968
|
-
}
|
|
969
|
-
/**
|
|
970
|
-
* Handler for bulk task creation
|
|
971
|
-
*/
|
|
972
|
-
export async function handleCreateBulkTasks(parameters) {
|
|
973
|
-
// Validate required parameters
|
|
974
|
-
const { tasks, listId, listName } = parameters;
|
|
975
|
-
if (!tasks || !Array.isArray(tasks) || tasks.length === 0) {
|
|
976
|
-
throw new Error('You must provide a non-empty array of tasks to create');
|
|
700
|
+
// Export handlers to be used by the server
|
|
701
|
+
export async function handleCreateTask(parameters) {
|
|
702
|
+
const { name, description, markdown_description, listId, listName, status, priority, dueDate } = parameters;
|
|
703
|
+
// Validate required fields
|
|
704
|
+
if (!name) {
|
|
705
|
+
throw new Error("Task name is required");
|
|
977
706
|
}
|
|
978
707
|
let targetListId = listId;
|
|
979
708
|
// If no listId but listName is provided, look up the list ID
|
|
980
709
|
if (!targetListId && listName) {
|
|
981
|
-
|
|
710
|
+
// Use workspace service to find the list by name in the hierarchy
|
|
711
|
+
const hierarchy = await workspaceService.getWorkspaceHierarchy();
|
|
712
|
+
const listInfo = workspaceService.findIDByNameInHierarchy(hierarchy, listName, 'list');
|
|
982
713
|
if (!listInfo) {
|
|
983
714
|
throw new Error(`List "${listName}" not found`);
|
|
984
715
|
}
|
|
@@ -987,94 +718,23 @@ export async function handleCreateBulkTasks(parameters) {
|
|
|
987
718
|
if (!targetListId) {
|
|
988
719
|
throw new Error("Either listId or listName must be provided");
|
|
989
720
|
}
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
721
|
+
// Prepare task data
|
|
722
|
+
const taskData = {
|
|
723
|
+
name,
|
|
724
|
+
description,
|
|
725
|
+
markdown_description,
|
|
726
|
+
status,
|
|
727
|
+
priority: priority,
|
|
728
|
+
due_date: dueDate ? parseDueDate(dueDate) : undefined
|
|
995
729
|
};
|
|
996
|
-
//
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
name: task.name,
|
|
1000
|
-
description: task.description,
|
|
1001
|
-
markdown_description: task.markdown_description,
|
|
1002
|
-
status: task.status,
|
|
1003
|
-
priority: task.priority,
|
|
1004
|
-
due_date: task.dueDate ? parseDueDate(task.dueDate) : undefined
|
|
1005
|
-
};
|
|
1006
|
-
// Add due_date_time flag if due date is set
|
|
1007
|
-
if (task.dueDate && taskData.due_date) {
|
|
1008
|
-
taskData.due_date_time = true;
|
|
1009
|
-
}
|
|
1010
|
-
return taskData;
|
|
1011
|
-
});
|
|
1012
|
-
// Create tasks in bulk using the task service
|
|
1013
|
-
try {
|
|
1014
|
-
const bulkResult = await taskService.createBulkTasks(targetListId, { tasks: clickupTasks });
|
|
1015
|
-
// Update results based on bulk operation outcome
|
|
1016
|
-
results.successful = bulkResult.successfulItems.length;
|
|
1017
|
-
results.failed = bulkResult.failedItems.length;
|
|
1018
|
-
results.failures = bulkResult.failedItems.map(failure => ({
|
|
1019
|
-
task: failure.item.name,
|
|
1020
|
-
error: failure.error.message
|
|
1021
|
-
}));
|
|
1022
|
-
}
|
|
1023
|
-
catch (error) {
|
|
1024
|
-
// If the bulk operation itself fails, mark all tasks as failed
|
|
1025
|
-
results.failed = tasks.length;
|
|
1026
|
-
results.failures = tasks.map(task => ({
|
|
1027
|
-
task: task.name,
|
|
1028
|
-
error: error.message
|
|
1029
|
-
}));
|
|
1030
|
-
}
|
|
1031
|
-
return {
|
|
1032
|
-
content: [{
|
|
1033
|
-
type: "text",
|
|
1034
|
-
text: JSON.stringify(results, null, 2)
|
|
1035
|
-
}]
|
|
1036
|
-
};
|
|
1037
|
-
}
|
|
1038
|
-
/**
|
|
1039
|
-
* Handler for the create_task tool
|
|
1040
|
-
*/
|
|
1041
|
-
export async function handleCreateTask(parameters) {
|
|
1042
|
-
const { name, description, markdown_description, listId, listName, status, priority, dueDate } = parameters;
|
|
1043
|
-
// Validate required fields
|
|
1044
|
-
if (!name) {
|
|
1045
|
-
throw new Error("Task name is required");
|
|
1046
|
-
}
|
|
1047
|
-
let targetListId = listId;
|
|
1048
|
-
// If no listId but listName is provided, look up the list ID
|
|
1049
|
-
if (!targetListId && listName) {
|
|
1050
|
-
// Use workspace service to find the list by name in the hierarchy
|
|
1051
|
-
const hierarchy = await workspaceService.getWorkspaceHierarchy();
|
|
1052
|
-
const listInfo = workspaceService.findIDByNameInHierarchy(hierarchy, listName, 'list');
|
|
1053
|
-
if (!listInfo) {
|
|
1054
|
-
throw new Error(`List "${listName}" not found`);
|
|
1055
|
-
}
|
|
1056
|
-
targetListId = listInfo.id;
|
|
1057
|
-
}
|
|
1058
|
-
if (!targetListId) {
|
|
1059
|
-
throw new Error("Either listId or listName must be provided");
|
|
1060
|
-
}
|
|
1061
|
-
// Prepare task data
|
|
1062
|
-
const taskData = {
|
|
1063
|
-
name,
|
|
1064
|
-
description,
|
|
1065
|
-
markdown_description,
|
|
1066
|
-
status,
|
|
1067
|
-
priority: priority,
|
|
1068
|
-
due_date: dueDate ? parseDueDate(dueDate) : undefined
|
|
1069
|
-
};
|
|
1070
|
-
// Add due_date_time flag if due date is set
|
|
1071
|
-
if (dueDate && taskData.due_date) {
|
|
1072
|
-
taskData.due_date_time = true;
|
|
730
|
+
// Add due_date_time flag if due date is set
|
|
731
|
+
if (dueDate && taskData.due_date) {
|
|
732
|
+
taskData.due_date_time = true;
|
|
1073
733
|
}
|
|
1074
734
|
// Create the task
|
|
1075
735
|
const task = await taskService.createTask(targetListId, taskData);
|
|
1076
|
-
//
|
|
1077
|
-
|
|
736
|
+
// Create response object
|
|
737
|
+
const response = {
|
|
1078
738
|
content: [{
|
|
1079
739
|
type: "text",
|
|
1080
740
|
text: JSON.stringify({
|
|
@@ -1089,10 +749,9 @@ export async function handleCreateTask(parameters) {
|
|
|
1089
749
|
}, null, 2)
|
|
1090
750
|
}]
|
|
1091
751
|
};
|
|
752
|
+
// Add sponsor message to response
|
|
753
|
+
return enhanceResponseWithSponsor(response);
|
|
1092
754
|
}
|
|
1093
|
-
/**
|
|
1094
|
-
* Handler for the update_task tool
|
|
1095
|
-
*/
|
|
1096
755
|
export async function handleUpdateTask(parameters) {
|
|
1097
756
|
const { taskId, taskName, listName, name, description, markdown_description, status, priority, dueDate } = parameters;
|
|
1098
757
|
let targetTaskId = taskId;
|
|
@@ -1156,9 +815,6 @@ export async function handleUpdateTask(parameters) {
|
|
|
1156
815
|
}]
|
|
1157
816
|
};
|
|
1158
817
|
}
|
|
1159
|
-
/**
|
|
1160
|
-
* Handler for the move_task tool
|
|
1161
|
-
*/
|
|
1162
818
|
export async function handleMoveTask(parameters) {
|
|
1163
819
|
const { taskId, taskName, sourceListName, listId, listName } = parameters;
|
|
1164
820
|
let targetTaskId = taskId;
|
|
@@ -1220,9 +876,6 @@ export async function handleMoveTask(parameters) {
|
|
|
1220
876
|
}]
|
|
1221
877
|
};
|
|
1222
878
|
}
|
|
1223
|
-
/**
|
|
1224
|
-
* Handler for the duplicate_task tool
|
|
1225
|
-
*/
|
|
1226
879
|
export async function handleDuplicateTask(parameters) {
|
|
1227
880
|
const { taskId, taskName, sourceListName, listId, listName } = parameters;
|
|
1228
881
|
let targetTaskId = taskId;
|
|
@@ -1284,9 +937,6 @@ export async function handleDuplicateTask(parameters) {
|
|
|
1284
937
|
}]
|
|
1285
938
|
};
|
|
1286
939
|
}
|
|
1287
|
-
/**
|
|
1288
|
-
* Handler for the get_tasks tool
|
|
1289
|
-
*/
|
|
1290
940
|
export async function handleGetTasks(parameters) {
|
|
1291
941
|
const { listId, listName, archived, page, order_by, reverse, subtasks, statuses, include_closed, assignees, due_date_gt, due_date_lt, date_created_gt, date_created_lt, date_updated_gt, date_updated_lt, custom_fields } = parameters;
|
|
1292
942
|
let targetListId = listId;
|
|
@@ -1334,9 +984,6 @@ export async function handleGetTasks(parameters) {
|
|
|
1334
984
|
}]
|
|
1335
985
|
};
|
|
1336
986
|
}
|
|
1337
|
-
/**
|
|
1338
|
-
* Handler for the delete_task tool
|
|
1339
|
-
*/
|
|
1340
987
|
export async function handleDeleteTask(parameters) {
|
|
1341
988
|
const { taskId, taskName, listName } = parameters;
|
|
1342
989
|
let targetTaskId = taskId;
|
|
@@ -1388,204 +1035,634 @@ export async function handleDeleteTask(parameters) {
|
|
|
1388
1035
|
}]
|
|
1389
1036
|
};
|
|
1390
1037
|
}
|
|
1038
|
+
export async function handleGetTaskComments(parameters) {
|
|
1039
|
+
const { taskId, taskName, listName, start, startId } = parameters;
|
|
1040
|
+
try {
|
|
1041
|
+
// Call the handler with validation
|
|
1042
|
+
const result = await getTaskCommentsTool.handler({
|
|
1043
|
+
taskId,
|
|
1044
|
+
taskName,
|
|
1045
|
+
listName,
|
|
1046
|
+
start: start ? Number(start) : undefined,
|
|
1047
|
+
startId
|
|
1048
|
+
});
|
|
1049
|
+
return {
|
|
1050
|
+
content: [{
|
|
1051
|
+
type: "text",
|
|
1052
|
+
text: JSON.stringify(result, null, 2)
|
|
1053
|
+
}]
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
catch (error) {
|
|
1057
|
+
// Handle and format error response with proper content array
|
|
1058
|
+
console.error('Error getting task comments:', error);
|
|
1059
|
+
return {
|
|
1060
|
+
content: [{
|
|
1061
|
+
type: "text",
|
|
1062
|
+
text: JSON.stringify({
|
|
1063
|
+
error: error.message || 'Failed to get task comments',
|
|
1064
|
+
taskId: taskId || null,
|
|
1065
|
+
taskName: taskName || null,
|
|
1066
|
+
listName: listName || null
|
|
1067
|
+
}, null, 2)
|
|
1068
|
+
}]
|
|
1069
|
+
};
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
// Helper function for path extraction
|
|
1073
|
+
export function extractPath(node) {
|
|
1074
|
+
if (!node)
|
|
1075
|
+
return '';
|
|
1076
|
+
if (!node.parent)
|
|
1077
|
+
return node.name;
|
|
1078
|
+
return `${extractPath(node.parent)} > ${node.name}`;
|
|
1079
|
+
}
|
|
1080
|
+
// Helper function for path traversal
|
|
1081
|
+
export function extractTreePath(root, targetId) {
|
|
1082
|
+
if (!root)
|
|
1083
|
+
return [];
|
|
1084
|
+
// If this node is the target, return it in an array
|
|
1085
|
+
if (root.id === targetId) {
|
|
1086
|
+
return [root];
|
|
1087
|
+
}
|
|
1088
|
+
// Check children if they exist
|
|
1089
|
+
if (root.children) {
|
|
1090
|
+
for (const child of root.children) {
|
|
1091
|
+
const path = extractTreePath(child, targetId);
|
|
1092
|
+
if (path.length > 0) {
|
|
1093
|
+
return [root, ...path];
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
// Not found in this branch
|
|
1098
|
+
return [];
|
|
1099
|
+
}
|
|
1100
|
+
// After the existing tools, add the bulk tools:
|
|
1391
1101
|
/**
|
|
1392
|
-
*
|
|
1102
|
+
* Tool definition for creating multiple tasks at once
|
|
1393
1103
|
*/
|
|
1394
|
-
export
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1104
|
+
export const createBulkTasksTool = {
|
|
1105
|
+
name: "create_bulk_tasks",
|
|
1106
|
+
description: "Create multiple tasks in a list efficiently. You MUST provide:\n1. An array of tasks with required properties\n2. Either listId or listName to specify the target list\n\nOptional: Configure batch size and concurrency for performance.",
|
|
1107
|
+
inputSchema: {
|
|
1108
|
+
type: "object",
|
|
1109
|
+
properties: {
|
|
1110
|
+
listId: {
|
|
1111
|
+
type: "string",
|
|
1112
|
+
description: "ID of list for new tasks (preferred). Use this instead of listName if you have it."
|
|
1113
|
+
},
|
|
1114
|
+
listName: {
|
|
1115
|
+
type: "string",
|
|
1116
|
+
description: "Name of list for new tasks. Only use if you don't have listId."
|
|
1117
|
+
},
|
|
1118
|
+
tasks: {
|
|
1119
|
+
type: "array",
|
|
1120
|
+
description: "Array of tasks to create. Each task must have at least a name.",
|
|
1121
|
+
items: {
|
|
1122
|
+
type: "object",
|
|
1123
|
+
properties: {
|
|
1124
|
+
name: {
|
|
1125
|
+
type: "string",
|
|
1126
|
+
description: "Task name with emoji prefix"
|
|
1127
|
+
},
|
|
1128
|
+
description: {
|
|
1129
|
+
type: "string",
|
|
1130
|
+
description: "Plain text description"
|
|
1131
|
+
},
|
|
1132
|
+
markdown_description: {
|
|
1133
|
+
type: "string",
|
|
1134
|
+
description: "Markdown description (overrides plain text)"
|
|
1135
|
+
},
|
|
1136
|
+
status: {
|
|
1137
|
+
type: "string",
|
|
1138
|
+
description: "Task status (uses list default if omitted)"
|
|
1139
|
+
},
|
|
1140
|
+
priority: {
|
|
1141
|
+
type: "number",
|
|
1142
|
+
description: "Priority 1-4 (1=urgent, 4=low)"
|
|
1143
|
+
},
|
|
1144
|
+
dueDate: {
|
|
1145
|
+
type: "string",
|
|
1146
|
+
description: "Due date. Supports Unix timestamps (in milliseconds) and natural language expressions like '1 hour from now', 'tomorrow', 'next week', etc."
|
|
1147
|
+
}
|
|
1148
|
+
},
|
|
1149
|
+
required: ["name"]
|
|
1419
1150
|
}
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1151
|
+
},
|
|
1152
|
+
options: {
|
|
1153
|
+
oneOf: [
|
|
1154
|
+
{
|
|
1155
|
+
type: "object",
|
|
1156
|
+
description: "Optional processing settings",
|
|
1157
|
+
properties: {
|
|
1158
|
+
batchSize: {
|
|
1159
|
+
type: "number",
|
|
1160
|
+
description: "Tasks per batch (default: 10)"
|
|
1161
|
+
},
|
|
1162
|
+
concurrency: {
|
|
1163
|
+
type: "number",
|
|
1164
|
+
description: "Parallel operations (default: 3)"
|
|
1165
|
+
},
|
|
1166
|
+
continueOnError: {
|
|
1167
|
+
type: "boolean",
|
|
1168
|
+
description: "Continue if some tasks fail"
|
|
1169
|
+
},
|
|
1170
|
+
retryCount: {
|
|
1171
|
+
type: "number",
|
|
1172
|
+
description: "Retry attempts for failures"
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
},
|
|
1176
|
+
{
|
|
1177
|
+
type: "string",
|
|
1178
|
+
description: "JSON string representing options. Will be parsed automatically."
|
|
1179
|
+
}
|
|
1180
|
+
],
|
|
1181
|
+
description: "Processing options (or JSON string representing options)"
|
|
1182
|
+
}
|
|
1183
|
+
},
|
|
1184
|
+
required: ["tasks"]
|
|
1185
|
+
}
|
|
1186
|
+
};
|
|
1187
|
+
/**
|
|
1188
|
+
* Tool definition for updating multiple tasks
|
|
1189
|
+
*/
|
|
1190
|
+
export const updateBulkTasksTool = {
|
|
1191
|
+
name: "update_bulk_tasks",
|
|
1192
|
+
description: "Update multiple tasks efficiently. For each task, you MUST provide either:\n1. taskId alone (preferred)\n2. taskName + listName\n\nOnly specified fields will be updated for each task.",
|
|
1193
|
+
inputSchema: {
|
|
1194
|
+
type: "object",
|
|
1195
|
+
properties: {
|
|
1196
|
+
tasks: {
|
|
1197
|
+
type: "array",
|
|
1198
|
+
description: "Array of tasks to update",
|
|
1199
|
+
items: {
|
|
1200
|
+
type: "object",
|
|
1201
|
+
properties: {
|
|
1202
|
+
taskId: {
|
|
1203
|
+
type: "string",
|
|
1204
|
+
description: "Task ID (preferred). Use instead of taskName if available."
|
|
1205
|
+
},
|
|
1206
|
+
taskName: {
|
|
1207
|
+
type: "string",
|
|
1208
|
+
description: "Task name. Requires listName when used."
|
|
1209
|
+
},
|
|
1210
|
+
listName: {
|
|
1211
|
+
type: "string",
|
|
1212
|
+
description: "REQUIRED with taskName: List containing the task."
|
|
1213
|
+
},
|
|
1214
|
+
name: {
|
|
1215
|
+
type: "string",
|
|
1216
|
+
description: "New name with emoji prefix"
|
|
1217
|
+
},
|
|
1218
|
+
description: {
|
|
1219
|
+
type: "string",
|
|
1220
|
+
description: "New plain text description"
|
|
1221
|
+
},
|
|
1222
|
+
markdown_description: {
|
|
1223
|
+
type: "string",
|
|
1224
|
+
description: "New markdown description"
|
|
1225
|
+
},
|
|
1226
|
+
status: {
|
|
1227
|
+
type: "string",
|
|
1228
|
+
description: "New status"
|
|
1229
|
+
},
|
|
1230
|
+
priority: {
|
|
1231
|
+
type: ["number", "null"],
|
|
1232
|
+
enum: [1, 2, 3, 4, null],
|
|
1233
|
+
description: "New priority (1-4 or null)"
|
|
1234
|
+
},
|
|
1235
|
+
dueDate: {
|
|
1236
|
+
type: "string",
|
|
1237
|
+
description: "New due date. Supports Unix timestamps (in milliseconds) and natural language expressions like '1 hour from now', 'tomorrow', etc."
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1424
1240
|
}
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1241
|
+
},
|
|
1242
|
+
options: {
|
|
1243
|
+
oneOf: [
|
|
1244
|
+
{
|
|
1245
|
+
type: "object",
|
|
1246
|
+
description: "Optional processing settings",
|
|
1247
|
+
properties: {
|
|
1248
|
+
batchSize: {
|
|
1249
|
+
type: "number",
|
|
1250
|
+
description: "Tasks per batch (default: 10)"
|
|
1251
|
+
},
|
|
1252
|
+
concurrency: {
|
|
1253
|
+
type: "number",
|
|
1254
|
+
description: "Parallel operations (default: 3)"
|
|
1255
|
+
},
|
|
1256
|
+
continueOnError: {
|
|
1257
|
+
type: "boolean",
|
|
1258
|
+
description: "Continue if some tasks fail"
|
|
1259
|
+
},
|
|
1260
|
+
retryCount: {
|
|
1261
|
+
type: "number",
|
|
1262
|
+
description: "Retry attempts for failures"
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
},
|
|
1266
|
+
{
|
|
1267
|
+
type: "string",
|
|
1268
|
+
description: "JSON string representing options. Will be parsed automatically."
|
|
1269
|
+
}
|
|
1270
|
+
],
|
|
1271
|
+
description: "Processing options (or JSON string representing options)"
|
|
1428
1272
|
}
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1273
|
+
},
|
|
1274
|
+
required: ["tasks"]
|
|
1275
|
+
}
|
|
1276
|
+
};
|
|
1277
|
+
/**
|
|
1278
|
+
* Tool definition for moving multiple tasks
|
|
1279
|
+
*/
|
|
1280
|
+
export const moveBulkTasksTool = {
|
|
1281
|
+
name: "move_bulk_tasks",
|
|
1282
|
+
description: "Move multiple tasks to a different list efficiently. For each task, you MUST provide either:\n1. taskId alone (preferred)\n2. taskName + listName\n\nWARNING: Task statuses may reset if target list has different status options.",
|
|
1283
|
+
inputSchema: {
|
|
1284
|
+
type: "object",
|
|
1285
|
+
properties: {
|
|
1286
|
+
tasks: {
|
|
1287
|
+
type: "array",
|
|
1288
|
+
description: "Array of tasks to move",
|
|
1289
|
+
items: {
|
|
1290
|
+
type: "object",
|
|
1291
|
+
properties: {
|
|
1292
|
+
taskId: {
|
|
1293
|
+
type: "string",
|
|
1294
|
+
description: "Task ID (preferred). Use instead of taskName if available."
|
|
1295
|
+
},
|
|
1296
|
+
taskName: {
|
|
1297
|
+
type: "string",
|
|
1298
|
+
description: "Task name. Requires listName when used."
|
|
1299
|
+
},
|
|
1300
|
+
listName: {
|
|
1301
|
+
type: "string",
|
|
1302
|
+
description: "REQUIRED with taskName: List containing the task."
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
},
|
|
1307
|
+
targetListId: {
|
|
1308
|
+
type: "string",
|
|
1309
|
+
description: "ID of destination list (preferred). Use instead of targetListName if available."
|
|
1310
|
+
},
|
|
1311
|
+
targetListName: {
|
|
1312
|
+
type: "string",
|
|
1313
|
+
description: "Name of destination list. Only use if you don't have targetListId."
|
|
1314
|
+
},
|
|
1315
|
+
options: {
|
|
1316
|
+
oneOf: [
|
|
1317
|
+
{
|
|
1318
|
+
type: "object",
|
|
1319
|
+
description: "Optional processing settings",
|
|
1320
|
+
properties: {
|
|
1321
|
+
batchSize: {
|
|
1322
|
+
type: "number",
|
|
1323
|
+
description: "Tasks per batch (default: 10)"
|
|
1324
|
+
},
|
|
1325
|
+
concurrency: {
|
|
1326
|
+
type: "number",
|
|
1327
|
+
description: "Parallel operations (default: 3)"
|
|
1328
|
+
},
|
|
1329
|
+
continueOnError: {
|
|
1330
|
+
type: "boolean",
|
|
1331
|
+
description: "Continue if some tasks fail"
|
|
1332
|
+
},
|
|
1333
|
+
retryCount: {
|
|
1334
|
+
type: "number",
|
|
1335
|
+
description: "Retry attempts for failures"
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
},
|
|
1339
|
+
{
|
|
1340
|
+
type: "string",
|
|
1341
|
+
description: "JSON string representing options. Will be parsed automatically."
|
|
1342
|
+
}
|
|
1343
|
+
],
|
|
1344
|
+
description: "Processing options (or JSON string representing options)"
|
|
1432
1345
|
}
|
|
1433
|
-
|
|
1434
|
-
|
|
1346
|
+
},
|
|
1347
|
+
required: ["tasks"]
|
|
1348
|
+
}
|
|
1349
|
+
};
|
|
1350
|
+
/**
|
|
1351
|
+
* Tool definition for deleting multiple tasks
|
|
1352
|
+
*/
|
|
1353
|
+
export const deleteBulkTasksTool = {
|
|
1354
|
+
name: "delete_bulk_tasks",
|
|
1355
|
+
description: "⚠️ PERMANENTLY DELETE multiple tasks. This action cannot be undone. For each task, you MUST provide either:\n1. taskId alone (preferred and safest)\n2. taskName + listName (use with caution)",
|
|
1356
|
+
inputSchema: {
|
|
1357
|
+
type: "object",
|
|
1358
|
+
properties: {
|
|
1359
|
+
tasks: {
|
|
1360
|
+
type: "array",
|
|
1361
|
+
description: "Array of tasks to delete",
|
|
1362
|
+
items: {
|
|
1363
|
+
type: "object",
|
|
1364
|
+
properties: {
|
|
1365
|
+
taskId: {
|
|
1366
|
+
type: "string",
|
|
1367
|
+
description: "Task ID (preferred). Use instead of taskName if available."
|
|
1368
|
+
},
|
|
1369
|
+
taskName: {
|
|
1370
|
+
type: "string",
|
|
1371
|
+
description: "Task name. Requires listName when used."
|
|
1372
|
+
},
|
|
1373
|
+
listName: {
|
|
1374
|
+
type: "string",
|
|
1375
|
+
description: "REQUIRED with taskName: List containing the task."
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
},
|
|
1380
|
+
options: {
|
|
1381
|
+
oneOf: [
|
|
1382
|
+
{
|
|
1383
|
+
type: "object",
|
|
1384
|
+
description: "Optional processing settings",
|
|
1385
|
+
properties: {
|
|
1386
|
+
batchSize: {
|
|
1387
|
+
type: "number",
|
|
1388
|
+
description: "Tasks per batch (default: 10)"
|
|
1389
|
+
},
|
|
1390
|
+
concurrency: {
|
|
1391
|
+
type: "number",
|
|
1392
|
+
description: "Parallel operations (default: 3)"
|
|
1393
|
+
},
|
|
1394
|
+
continueOnError: {
|
|
1395
|
+
type: "boolean",
|
|
1396
|
+
description: "Continue if some tasks fail"
|
|
1397
|
+
},
|
|
1398
|
+
retryCount: {
|
|
1399
|
+
type: "number",
|
|
1400
|
+
description: "Retry attempts for failures"
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
},
|
|
1404
|
+
{
|
|
1405
|
+
type: "string",
|
|
1406
|
+
description: "JSON string representing options. Will be parsed automatically."
|
|
1407
|
+
}
|
|
1408
|
+
],
|
|
1409
|
+
description: "Processing options (or JSON string representing options)"
|
|
1435
1410
|
}
|
|
1436
|
-
|
|
1411
|
+
},
|
|
1412
|
+
required: ["tasks"]
|
|
1413
|
+
}
|
|
1414
|
+
};
|
|
1415
|
+
/**
|
|
1416
|
+
* Handler for bulk task creation
|
|
1417
|
+
*/
|
|
1418
|
+
export async function handleCreateBulkTasks(parameters) {
|
|
1419
|
+
const { listId, listName, tasks, options: rawOptions } = parameters;
|
|
1420
|
+
// Handle options parameter - may be a string that needs to be parsed
|
|
1421
|
+
let options = rawOptions;
|
|
1422
|
+
if (typeof rawOptions === 'string') {
|
|
1423
|
+
try {
|
|
1424
|
+
options = JSON.parse(rawOptions);
|
|
1437
1425
|
}
|
|
1438
1426
|
catch (error) {
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1427
|
+
// Just use default options on parse error
|
|
1428
|
+
options = undefined;
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
try {
|
|
1432
|
+
// Validate tasks parameter
|
|
1433
|
+
if (!tasks || !Array.isArray(tasks) || tasks.length === 0) {
|
|
1434
|
+
throw new Error('You must provide a non-empty array of tasks to create');
|
|
1444
1435
|
}
|
|
1436
|
+
// Resolve list ID
|
|
1437
|
+
const targetListId = await resolveListId(listId, listName);
|
|
1438
|
+
// Format tasks with proper data types
|
|
1439
|
+
const formattedTasks = tasks.map((task) => ({
|
|
1440
|
+
name: task.name,
|
|
1441
|
+
description: task.description,
|
|
1442
|
+
markdown_description: task.markdown_description,
|
|
1443
|
+
status: task.status,
|
|
1444
|
+
priority: task.priority,
|
|
1445
|
+
due_date: task.dueDate ? parseDueDate(task.dueDate) : undefined,
|
|
1446
|
+
due_date_time: task.dueDate ? true : undefined
|
|
1447
|
+
}));
|
|
1448
|
+
// Use bulk service to create tasks
|
|
1449
|
+
const result = await bulkService.createTasks(targetListId, formattedTasks, options);
|
|
1450
|
+
// Format response
|
|
1451
|
+
const response = {
|
|
1452
|
+
content: [{
|
|
1453
|
+
type: "text",
|
|
1454
|
+
text: JSON.stringify({
|
|
1455
|
+
total: result.totals.total,
|
|
1456
|
+
successful: result.totals.success,
|
|
1457
|
+
failed: result.totals.failure,
|
|
1458
|
+
failures: result.failed.map(failure => ({
|
|
1459
|
+
task: failure.item.name,
|
|
1460
|
+
error: failure.error.message
|
|
1461
|
+
}))
|
|
1462
|
+
}, null, 2)
|
|
1463
|
+
}]
|
|
1464
|
+
};
|
|
1465
|
+
return enhanceResponseWithSponsor(response);
|
|
1445
1466
|
}
|
|
1446
|
-
|
|
1447
|
-
|
|
1467
|
+
catch (error) {
|
|
1468
|
+
return {
|
|
1469
|
+
content: [{
|
|
1470
|
+
type: "text",
|
|
1471
|
+
text: JSON.stringify({
|
|
1472
|
+
error: error.message || 'Failed to create tasks in bulk',
|
|
1473
|
+
listId,
|
|
1474
|
+
listName
|
|
1475
|
+
}, null, 2)
|
|
1476
|
+
}]
|
|
1477
|
+
};
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
/**
|
|
1481
|
+
* Handler for bulk task updates
|
|
1482
|
+
*/
|
|
1483
|
+
export async function handleUpdateBulkTasks(parameters) {
|
|
1484
|
+
const { tasks, options: rawOptions } = parameters;
|
|
1485
|
+
// Handle options parameter - may be a string that needs to be parsed
|
|
1486
|
+
let options = rawOptions;
|
|
1487
|
+
if (typeof rawOptions === 'string') {
|
|
1448
1488
|
try {
|
|
1449
|
-
|
|
1450
|
-
// Process successful deletions
|
|
1451
|
-
for (const deletedId of bulkResult.successfulItems) {
|
|
1452
|
-
results.successful++;
|
|
1453
|
-
const taskInfo = taskMap.get(deletedId);
|
|
1454
|
-
results.deleted.push({
|
|
1455
|
-
id: deletedId,
|
|
1456
|
-
name: taskInfo?.name || "Unknown",
|
|
1457
|
-
deleted: true
|
|
1458
|
-
});
|
|
1459
|
-
}
|
|
1460
|
-
// Process failed deletions
|
|
1461
|
-
for (const failure of bulkResult.failedItems) {
|
|
1462
|
-
results.failed++;
|
|
1463
|
-
const taskInfo = taskMap.get(failure.item);
|
|
1464
|
-
results.failures.push({
|
|
1465
|
-
task: taskInfo?.name || failure.item,
|
|
1466
|
-
error: failure.error.message
|
|
1467
|
-
});
|
|
1468
|
-
}
|
|
1489
|
+
options = JSON.parse(rawOptions);
|
|
1469
1490
|
}
|
|
1470
1491
|
catch (error) {
|
|
1471
|
-
//
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1492
|
+
// Just use default options on parse error
|
|
1493
|
+
options = undefined;
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
try {
|
|
1497
|
+
// Validate tasks parameter
|
|
1498
|
+
if (!tasks || !Array.isArray(tasks) || tasks.length === 0) {
|
|
1499
|
+
throw new Error('You must provide a non-empty array of tasks to update');
|
|
1500
|
+
}
|
|
1501
|
+
// Process the tasks using TaskResolver
|
|
1502
|
+
const tasksToUpdate = await Promise.all(tasks.map(async (task) => {
|
|
1503
|
+
const taskId = await resolveTaskId(task.taskId, task.taskName, undefined, task.listName);
|
|
1504
|
+
// Create update data object
|
|
1505
|
+
const updateData = {};
|
|
1506
|
+
if (task.name !== undefined)
|
|
1507
|
+
updateData.name = task.name;
|
|
1508
|
+
if (task.description !== undefined)
|
|
1509
|
+
updateData.description = task.description;
|
|
1510
|
+
if (task.markdown_description !== undefined)
|
|
1511
|
+
updateData.markdown_description = task.markdown_description;
|
|
1512
|
+
if (task.status !== undefined)
|
|
1513
|
+
updateData.status = task.status;
|
|
1514
|
+
if (task.priority !== undefined) {
|
|
1515
|
+
updateData.priority = task.priority === null ? null : task.priority;
|
|
1516
|
+
}
|
|
1517
|
+
if (task.dueDate !== undefined) {
|
|
1518
|
+
updateData.due_date = task.dueDate ? parseDueDate(task.dueDate) : null;
|
|
1519
|
+
if (task.dueDate && updateData.due_date) {
|
|
1520
|
+
updateData.due_date_time = true;
|
|
1481
1521
|
}
|
|
1482
1522
|
}
|
|
1483
|
-
|
|
1523
|
+
return { id: taskId, data: updateData };
|
|
1524
|
+
}));
|
|
1525
|
+
// Use bulk service to update tasks
|
|
1526
|
+
const result = await bulkService.updateTasks(tasksToUpdate, options);
|
|
1527
|
+
// Format response
|
|
1528
|
+
const response = {
|
|
1529
|
+
content: [{
|
|
1530
|
+
type: "text",
|
|
1531
|
+
text: JSON.stringify({
|
|
1532
|
+
total: result.totals.total,
|
|
1533
|
+
successful: result.totals.success,
|
|
1534
|
+
failed: result.totals.failure,
|
|
1535
|
+
failures: result.failed.map(failure => ({
|
|
1536
|
+
taskId: failure.item.id,
|
|
1537
|
+
error: failure.error.message
|
|
1538
|
+
}))
|
|
1539
|
+
}, null, 2)
|
|
1540
|
+
}]
|
|
1541
|
+
};
|
|
1542
|
+
return enhanceResponseWithSponsor(response);
|
|
1543
|
+
}
|
|
1544
|
+
catch (error) {
|
|
1545
|
+
return {
|
|
1546
|
+
content: [{
|
|
1547
|
+
type: "text",
|
|
1548
|
+
text: JSON.stringify({
|
|
1549
|
+
error: error.message || 'Failed to update tasks in bulk',
|
|
1550
|
+
taskCount: tasks?.length || 0
|
|
1551
|
+
}, null, 2)
|
|
1552
|
+
}]
|
|
1553
|
+
};
|
|
1484
1554
|
}
|
|
1485
|
-
return {
|
|
1486
|
-
content: [{
|
|
1487
|
-
type: "text",
|
|
1488
|
-
text: JSON.stringify(results, null, 2)
|
|
1489
|
-
}]
|
|
1490
|
-
};
|
|
1491
1555
|
}
|
|
1492
1556
|
/**
|
|
1493
1557
|
* Handler for bulk task moves
|
|
1494
1558
|
*/
|
|
1495
1559
|
export async function handleMoveBulkTasks(parameters) {
|
|
1496
|
-
const { tasks, targetListId, targetListName } = parameters;
|
|
1497
|
-
|
|
1498
|
-
|
|
1560
|
+
const { tasks, targetListId, targetListName, options: rawOptions } = parameters;
|
|
1561
|
+
// Handle options parameter - may be a string that needs to be parsed
|
|
1562
|
+
let options = rawOptions;
|
|
1563
|
+
if (typeof rawOptions === 'string') {
|
|
1564
|
+
try {
|
|
1565
|
+
options = JSON.parse(rawOptions);
|
|
1566
|
+
}
|
|
1567
|
+
catch (error) {
|
|
1568
|
+
// Just use default options on parse error
|
|
1569
|
+
options = undefined;
|
|
1570
|
+
}
|
|
1499
1571
|
}
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1572
|
+
try {
|
|
1573
|
+
// Validate tasks parameter
|
|
1574
|
+
if (!tasks || !Array.isArray(tasks) || tasks.length === 0) {
|
|
1575
|
+
throw new Error('You must provide a non-empty array of tasks to move');
|
|
1576
|
+
}
|
|
1577
|
+
// Resolve target list ID
|
|
1578
|
+
const resolvedTargetListId = await resolveListId(targetListId, targetListName);
|
|
1579
|
+
if (!resolvedTargetListId) {
|
|
1580
|
+
throw new Error('Either targetListId or targetListName must be provided');
|
|
1506
1581
|
}
|
|
1507
|
-
|
|
1582
|
+
// Resolve task IDs
|
|
1583
|
+
const taskIds = await Promise.all(tasks.map((task) => resolveTaskId(task.taskId, task.taskName, undefined, task.listName)));
|
|
1584
|
+
// Use bulk service to move tasks
|
|
1585
|
+
const result = await bulkService.moveTasks(taskIds, resolvedTargetListId, options);
|
|
1586
|
+
// Format response
|
|
1587
|
+
const response = {
|
|
1588
|
+
content: [{
|
|
1589
|
+
type: "text",
|
|
1590
|
+
text: JSON.stringify({
|
|
1591
|
+
total: result.totals.total,
|
|
1592
|
+
successful: result.totals.success,
|
|
1593
|
+
failed: result.totals.failure,
|
|
1594
|
+
targetList: targetListName || resolvedTargetListId,
|
|
1595
|
+
failures: result.failed.map(failure => ({
|
|
1596
|
+
taskId: failure.item,
|
|
1597
|
+
error: failure.error.message
|
|
1598
|
+
}))
|
|
1599
|
+
}, null, 2)
|
|
1600
|
+
}]
|
|
1601
|
+
};
|
|
1602
|
+
return enhanceResponseWithSponsor(response);
|
|
1508
1603
|
}
|
|
1509
|
-
|
|
1510
|
-
|
|
1604
|
+
catch (error) {
|
|
1605
|
+
return {
|
|
1606
|
+
content: [{
|
|
1607
|
+
type: "text",
|
|
1608
|
+
text: JSON.stringify({
|
|
1609
|
+
error: error.message || 'Failed to move tasks in bulk',
|
|
1610
|
+
targetListId: targetListId || targetListName,
|
|
1611
|
+
taskCount: tasks?.length || 0
|
|
1612
|
+
}, null, 2)
|
|
1613
|
+
}]
|
|
1614
|
+
};
|
|
1511
1615
|
}
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
};
|
|
1518
|
-
|
|
1616
|
+
}
|
|
1617
|
+
/**
|
|
1618
|
+
* Handler for bulk task deletion
|
|
1619
|
+
*/
|
|
1620
|
+
export async function handleDeleteBulkTasks(parameters) {
|
|
1621
|
+
const { tasks, options: rawOptions } = parameters;
|
|
1622
|
+
// Handle options parameter - may be a string that needs to be parsed
|
|
1623
|
+
let options = rawOptions;
|
|
1624
|
+
if (typeof rawOptions === 'string') {
|
|
1519
1625
|
try {
|
|
1520
|
-
|
|
1521
|
-
if (!taskId && task.taskName) {
|
|
1522
|
-
if (!task.listName) {
|
|
1523
|
-
throw new Error(`List name is required when using task name for task "${task.taskName}"`);
|
|
1524
|
-
}
|
|
1525
|
-
const listInfo = await findListIDByName(workspaceService, task.listName);
|
|
1526
|
-
if (!listInfo) {
|
|
1527
|
-
throw new Error(`List "${task.listName}" not found`);
|
|
1528
|
-
}
|
|
1529
|
-
const taskList = await taskService.getTasks(listInfo.id);
|
|
1530
|
-
const foundTask = taskList.find(t => t.name.toLowerCase() === task.taskName.toLowerCase());
|
|
1531
|
-
if (!foundTask) {
|
|
1532
|
-
throw new Error(`Task "${task.taskName}" not found in list "${task.listName}"`);
|
|
1533
|
-
}
|
|
1534
|
-
taskId = foundTask.id;
|
|
1535
|
-
}
|
|
1536
|
-
if (!taskId) {
|
|
1537
|
-
throw new Error("Either taskId or taskName must be provided");
|
|
1538
|
-
}
|
|
1539
|
-
await taskService.moveTask(taskId, finalTargetListId);
|
|
1540
|
-
results.successful++;
|
|
1626
|
+
options = JSON.parse(rawOptions);
|
|
1541
1627
|
}
|
|
1542
1628
|
catch (error) {
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
task: task.taskId || task.taskName,
|
|
1546
|
-
error: error.message
|
|
1547
|
-
});
|
|
1629
|
+
// Just use default options on parse error
|
|
1630
|
+
options = undefined;
|
|
1548
1631
|
}
|
|
1549
1632
|
}
|
|
1550
|
-
return {
|
|
1551
|
-
content: [{
|
|
1552
|
-
type: "text",
|
|
1553
|
-
text: JSON.stringify(results, null, 2)
|
|
1554
|
-
}]
|
|
1555
|
-
};
|
|
1556
|
-
}
|
|
1557
|
-
/**
|
|
1558
|
-
* Handler for getting task comments
|
|
1559
|
-
*/
|
|
1560
|
-
export async function handleGetTaskComments(parameters) {
|
|
1561
|
-
const { taskId, taskName, listName, start, startId } = parameters;
|
|
1562
1633
|
try {
|
|
1563
|
-
//
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1634
|
+
// Validate tasks parameter
|
|
1635
|
+
if (!tasks || !Array.isArray(tasks) || tasks.length === 0) {
|
|
1636
|
+
throw new Error('You must provide a non-empty array of tasks to delete');
|
|
1637
|
+
}
|
|
1638
|
+
// Resolve task IDs
|
|
1639
|
+
const taskIds = await Promise.all(tasks.map((task) => resolveTaskId(task.taskId, task.taskName, undefined, task.listName)));
|
|
1640
|
+
// Use bulk service to delete tasks
|
|
1641
|
+
const result = await bulkService.deleteTasks(taskIds, options);
|
|
1642
|
+
// Format response
|
|
1643
|
+
const response = {
|
|
1572
1644
|
content: [{
|
|
1573
1645
|
type: "text",
|
|
1574
|
-
text: JSON.stringify(
|
|
1646
|
+
text: JSON.stringify({
|
|
1647
|
+
total: result.totals.total,
|
|
1648
|
+
successful: result.totals.success,
|
|
1649
|
+
failed: result.totals.failure,
|
|
1650
|
+
failures: result.failed.map(failure => ({
|
|
1651
|
+
taskId: failure.item,
|
|
1652
|
+
error: failure.error.message
|
|
1653
|
+
}))
|
|
1654
|
+
}, null, 2)
|
|
1575
1655
|
}]
|
|
1576
1656
|
};
|
|
1657
|
+
return enhanceResponseWithSponsor(response);
|
|
1577
1658
|
}
|
|
1578
1659
|
catch (error) {
|
|
1579
|
-
// Handle and format error response with proper content array
|
|
1580
|
-
console.error('Error getting task comments:', error);
|
|
1581
1660
|
return {
|
|
1582
1661
|
content: [{
|
|
1583
1662
|
type: "text",
|
|
1584
1663
|
text: JSON.stringify({
|
|
1585
|
-
error: error.message || 'Failed to
|
|
1586
|
-
|
|
1587
|
-
taskName: taskName || null,
|
|
1588
|
-
listName: listName || null
|
|
1664
|
+
error: error.message || 'Failed to delete tasks in bulk',
|
|
1665
|
+
taskCount: tasks?.length || 0
|
|
1589
1666
|
}, null, 2)
|
|
1590
1667
|
}]
|
|
1591
1668
|
};
|