@chykalophia/clickup-mcp-server 3.4.0 → 4.1.2

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.
Files changed (133) hide show
  1. package/build/clickup-client/attachments-enhanced.js +4 -1
  2. package/build/clickup-client/attachments-enhanced.js.map +1 -1
  3. package/build/clickup-client/auth.js.map +1 -1
  4. package/build/clickup-client/chat-enhanced.js +3 -1
  5. package/build/clickup-client/chat-enhanced.js.map +1 -1
  6. package/build/clickup-client/checklists.js.map +1 -1
  7. package/build/clickup-client/comments-enhanced.js +4 -3
  8. package/build/clickup-client/comments-enhanced.js.map +1 -1
  9. package/build/clickup-client/comments.js +2 -1
  10. package/build/clickup-client/comments.js.map +1 -1
  11. package/build/clickup-client/custom-fields-enhanced.js +7 -7
  12. package/build/clickup-client/custom-fields-enhanced.js.map +1 -1
  13. package/build/clickup-client/dependencies-enhanced.js.map +1 -1
  14. package/build/clickup-client/docs-enhanced.d.ts +1 -1
  15. package/build/clickup-client/docs-enhanced.js +35 -7
  16. package/build/clickup-client/docs-enhanced.js.map +1 -1
  17. package/build/clickup-client/docs.js +6 -6
  18. package/build/clickup-client/docs.js.map +1 -1
  19. package/build/clickup-client/folders.js.map +1 -1
  20. package/build/clickup-client/goals-enhanced.js.map +1 -1
  21. package/build/clickup-client/index.js +2 -2
  22. package/build/clickup-client/index.js.map +1 -1
  23. package/build/clickup-client/lists.js.map +1 -1
  24. package/build/clickup-client/secure-client.js +7 -6
  25. package/build/clickup-client/secure-client.js.map +1 -1
  26. package/build/clickup-client/tasks.d.ts +41 -0
  27. package/build/clickup-client/tasks.js +128 -2
  28. package/build/clickup-client/tasks.js.map +1 -1
  29. package/build/clickup-client/time-tracking-enhanced.js +2 -6
  30. package/build/clickup-client/time-tracking-enhanced.js.map +1 -1
  31. package/build/clickup-client/views-enhanced.js.map +1 -1
  32. package/build/clickup-client/webhooks-enhanced.js +4 -3
  33. package/build/clickup-client/webhooks-enhanced.js.map +1 -1
  34. package/build/index-efficiency-simple.js +81 -50
  35. package/build/index-efficiency-simple.js.map +1 -1
  36. package/build/index-enhanced.js +1 -0
  37. package/build/index-enhanced.js.map +1 -1
  38. package/build/index.js +1 -0
  39. package/build/index.js.map +1 -1
  40. package/build/resources/checklist-resources.js +1 -0
  41. package/build/resources/checklist-resources.js.map +1 -1
  42. package/build/resources/comment-resources.js +1 -0
  43. package/build/resources/comment-resources.js.map +1 -1
  44. package/build/resources/doc-resources.js +1 -0
  45. package/build/resources/doc-resources.js.map +1 -1
  46. package/build/resources/folder-resources.js +1 -0
  47. package/build/resources/folder-resources.js.map +1 -1
  48. package/build/resources/list-resources.js +1 -0
  49. package/build/resources/list-resources.js.map +1 -1
  50. package/build/resources/space-resources.js +1 -0
  51. package/build/resources/space-resources.js.map +1 -1
  52. package/build/resources/task-resources.js +1 -0
  53. package/build/resources/task-resources.js.map +1 -1
  54. package/build/schemas/attachments-schemas.js +16 -10
  55. package/build/schemas/attachments-schemas.js.map +1 -1
  56. package/build/schemas/chat-schemas.d.ts +7 -7
  57. package/build/schemas/chat-schemas.js +22 -6
  58. package/build/schemas/chat-schemas.js.map +1 -1
  59. package/build/schemas/custom-field-schemas.d.ts +80 -80
  60. package/build/schemas/custom-field-schemas.js +27 -11
  61. package/build/schemas/custom-field-schemas.js.map +1 -1
  62. package/build/schemas/dependencies-schemas.d.ts +11 -11
  63. package/build/schemas/dependencies-schemas.js +14 -11
  64. package/build/schemas/dependencies-schemas.js.map +1 -1
  65. package/build/schemas/document-schemas.d.ts +14 -14
  66. package/build/schemas/document-schemas.js +38 -14
  67. package/build/schemas/document-schemas.js.map +1 -1
  68. package/build/schemas/goals-schemas.d.ts +50 -50
  69. package/build/schemas/goals-schemas.js +4 -1
  70. package/build/schemas/goals-schemas.js.map +1 -1
  71. package/build/schemas/task-schemas.d.ts +382 -0
  72. package/build/schemas/task-schemas.js +115 -0
  73. package/build/schemas/task-schemas.js.map +1 -0
  74. package/build/schemas/time-tracking-schemas.d.ts +20 -20
  75. package/build/schemas/time-tracking-schemas.js +11 -10
  76. package/build/schemas/time-tracking-schemas.js.map +1 -1
  77. package/build/schemas/views-schemas.d.ts +2 -2
  78. package/build/schemas/views-schemas.js +9 -9
  79. package/build/schemas/views-schemas.js.map +1 -1
  80. package/build/schemas/webhook-schemas.js +3 -3
  81. package/build/schemas/webhook-schemas.js.map +1 -1
  82. package/build/tools/attachments-tools-setup.js +146 -65
  83. package/build/tools/attachments-tools-setup.js.map +1 -1
  84. package/build/tools/chat-tools.js +233 -106
  85. package/build/tools/chat-tools.js.map +1 -1
  86. package/build/tools/checklist-tools.js +13 -5
  87. package/build/tools/checklist-tools.js.map +1 -1
  88. package/build/tools/comment-tools.js +35 -14
  89. package/build/tools/comment-tools.js.map +1 -1
  90. package/build/tools/custom-field-tools.js +192 -67
  91. package/build/tools/custom-field-tools.js.map +1 -1
  92. package/build/tools/dependencies-tools-setup.js +133 -58
  93. package/build/tools/dependencies-tools-setup.js.map +1 -1
  94. package/build/tools/doc-tools-enhanced.js +153 -45
  95. package/build/tools/doc-tools-enhanced.js.map +1 -1
  96. package/build/tools/doc-tools.js +4 -1
  97. package/build/tools/doc-tools.js.map +1 -1
  98. package/build/tools/goals-tools.js +113 -41
  99. package/build/tools/goals-tools.js.map +1 -1
  100. package/build/tools/helper-tools.js +131 -53
  101. package/build/tools/helper-tools.js.map +1 -1
  102. package/build/tools/space-tools.js.map +1 -1
  103. package/build/tools/task-tools.js +501 -21
  104. package/build/tools/task-tools.js.map +1 -1
  105. package/build/tools/test-task-update.d.ts +2 -0
  106. package/build/tools/test-task-update.js +124 -0
  107. package/build/tools/test-task-update.js.map +1 -0
  108. package/build/tools/time-tracking-tools.js +105 -36
  109. package/build/tools/time-tracking-tools.js.map +1 -1
  110. package/build/tools/views-tools-setup.js +124 -61
  111. package/build/tools/views-tools-setup.js.map +1 -1
  112. package/build/tools/webhook-tools-setup.js +175 -72
  113. package/build/tools/webhook-tools-setup.js.map +1 -1
  114. package/build/tools/webhook-tools.js +51 -18
  115. package/build/tools/webhook-tools.js.map +1 -1
  116. package/build/utils/clickup-comment-formatter.js +55 -25
  117. package/build/utils/clickup-comment-formatter.js.map +1 -1
  118. package/build/utils/context-aware-suggestions.js +42 -22
  119. package/build/utils/context-aware-suggestions.js.map +1 -1
  120. package/build/utils/error-handling.js +29 -13
  121. package/build/utils/error-handling.js.map +1 -1
  122. package/build/utils/markdown-styling.js.map +1 -1
  123. package/build/utils/markdown.d.ts +5 -4
  124. package/build/utils/markdown.js +24 -19
  125. package/build/utils/markdown.js.map +1 -1
  126. package/build/utils/security.d.ts +76 -0
  127. package/build/utils/security.js +280 -25
  128. package/build/utils/security.js.map +1 -1
  129. package/build/utils/tool-efficiency.js +21 -12
  130. package/build/utils/tool-efficiency.js.map +1 -1
  131. package/package.json +5 -5
  132. package/LICENSE +0 -21
  133. package/README.md +0 -510
@@ -4,6 +4,7 @@ import { createTasksClient } from '../clickup-client/tasks.js';
4
4
  import { createListsClient } from '../clickup-client/lists.js';
5
5
  import { createFoldersClient } from '../clickup-client/folders.js';
6
6
  import { createAuthClient } from '../clickup-client/auth.js';
7
+ import { BulkCreateTasksSchema, BulkUpdateTasksSchema, BulkCreateTaskItemSchema, BulkUpdateTaskItemSchema } from '../schemas/task-schemas.js';
7
8
  // Create clients
8
9
  const clickUpClient = createClickUpClient();
9
10
  const tasksClient = createTasksClient(clickUpClient);
@@ -67,7 +68,10 @@ export function setupTaskTools(server) {
67
68
  });
68
69
  server.tool('clickup_get_task_details', 'Get detailed information about a specific ClickUp task. Returns comprehensive task data including description, assignees, status, and dates.', {
69
70
  task_id: z.string().describe('The ID of the task to get'),
70
- include_subtasks: z.boolean().optional().describe('Whether to include subtasks in the task details')
71
+ include_subtasks: z
72
+ .boolean()
73
+ .optional()
74
+ .describe('Whether to include subtasks in the task details')
71
75
  }, async ({ task_id, include_subtasks }) => {
72
76
  try {
73
77
  const task = await tasksClient.getTask(task_id, { include_subtasks });
@@ -86,20 +90,38 @@ export function setupTaskTools(server) {
86
90
  server.tool('clickup_create_task', 'Create a new task in a ClickUp list with specified properties like name, description, assignees, status, and dates. Supports GitHub Flavored Markdown in description field.', {
87
91
  list_id: z.string().describe('The ID of the list to create the task in'),
88
92
  name: z.string().describe('The name of the task'),
89
- description: z.string().optional().describe('The description of the task (supports GitHub Flavored Markdown including headers, bold, italic, code blocks, links, lists, etc.)'),
90
- assignees: z.array(z.number()).optional().describe('The IDs of the users to assign to the task'),
93
+ description: z
94
+ .string()
95
+ .optional()
96
+ .describe('The description of the task (supports GitHub Flavored Markdown including headers, bold, italic, code blocks, links, lists, etc.)'),
97
+ markdown_content: z
98
+ .string()
99
+ .optional()
100
+ .describe('Raw markdown content for the task description (alternative to description field)'),
101
+ assignees: z
102
+ .array(z.number())
103
+ .optional()
104
+ .describe('The IDs of the users to assign to the task'),
91
105
  tags: z.array(z.string()).optional().describe('The tags to add to the task'),
92
106
  status: z.string().optional().describe('The status of the task'),
93
107
  priority: z.number().optional().describe('The priority of the task (1-4)'),
94
108
  due_date: z.number().optional().describe('The due date of the task (Unix timestamp)'),
95
109
  due_date_time: z.boolean().optional().describe('Whether the due date includes a time'),
96
- time_estimate: z.number().optional().describe('The time estimate for the task (in milliseconds)'),
110
+ time_estimate: z
111
+ .number()
112
+ .optional()
113
+ .describe('The time estimate for the task (in milliseconds)'),
97
114
  start_date: z.number().optional().describe('The start date of the task (Unix timestamp)'),
98
115
  start_date_time: z.boolean().optional().describe('Whether the start date includes a time'),
99
116
  notify_all: z.boolean().optional().describe('Whether to notify all assignees'),
100
117
  parent: z.string().optional().describe('The ID of the parent task')
101
118
  }, async ({ list_id, ...taskParams }) => {
102
119
  try {
120
+ // If both description and markdown_content are provided, prefer markdown_content
121
+ if (taskParams.markdown_content && taskParams.description) {
122
+ console.warn('Both description and markdown_content provided. Using markdown_content.');
123
+ delete taskParams.description;
124
+ }
103
125
  const result = await tasksClient.createTask(list_id, taskParams);
104
126
  return {
105
127
  content: [{ type: 'text', text: JSON.stringify(result, null, 2) }]
@@ -113,21 +135,39 @@ export function setupTaskTools(server) {
113
135
  };
114
136
  }
115
137
  });
116
- server.tool('clickup_update_task', 'Update an existing ClickUp task\'s properties including name, description, assignees, status, and dates. Supports GitHub Flavored Markdown in description field.', {
138
+ server.tool('clickup_update_task', "Update an existing ClickUp task's properties including name, description, assignees, status, and dates. Supports GitHub Flavored Markdown in description field.", {
117
139
  task_id: z.string().describe('The ID of the task to update'),
118
140
  name: z.string().optional().describe('The new name of the task'),
119
- description: z.string().optional().describe('The new description of the task (supports GitHub Flavored Markdown including headers, bold, italic, code blocks, links, lists, etc.)'),
120
- assignees: z.array(z.number()).optional().describe('The IDs of the users to assign to the task'),
141
+ description: z
142
+ .string()
143
+ .optional()
144
+ .describe('The new description of the task (supports GitHub Flavored Markdown including headers, bold, italic, code blocks, links, lists, etc.)'),
145
+ markdown_content: z
146
+ .string()
147
+ .optional()
148
+ .describe('Raw markdown content for the task description (alternative to description field)'),
149
+ assignees: z
150
+ .array(z.number())
151
+ .optional()
152
+ .describe('The IDs of the users to assign to the task'),
121
153
  status: z.string().optional().describe('The new status of the task'),
122
154
  priority: z.number().optional().describe('The new priority of the task (1-4)'),
123
155
  due_date: z.number().optional().describe('The new due date of the task (Unix timestamp)'),
124
156
  due_date_time: z.boolean().optional().describe('Whether the due date includes a time'),
125
- time_estimate: z.number().optional().describe('The new time estimate for the task (in milliseconds)'),
157
+ time_estimate: z
158
+ .number()
159
+ .optional()
160
+ .describe('The new time estimate for the task (in milliseconds)'),
126
161
  start_date: z.number().optional().describe('The new start date of the task (Unix timestamp)'),
127
162
  start_date_time: z.boolean().optional().describe('Whether the start date includes a time'),
128
163
  notify_all: z.boolean().optional().describe('Whether to notify all assignees')
129
164
  }, async ({ task_id, ...taskParams }) => {
130
165
  try {
166
+ // If both description and markdown_content are provided, prefer markdown_content
167
+ if (taskParams.markdown_content && taskParams.description) {
168
+ console.warn('Both description and markdown_content provided. Using markdown_content.');
169
+ delete taskParams.description;
170
+ }
131
171
  const result = await tasksClient.updateTask(task_id, taskParams);
132
172
  return {
133
173
  content: [{ type: 'text', text: JSON.stringify(result, null, 2) }]
@@ -143,7 +183,9 @@ export function setupTaskTools(server) {
143
183
  });
144
184
  // List and Folder tools
145
185
  server.tool('clickup_get_lists', 'Get lists from a ClickUp folder or space. Returns list details including name and content.', {
146
- container_type: z.enum(['folder', 'space']).describe('The type of container to get lists from'),
186
+ container_type: z
187
+ .enum(['folder', 'space'])
188
+ .describe('The type of container to get lists from'),
147
189
  container_id: z.string().describe('The ID of the container to get lists from')
148
190
  }, async ({ container_type, container_id }) => {
149
191
  try {
@@ -187,7 +229,7 @@ export function setupTaskTools(server) {
187
229
  };
188
230
  }
189
231
  });
190
- server.tool('clickup_update_folder', 'Update an existing ClickUp folder\'s name.', {
232
+ server.tool('clickup_update_folder', "Update an existing ClickUp folder's name.", {
191
233
  folder_id: z.string().describe('The ID of the folder to update'),
192
234
  name: z.string().describe('The new name of the folder')
193
235
  }, async ({ folder_id, name }) => {
@@ -240,7 +282,9 @@ export function setupTaskTools(server) {
240
282
  }
241
283
  });
242
284
  server.tool('clickup_create_list', 'Create a new list in a ClickUp folder or space with the specified name.', {
243
- container_type: z.enum(['folder', 'space']).describe('The type of container to create the list in'),
285
+ container_type: z
286
+ .enum(['folder', 'space'])
287
+ .describe('The type of container to create the list in'),
244
288
  container_id: z.string().describe('The ID of the container to create the list in'),
245
289
  name: z.string().describe('The name of the list')
246
290
  }, async ({ container_type, container_id, name }) => {
@@ -302,7 +346,7 @@ export function setupTaskTools(server) {
302
346
  };
303
347
  }
304
348
  });
305
- server.tool('clickup_update_list', 'Update an existing ClickUp list\'s name.', {
349
+ server.tool('clickup_update_list', "Update an existing ClickUp list's name.", {
306
350
  list_id: z.string().describe('The ID of the list to update'),
307
351
  name: z.string().describe('The new name of the list')
308
352
  }, async ({ list_id, name }) => {
@@ -320,13 +364,35 @@ export function setupTaskTools(server) {
320
364
  };
321
365
  }
322
366
  });
323
- server.tool('clickup_delete_list', 'Delete a list from ClickUp. Removes the list and its tasks.', {
324
- list_id: z.string().describe('The ID of the list to delete')
325
- }, async ({ list_id }) => {
367
+ server.tool('clickup_delete_list', '⚠️ DESTRUCTIVE: Delete a list from ClickUp. This action cannot be undone and will permanently remove the list and all its tasks.', {
368
+ list_id: z.string().describe('The ID of the list to delete'),
369
+ confirm_deletion: z
370
+ .boolean()
371
+ .describe('Confirmation that you want to permanently delete this list and all its tasks (must be true)')
372
+ }, async ({ list_id, confirm_deletion }) => {
326
373
  try {
327
- const result = await listsClient.deleteList(list_id);
374
+ if (!confirm_deletion) {
375
+ return {
376
+ content: [
377
+ {
378
+ type: 'text',
379
+ text: '❌ List deletion cancelled. You must set confirm_deletion to true to proceed with this destructive operation.'
380
+ }
381
+ ],
382
+ isError: true
383
+ };
384
+ }
385
+ // Get list details first for confirmation message
386
+ const listDetails = await listsClient.getList(list_id);
387
+ await listsClient.deleteList(list_id);
328
388
  return {
329
- content: [{ type: 'text', text: JSON.stringify(result, null, 2) }]
389
+ content: [
390
+ {
391
+ type: 'text',
392
+ text: `✅ List "${listDetails.name}" (ID: ${list_id}) has been permanently deleted.\n\n` +
393
+ '⚠️ This action cannot be undone. The list and all its tasks have been removed from ClickUp.'
394
+ }
395
+ ]
330
396
  };
331
397
  }
332
398
  catch (error) {
@@ -373,13 +439,421 @@ export function setupTaskTools(server) {
373
439
  };
374
440
  }
375
441
  });
442
+ server.tool('clickup_bulk_create_tasks', 'Create multiple tasks in a ClickUp list in a single operation. More efficient than creating tasks individually. ' +
443
+ 'Supports up to 50 tasks per request.', {
444
+ list_id: z.string().min(1).describe('The ID of the list to create tasks in'),
445
+ tasks: z
446
+ .array(BulkCreateTaskItemSchema)
447
+ .min(1)
448
+ .max(50)
449
+ .describe('Array of tasks to create (maximum 50 tasks per request)'),
450
+ continue_on_error: z
451
+ .boolean()
452
+ .default(false)
453
+ .describe('Whether to continue creating remaining tasks if one fails')
454
+ }, async ({ list_id, tasks, continue_on_error }) => {
455
+ try {
456
+ const validatedData = BulkCreateTasksSchema.parse({ list_id, tasks, continue_on_error });
457
+ // Convert tasks to CreateTaskParams format
458
+ const taskParams = validatedData.tasks.map(task => {
459
+ // Handle markdown content preference
460
+ if (task.markdown_content && task.description) {
461
+ console.warn('Both description and markdown_content provided for a task. Using markdown_content.');
462
+ const { ...rest } = task;
463
+ return rest;
464
+ }
465
+ return task;
466
+ });
467
+ const result = await tasksClient.bulkCreateTasks(validatedData.list_id, taskParams, validatedData.continue_on_error);
468
+ return {
469
+ content: [
470
+ {
471
+ type: 'text',
472
+ text: 'Bulk task creation completed!\n\n' +
473
+ `✅ Successfully created: ${result.success_count} tasks\n` +
474
+ `❌ Failed: ${result.error_count} tasks\n` +
475
+ `📊 Total: ${result.total_count} tasks\n` +
476
+ `⏱️ Execution time: ${result.execution_time_ms}ms\n\n` +
477
+ `Detailed Results:\n${JSON.stringify(result.results, null, 2)}`
478
+ }
479
+ ]
480
+ };
481
+ }
482
+ catch (error) {
483
+ console.error('Error in bulk task creation:', error);
484
+ return {
485
+ content: [
486
+ {
487
+ type: 'text',
488
+ text: `Error in bulk task creation: ${error.message}`
489
+ }
490
+ ],
491
+ isError: true
492
+ };
493
+ }
494
+ });
495
+ server.tool('clickup_bulk_update_tasks', 'Update multiple tasks in ClickUp in a single operation. More efficient than updating tasks individually. ' +
496
+ 'Supports up to 50 tasks per request.', {
497
+ tasks: z
498
+ .array(BulkUpdateTaskItemSchema)
499
+ .min(1)
500
+ .max(50)
501
+ .describe('Array of task updates to perform (maximum 50 tasks per request)'),
502
+ continue_on_error: z
503
+ .boolean()
504
+ .default(false)
505
+ .describe('Whether to continue updating remaining tasks if one fails')
506
+ }, async ({ tasks, continue_on_error }) => {
507
+ try {
508
+ const validatedData = BulkUpdateTasksSchema.parse({ tasks, continue_on_error });
509
+ // Convert tasks to the format expected by bulkUpdateTasks
510
+ const taskUpdates = validatedData.tasks.map(task => {
511
+ // Handle markdown content preference
512
+ if (task.markdown_content && task.description) {
513
+ console.warn('Both description and markdown_content provided for a task. Using markdown_content.');
514
+ const { ...rest } = task;
515
+ return rest;
516
+ }
517
+ return task;
518
+ });
519
+ const result = await tasksClient.bulkUpdateTasks(taskUpdates, validatedData.continue_on_error);
520
+ return {
521
+ content: [
522
+ {
523
+ type: 'text',
524
+ text: 'Bulk task update completed!\n\n' +
525
+ `✅ Successfully updated: ${result.success_count} tasks\n` +
526
+ `❌ Failed: ${result.error_count} tasks\n` +
527
+ `📊 Total: ${result.total_count} tasks\n` +
528
+ `⏱️ Execution time: ${result.execution_time_ms}ms\n\n` +
529
+ `Detailed Results:\n${JSON.stringify(result.results, null, 2)}`
530
+ }
531
+ ]
532
+ };
533
+ }
534
+ catch (error) {
535
+ console.error('Error in bulk task update:', error);
536
+ return {
537
+ content: [
538
+ {
539
+ type: 'text',
540
+ text: `Error in bulk task update: ${error.message}`
541
+ }
542
+ ],
543
+ isError: true
544
+ };
545
+ }
546
+ });
547
+ server.tool('clickup_delete_task', '⚠️ DESTRUCTIVE: Delete a task from ClickUp. This action cannot be undone and will permanently remove the task and all its data.', {
548
+ task_id: z.string().min(1).describe('The ID of the task to delete'),
549
+ confirm_deletion: z
550
+ .boolean()
551
+ .describe('Confirmation that you want to permanently delete this task (must be true)')
552
+ }, async ({ task_id, confirm_deletion }) => {
553
+ try {
554
+ if (!confirm_deletion) {
555
+ return {
556
+ content: [
557
+ {
558
+ type: 'text',
559
+ text: '❌ Task deletion cancelled. You must set confirm_deletion to true to proceed with this destructive operation.'
560
+ }
561
+ ],
562
+ isError: true
563
+ };
564
+ }
565
+ // Get task details first for confirmation message
566
+ const taskDetails = await tasksClient.getTask(task_id);
567
+ await tasksClient.deleteTask(task_id);
568
+ return {
569
+ content: [
570
+ {
571
+ type: 'text',
572
+ text: `✅ Task "${taskDetails.name}" (ID: ${task_id}) has been permanently deleted.\n\n` +
573
+ '⚠️ This action cannot be undone. The task and all its data have been removed from ClickUp.'
574
+ }
575
+ ]
576
+ };
577
+ }
578
+ catch (error) {
579
+ console.error('Error deleting task:', error);
580
+ return {
581
+ content: [
582
+ {
583
+ type: 'text',
584
+ text: `Error deleting task: ${error.message}`
585
+ }
586
+ ],
587
+ isError: true
588
+ };
589
+ }
590
+ });
591
+ server.tool('clickup_bulk_delete_tasks', '⚠️ DESTRUCTIVE: Delete multiple tasks from ClickUp in a single operation. This action cannot be undone and will permanently remove all specified tasks.', {
592
+ task_ids: z
593
+ .array(z.string().min(1))
594
+ .min(1)
595
+ .max(50)
596
+ .describe('Array of task IDs to delete (maximum 50 tasks per request)'),
597
+ confirm_deletion: z
598
+ .boolean()
599
+ .describe('Confirmation that you want to permanently delete these tasks (must be true)'),
600
+ continue_on_error: z
601
+ .boolean()
602
+ .default(false)
603
+ .describe('Whether to continue deleting remaining tasks if one fails')
604
+ }, async ({ task_ids, confirm_deletion, continue_on_error }) => {
605
+ try {
606
+ if (!confirm_deletion) {
607
+ return {
608
+ content: [
609
+ {
610
+ type: 'text',
611
+ text: '❌ Bulk task deletion cancelled. You must set confirm_deletion to true to proceed with this destructive operation.'
612
+ }
613
+ ],
614
+ isError: true
615
+ };
616
+ }
617
+ const startTime = Date.now();
618
+ const results = [];
619
+ let successCount = 0;
620
+ let errorCount = 0;
621
+ for (let i = 0; i < task_ids.length; i++) {
622
+ try {
623
+ const taskId = task_ids[i];
624
+ // Get task name for confirmation
625
+ const taskDetails = await tasksClient.getTask(taskId);
626
+ await tasksClient.deleteTask(taskId);
627
+ results.push({
628
+ success: true,
629
+ task_id: taskId,
630
+ task_name: taskDetails.name,
631
+ index: i
632
+ });
633
+ successCount++;
634
+ }
635
+ catch (error) {
636
+ const errorMessage = error.message || 'Unknown error occurred';
637
+ results.push({
638
+ success: false,
639
+ task_id: task_ids[i],
640
+ error: errorMessage,
641
+ index: i
642
+ });
643
+ errorCount++;
644
+ if (!continue_on_error) {
645
+ // Add remaining tasks as failed
646
+ for (let j = i + 1; j < task_ids.length; j++) {
647
+ results.push({
648
+ success: false,
649
+ task_id: task_ids[j],
650
+ error: 'Skipped due to previous error',
651
+ index: j
652
+ });
653
+ errorCount++;
654
+ }
655
+ break;
656
+ }
657
+ }
658
+ }
659
+ const executionTime = Date.now() - startTime;
660
+ return {
661
+ content: [
662
+ {
663
+ type: 'text',
664
+ text: '⚠️ Bulk task deletion completed!\n\n' +
665
+ `✅ Successfully deleted: ${successCount} tasks\n` +
666
+ `❌ Failed: ${errorCount} tasks\n` +
667
+ `📊 Total: ${task_ids.length} tasks\n` +
668
+ `⏱️ Execution time: ${executionTime}ms\n\n` +
669
+ '⚠️ This action cannot be undone. All successfully deleted tasks have been permanently removed.\n\n' +
670
+ `Detailed Results:\n${JSON.stringify(results, null, 2)}`
671
+ }
672
+ ]
673
+ };
674
+ }
675
+ catch (error) {
676
+ console.error('Error in bulk task deletion:', error);
677
+ return {
678
+ content: [
679
+ {
680
+ type: 'text',
681
+ text: `Error in bulk task deletion: ${error.message}`
682
+ }
683
+ ],
684
+ isError: true
685
+ };
686
+ }
687
+ });
688
+ server.tool('clickup_delete_subtask', '⚠️ DESTRUCTIVE: Delete a subtask from ClickUp. This action cannot be undone and will permanently remove the subtask.', {
689
+ task_id: z.string().min(1).describe('The ID of the subtask to delete'),
690
+ confirm_deletion: z
691
+ .boolean()
692
+ .describe('Confirmation that you want to permanently delete this subtask (must be true)')
693
+ }, async ({ task_id, confirm_deletion }) => {
694
+ try {
695
+ if (!confirm_deletion) {
696
+ return {
697
+ content: [
698
+ {
699
+ type: 'text',
700
+ text: '❌ Subtask deletion cancelled. You must set confirm_deletion to true to proceed with this destructive operation.'
701
+ }
702
+ ],
703
+ isError: true
704
+ };
705
+ }
706
+ // Get subtask details first for confirmation message
707
+ const subtaskDetails = await tasksClient.getTask(task_id);
708
+ await tasksClient.deleteTask(task_id);
709
+ return {
710
+ content: [
711
+ {
712
+ type: 'text',
713
+ text: `✅ Subtask "${subtaskDetails.name}" (ID: ${task_id}) has been permanently deleted.\n\n` +
714
+ '⚠️ This action cannot be undone. The subtask and all its data have been removed from ClickUp.'
715
+ }
716
+ ]
717
+ };
718
+ }
719
+ catch (error) {
720
+ console.error('Error deleting subtask:', error);
721
+ return {
722
+ content: [
723
+ {
724
+ type: 'text',
725
+ text: `Error deleting subtask: ${error.message}`
726
+ }
727
+ ],
728
+ isError: true
729
+ };
730
+ }
731
+ });
732
+ server.tool('clickup_merge_tasks', 'Merge multiple tasks into a single task. The primary task will retain all data, and secondary tasks will be deleted after merging their content.', {
733
+ primary_task_id: z
734
+ .string()
735
+ .min(1)
736
+ .describe('The ID of the task that will remain after merging (receives all merged content)'),
737
+ secondary_task_ids: z
738
+ .array(z.string().min(1))
739
+ .min(1)
740
+ .max(10)
741
+ .describe('Array of task IDs to merge into the primary task (maximum 10 tasks, will be deleted after merge)'),
742
+ merge_descriptions: z
743
+ .boolean()
744
+ .default(true)
745
+ .describe('Whether to merge task descriptions into the primary task'),
746
+ merge_comments: z
747
+ .boolean()
748
+ .default(true)
749
+ .describe('Whether to merge comments from secondary tasks'),
750
+ merge_attachments: z
751
+ .boolean()
752
+ .default(true)
753
+ .describe('Whether to merge attachments from secondary tasks'),
754
+ merge_time_tracking: z
755
+ .boolean()
756
+ .default(true)
757
+ .describe('Whether to merge time tracking data'),
758
+ confirm_merge: z
759
+ .boolean()
760
+ .describe('Confirmation that you want to merge these tasks (secondary tasks will be deleted)')
761
+ }, async ({ primary_task_id, secondary_task_ids, merge_descriptions, merge_comments: _merge_comments, merge_attachments: _merge_attachments, merge_time_tracking: _merge_time_tracking, confirm_merge }) => {
762
+ try {
763
+ if (!confirm_merge) {
764
+ return {
765
+ content: [
766
+ {
767
+ type: 'text',
768
+ text: '❌ Task merge cancelled. You must set confirm_merge to true to proceed. Secondary tasks will be deleted after merging.'
769
+ }
770
+ ],
771
+ isError: true
772
+ };
773
+ }
774
+ // Get all task details first
775
+ const primaryTask = await tasksClient.getTask(primary_task_id);
776
+ const secondaryTasks = await Promise.all(secondary_task_ids.map(id => tasksClient.getTask(id)));
777
+ let mergedDescription = primaryTask.description || '';
778
+ const mergeResults = {
779
+ primary_task: primaryTask.name,
780
+ merged_tasks: [],
781
+ merged_content: {
782
+ descriptions: 0,
783
+ comments: 0,
784
+ attachments: 0,
785
+ time_entries: 0
786
+ }
787
+ };
788
+ // Merge descriptions
789
+ if (merge_descriptions) {
790
+ for (const task of secondaryTasks) {
791
+ if (task.description) {
792
+ mergedDescription += `\n\n---\n**Merged from "${task.name}":**\n${task.description}`;
793
+ mergeResults.merged_content.descriptions++;
794
+ }
795
+ mergeResults.merged_tasks.push(task.name);
796
+ }
797
+ }
798
+ // Update primary task with merged description
799
+ if (merge_descriptions && mergedDescription !== primaryTask.description) {
800
+ await tasksClient.updateTask(primary_task_id, {
801
+ description: mergedDescription
802
+ });
803
+ }
804
+ // TODO: Implement comment, attachment, and time tracking merging
805
+ // This would require additional API calls to get and move these items
806
+ // Delete secondary tasks
807
+ const deletionResults = [];
808
+ for (const taskId of secondary_task_ids) {
809
+ try {
810
+ await tasksClient.deleteTask(taskId);
811
+ deletionResults.push({ task_id: taskId, deleted: true });
812
+ }
813
+ catch (error) {
814
+ deletionResults.push({ task_id: taskId, deleted: false, error: error.message });
815
+ }
816
+ }
817
+ return {
818
+ content: [
819
+ {
820
+ type: 'text',
821
+ text: '✅ Task merge completed!\n\n' +
822
+ `Primary Task: "${primaryTask.name}" (${primary_task_id})\n` +
823
+ `Merged Tasks: ${mergeResults.merged_tasks.join(', ')}\n\n` +
824
+ 'Merged Content:\n' +
825
+ `- Descriptions: ${mergeResults.merged_content.descriptions}\n` +
826
+ `- Comments: ${mergeResults.merged_content.comments} (not yet implemented)\n` +
827
+ `- Attachments: ${mergeResults.merged_content.attachments} (not yet implemented)\n` +
828
+ `- Time Entries: ${mergeResults.merged_content.time_entries} (not yet implemented)\n\n` +
829
+ `Deletion Results:\n${JSON.stringify(deletionResults, null, 2)}\n\n` +
830
+ '⚠️ Secondary tasks have been permanently deleted and cannot be recovered.'
831
+ }
832
+ ]
833
+ };
834
+ }
835
+ catch (error) {
836
+ console.error('Error merging tasks:', error);
837
+ return {
838
+ content: [
839
+ {
840
+ type: 'text',
841
+ text: `Error merging tasks: ${error.message}`
842
+ }
843
+ ],
844
+ isError: true
845
+ };
846
+ }
847
+ });
376
848
  server.tool('clickup_create_list_from_template_in_folder', 'Create a new list in a ClickUp folder using an existing template.', {
377
849
  folder_id: z.string().describe('The ID of the folder to create the list in'),
378
850
  template_id: z.string().describe('The ID of the template to use'),
379
851
  name: z.string().describe('The name of the list')
380
852
  }, async ({ folder_id, template_id, name }) => {
381
853
  try {
382
- const result = await listsClient.createListFromTemplateInFolder(folder_id, template_id, { name });
854
+ const result = await listsClient.createListFromTemplateInFolder(folder_id, template_id, {
855
+ name
856
+ });
383
857
  return {
384
858
  content: [{ type: 'text', text: JSON.stringify(result, null, 2) }]
385
859
  };
@@ -387,7 +861,9 @@ export function setupTaskTools(server) {
387
861
  catch (error) {
388
862
  console.error('Error creating list from template in folder:', error);
389
863
  return {
390
- content: [{ type: 'text', text: `Error creating list from template in folder: ${error.message}` }],
864
+ content: [
865
+ { type: 'text', text: `Error creating list from template in folder: ${error.message}` }
866
+ ],
391
867
  isError: true
392
868
  };
393
869
  }
@@ -398,7 +874,9 @@ export function setupTaskTools(server) {
398
874
  name: z.string().describe('The name of the list')
399
875
  }, async ({ space_id, template_id, name }) => {
400
876
  try {
401
- const result = await listsClient.createListFromTemplateInSpace(space_id, template_id, { name });
877
+ const result = await listsClient.createListFromTemplateInSpace(space_id, template_id, {
878
+ name
879
+ });
402
880
  return {
403
881
  content: [{ type: 'text', text: JSON.stringify(result, null, 2) }]
404
882
  };
@@ -406,7 +884,9 @@ export function setupTaskTools(server) {
406
884
  catch (error) {
407
885
  console.error('Error creating list from template in space:', error);
408
886
  return {
409
- content: [{ type: 'text', text: `Error creating list from template in space: ${error.message}` }],
887
+ content: [
888
+ { type: 'text', text: `Error creating list from template in space: ${error.message}` }
889
+ ],
410
890
  isError: true
411
891
  };
412
892
  }