@taazkareem/clickup-mcp-server 0.6.3 → 0.6.5

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.
@@ -33,22 +33,23 @@ export const attachTaskFileTool = {
33
33
  description: `Purpose: Attaches a file to a ClickUp task.
34
34
 
35
35
  Valid Usage:
36
- 1. Upload from base64: Provide file_data + file_name
37
- 2. Upload from URL or local file: Provide file_url + optional file_name
38
- - For web URLs: Use http:// or https:// URLs
39
- - For local files: Use absolute file paths (starting with / or drive letter)
40
- 3. For large files, advanced options are available via chunk_* parameters
36
+ 1. Use taskId (preferred) - works with both regular and custom IDs
37
+ 2. Use taskName + listName for targeted search
38
+
39
+ File Sources:
40
+ - Base64: Provide file_data + file_name
41
+ - URL: Provide file_url starting with http:// or https://
42
+ - Local file: Provide file_url as absolute path
43
+ - Chunked upload: Use chunk_* parameters for large files
41
44
 
42
45
  Requirements:
43
- - EITHER taskId OR (taskName + listName) is REQUIRED
44
- - EITHER file_data OR file_url is REQUIRED
46
+ - Task identification: EITHER taskId OR taskName REQUIRED
47
+ - File source: EITHER file_data+file_name OR file_url OR chunk_session REQUIRED
45
48
 
46
49
  Notes:
47
- - The system automatically selects the best upload method based on file size and source
48
- - Base64 method has a 10MB size limit due to encoding overhead (file_data parameter)
49
- - URL method works for files hosted online (file_url parameter with http/https)
50
- - Local file method works with absolute paths only (file_url parameter with / or drive letter)
51
- - For large files, the system may use chunked uploading automatically`,
50
+ - System automatically selects best upload method based on file size
51
+ - Base64 uploads limited to 10MB
52
+ - Using taskName without listName may match multiple tasks`,
52
53
  inputSchema: {
53
54
  type: "object",
54
55
  properties: {
@@ -58,11 +59,11 @@ Notes:
58
59
  },
59
60
  taskName: {
60
61
  type: "string",
61
- description: "Name of the task to attach the file to. When using this parameter, you MUST also provide listName."
62
+ description: "Name of the task to attach the file to. The tool will search for tasks with this name across all lists unless listName is specified."
62
63
  },
63
64
  listName: {
64
65
  type: "string",
65
- description: "Name of the list containing the task. REQUIRED when using taskName."
66
+ description: "Optional: Name of list containing the task. Providing this narrows the search to a specific list, improving performance and reducing ambiguity."
66
67
  },
67
68
  file_name: {
68
69
  type: "string",
@@ -97,8 +98,7 @@ Notes:
97
98
  type: "boolean",
98
99
  description: "Optional: For advanced usage with large file chunking. Whether this is the final chunk."
99
100
  }
100
- },
101
- required: [] // Will validate based on context in the handler
101
+ }
102
102
  }
103
103
  };
104
104
  /**
@@ -76,18 +76,16 @@ export const createBulkTasksTool = {
76
76
  description: `Purpose: Create multiple tasks in a list efficiently.
77
77
 
78
78
  Valid Usage:
79
- 1. An array of tasks with required properties + listId (preferred)
80
- 2. An array of tasks with required properties + listName
79
+ 1. Provide listId + array of tasks (preferred)
80
+ 2. Provide listName + array of tasks
81
81
 
82
82
  Requirements:
83
83
  - tasks: REQUIRED (array of tasks, each with at least a name)
84
- - EITHER listId OR listName: REQUIRED
84
+ - List identification: EITHER listId OR listName REQUIRED
85
85
 
86
86
  Notes:
87
- - Configure batch size and concurrency via options for performance
88
- - Each task should have a name with emoji prefix
89
- - All tasks will be created in the same list
90
- - Custom fields can be set for each task using the custom_fields property (array of {id, value} objects)`,
87
+ - Configure batch processing via options parameter
88
+ - Custom fields supported for each task`,
91
89
  inputSchema: {
92
90
  type: "object",
93
91
  properties: {
@@ -157,37 +155,7 @@ Notes:
157
155
  type: "string",
158
156
  description: "Name of list for new tasks. Only use if you don't have listId."
159
157
  },
160
- options: {
161
- description: "Processing options (or JSON string representing options)",
162
- oneOf: [
163
- {
164
- type: "object",
165
- description: "Optional processing settings",
166
- properties: {
167
- batchSize: {
168
- type: "number",
169
- description: "Tasks per batch (default: 10)"
170
- },
171
- concurrency: {
172
- type: "number",
173
- description: "Parallel operations (default: 3)"
174
- },
175
- continueOnError: {
176
- type: "boolean",
177
- description: "Continue if some tasks fail"
178
- },
179
- retryCount: {
180
- type: "number",
181
- description: "Retry attempts for failures"
182
- }
183
- }
184
- },
185
- {
186
- type: "string",
187
- description: "JSON string representing options. Will be parsed automatically."
188
- }
189
- ]
190
- }
158
+ options: bulkOptionsSchema
191
159
  },
192
160
  required: ["tasks"]
193
161
  }
@@ -200,19 +168,16 @@ export const updateBulkTasksTool = {
200
168
  description: `Purpose: Update multiple tasks efficiently in a single operation.
201
169
 
202
170
  Valid Usage:
203
- 1. For each task, provide taskId (preferred)
204
- 2. For each task, provide taskName + listName
171
+ 1. Provide array of tasks with taskId (preferred)
172
+ 2. Provide array of tasks with taskName + listName
205
173
 
206
174
  Requirements:
207
175
  - tasks: REQUIRED (array of tasks to update)
208
- - For each task entry, EITHER taskId OR (taskName + listName) is REQUIRED
209
- - At least one update field per task (name, description, status, priority, dueDate)
176
+ - Each task needs identification and update fields
210
177
 
211
178
  Notes:
212
179
  - Only specified fields will be updated for each task
213
- - Configure batch size and concurrency via options for performance
214
- - Each task can have different fields to update
215
- - Custom fields can be updated using the custom_fields property (array of {id, value} objects)`,
180
+ - Configure batch processing via options parameter`,
216
181
  inputSchema: {
217
182
  type: "object",
218
183
  properties: {
@@ -222,7 +187,22 @@ Notes:
222
187
  items: {
223
188
  type: "object",
224
189
  properties: {
225
- ...taskIdentifierSchema,
190
+ taskId: {
191
+ type: "string",
192
+ description: "Task ID (preferred). Works with both regular task IDs (9 characters) and custom IDs with uppercase prefixes (like 'DEV-1234')."
193
+ },
194
+ taskName: {
195
+ type: "string",
196
+ description: "Task name. Requires listName when used."
197
+ },
198
+ listName: {
199
+ type: "string",
200
+ description: "REQUIRED with taskName: List containing the task."
201
+ },
202
+ customTaskId: {
203
+ type: "string",
204
+ description: "Custom task ID (e.g., 'DEV-1234'). Only use if you want to explicitly force custom ID lookup. In most cases, use taskId which auto-detects ID format."
205
+ },
226
206
  name: {
227
207
  type: "string",
228
208
  description: "New name with emoji prefix"
@@ -268,37 +248,7 @@ Notes:
268
248
  }
269
249
  }
270
250
  },
271
- options: {
272
- description: "Processing options (or JSON string representing options)",
273
- oneOf: [
274
- {
275
- type: "object",
276
- description: "Optional processing settings",
277
- properties: {
278
- batchSize: {
279
- type: "number",
280
- description: "Tasks per batch (default: 10)"
281
- },
282
- concurrency: {
283
- type: "number",
284
- description: "Parallel operations (default: 3)"
285
- },
286
- continueOnError: {
287
- type: "boolean",
288
- description: "Continue if some tasks fail"
289
- },
290
- retryCount: {
291
- type: "number",
292
- description: "Retry attempts for failures"
293
- }
294
- }
295
- },
296
- {
297
- type: "string",
298
- description: "JSON string representing options. Will be parsed automatically."
299
- }
300
- ]
301
- }
251
+ options: bulkOptionsSchema
302
252
  },
303
253
  required: ["tasks"]
304
254
  }
@@ -311,21 +261,14 @@ export const moveBulkTasksTool = {
311
261
  description: `Purpose: Move multiple tasks to a different list efficiently.
312
262
 
313
263
  Valid Usage:
314
- 1. For each task, provide taskId + target list (preferred)
315
- 2. For each task, provide taskName + listName + target list
264
+ 1. Provide tasks array + target list
316
265
 
317
266
  Requirements:
318
- - tasks: REQUIRED (array of tasks to move)
319
- - EITHER targetListId OR targetListName: REQUIRED
320
- - For each task entry, EITHER taskId OR (taskName + listName) is REQUIRED
321
-
322
- Notes:
323
- - Configure batch size and concurrency via options for performance
324
- - All tasks will be moved to the same destination list
267
+ - tasks: REQUIRED (array of task identifiers)
268
+ - Target list: EITHER targetListId OR targetListName REQUIRED
325
269
 
326
270
  Warning:
327
- - Task statuses may reset if destination list has different status options
328
- - Using taskName without listName will fail as tasks may have identical names across lists`,
271
+ - Task statuses may reset if destination list has different status options`,
329
272
  inputSchema: {
330
273
  type: "object",
331
274
  properties: {
@@ -335,7 +278,22 @@ Warning:
335
278
  items: {
336
279
  type: "object",
337
280
  properties: {
338
- ...taskIdentifierSchema
281
+ taskId: {
282
+ type: "string",
283
+ description: "Task ID (preferred). Works with both regular task IDs (9 characters) and custom IDs with uppercase prefixes (like 'DEV-1234')."
284
+ },
285
+ taskName: {
286
+ type: "string",
287
+ description: "Task name. Requires listName when used."
288
+ },
289
+ listName: {
290
+ type: "string",
291
+ description: "REQUIRED with taskName: List containing the task."
292
+ },
293
+ customTaskId: {
294
+ type: "string",
295
+ description: "Custom task ID (e.g., 'DEV-1234'). Only use if you want to explicitly force custom ID lookup. In most cases, use taskId which auto-detects ID format."
296
+ }
339
297
  }
340
298
  }
341
299
  },
@@ -360,20 +318,15 @@ export const deleteBulkTasksTool = {
360
318
  description: `Purpose: PERMANENTLY DELETE multiple tasks at once.
361
319
 
362
320
  Valid Usage:
363
- 1. For each task, provide taskId (preferred and safest)
364
- 2. For each task, provide taskName + listName
321
+ 1. Provide array of tasks with taskId (preferred)
322
+ 2. Provide array of tasks with taskName + listName
365
323
 
366
324
  Requirements:
367
- - tasks: REQUIRED (array of tasks to delete)
368
- - For each task entry, EITHER taskId OR (taskName + listName) is REQUIRED
369
-
370
- Notes:
371
- - Configure batch size and concurrency via options for performance
325
+ - tasks: REQUIRED (array of task identifiers)
372
326
 
373
327
  Warning:
374
- - This action CANNOT be undone for any of the tasks
375
- - Using taskName without listName is dangerous as names may not be unique
376
- - Always provide listName when using taskName for safer targeting`,
328
+ - This action CANNOT be undone
329
+ - Always provide listName when using taskName`,
377
330
  inputSchema: {
378
331
  type: "object",
379
332
  properties: {
@@ -383,7 +336,22 @@ Warning:
383
336
  items: {
384
337
  type: "object",
385
338
  properties: {
386
- ...taskIdentifierSchema
339
+ taskId: {
340
+ type: "string",
341
+ description: "Task ID (preferred). Works with both regular task IDs (9 characters) and custom IDs with uppercase prefixes (like 'DEV-1234')."
342
+ },
343
+ taskName: {
344
+ type: "string",
345
+ description: "Task name. Requires listName when used."
346
+ },
347
+ listName: {
348
+ type: "string",
349
+ description: "REQUIRED with taskName: List containing the task."
350
+ },
351
+ customTaskId: {
352
+ type: "string",
353
+ description: "Custom task ID (e.g., 'DEV-1234'). Only use if you want to explicitly force custom ID lookup. In most cases, use taskId which auto-detects ID format."
354
+ }
387
355
  }
388
356
  }
389
357
  },
@@ -392,58 +392,76 @@ export async function getWorkspaceTasksHandler(taskService, params) {
392
392
  * Handler for creating multiple tasks
393
393
  */
394
394
  export async function createBulkTasksHandler(params) {
395
- validateBulkTasks(params.tasks);
396
- const listId = await getListId(params.listId, params.listName);
397
- // Process tasks - prepare data for each task
398
- const tasks = params.tasks.map(task => {
399
- const processedTask = { ...task };
395
+ const { tasks, listId, listName, options } = params;
396
+ // Validate tasks array
397
+ validateBulkTasks(tasks, 'create');
398
+ // Validate and resolve list ID
399
+ const targetListId = await resolveListIdWithValidation(listId, listName);
400
+ // Format tasks for creation
401
+ const formattedTasks = tasks.map(task => {
402
+ const taskData = {
403
+ name: task.name,
404
+ description: task.description,
405
+ markdown_description: task.markdown_description,
406
+ status: task.status,
407
+ priority: toTaskPriority(task.priority),
408
+ tags: task.tags,
409
+ custom_fields: task.custom_fields
410
+ };
411
+ // Add due date if specified
400
412
  if (task.dueDate) {
401
- processedTask.due_date = parseDueDate(task.dueDate);
402
- delete processedTask.dueDate;
413
+ taskData.due_date = parseDueDate(task.dueDate);
414
+ taskData.due_date_time = true;
403
415
  }
404
- // Make sure custom_fields is preserved in the processed task
405
- if (task.custom_fields) {
406
- processedTask.custom_fields = task.custom_fields;
416
+ // Add start date if specified
417
+ if (task.startDate) {
418
+ taskData.start_date = parseDueDate(task.startDate);
419
+ taskData.start_date_time = true;
407
420
  }
408
- return processedTask;
421
+ return taskData;
409
422
  });
410
- const result = await bulkService.createTasks(listId, tasks, parseBulkOptions(params.options));
411
- return result.successful;
423
+ // Parse bulk options
424
+ const bulkOptions = parseBulkOptions(options);
425
+ // Create tasks - pass arguments in correct order: listId, tasks, options
426
+ return await bulkService.createTasks(targetListId, formattedTasks, bulkOptions);
412
427
  }
413
428
  /**
414
429
  * Handler for updating multiple tasks
415
430
  */
416
431
  export async function updateBulkTasksHandler(params) {
417
- validateBulkTasks(params.tasks);
418
- const updates = await Promise.all(params.tasks.map(async (task) => {
419
- validateTaskUpdateData(task);
420
- const taskId = await getTaskId(task.taskId, task.taskName, task.listName);
421
- return { id: taskId, data: buildUpdateData(task) };
422
- }));
423
- const result = await bulkService.updateTasks(updates, parseBulkOptions(params.options));
424
- return result.successful;
432
+ const { tasks, options } = params;
433
+ // Validate tasks array
434
+ validateBulkTasks(tasks, 'update');
435
+ // Parse bulk options
436
+ const bulkOptions = parseBulkOptions(options);
437
+ // Update tasks
438
+ return await bulkService.updateTasks(tasks, bulkOptions);
425
439
  }
426
440
  /**
427
441
  * Handler for moving multiple tasks
428
442
  */
429
443
  export async function moveBulkTasksHandler(params) {
430
- validateBulkTasks(params.tasks);
431
- if (!params.targetListId && !params.targetListName) {
432
- throw new Error("Either targetListId or targetListName must be provided");
433
- }
434
- const targetListId = await getListId(params.targetListId, params.targetListName);
435
- const taskIds = await mapTaskIds(params.tasks);
436
- const result = await bulkService.moveTasks(taskIds.map(taskId => ({ taskId })), targetListId, parseBulkOptions(params.options));
437
- return result.successful;
444
+ const { tasks, targetListId, targetListName, options } = params;
445
+ // Validate tasks array
446
+ validateBulkTasks(tasks, 'move');
447
+ // Validate and resolve target list ID
448
+ const resolvedTargetListId = await resolveListIdWithValidation(targetListId, targetListName);
449
+ // Parse bulk options
450
+ const bulkOptions = parseBulkOptions(options);
451
+ // Move tasks
452
+ return await bulkService.moveTasks(tasks, resolvedTargetListId, bulkOptions);
438
453
  }
439
454
  /**
440
455
  * Handler for deleting multiple tasks
441
456
  */
442
457
  export async function deleteBulkTasksHandler(params) {
443
- validateBulkTasks(params.tasks);
444
- const taskIds = await mapTaskIds(params.tasks);
445
- await bulkService.deleteTasks(taskIds, parseBulkOptions(params.options));
446
- return taskIds.map(() => true);
458
+ const { tasks, options } = params;
459
+ // Validate tasks array
460
+ validateBulkTasks(tasks, 'delete');
461
+ // Parse bulk options
462
+ const bulkOptions = parseBulkOptions(options);
463
+ // Delete tasks
464
+ return await bulkService.deleteTasks(tasks, bulkOptions);
447
465
  }
448
466
  /**
449
467
  * Handler for deleting a task
@@ -100,22 +100,37 @@ export const handleCreateTaskComment = createHandlerWrapper(createTaskCommentHan
100
100
  //=============================================================================
101
101
  // BULK TASK OPERATIONS - HANDLER IMPLEMENTATIONS
102
102
  //=============================================================================
103
- export const handleCreateBulkTasks = createHandlerWrapper(createBulkTasksHandler, (tasks) => ({
104
- tasks,
105
- count: tasks.length
103
+ export const handleCreateBulkTasks = createHandlerWrapper(createBulkTasksHandler, (result) => ({
104
+ successful: result.successful,
105
+ failed: result.failed,
106
+ count: result.totals.total,
107
+ success_count: result.totals.success,
108
+ failure_count: result.totals.failure,
109
+ errors: result.failed.map(f => f.error)
106
110
  }));
107
- export const handleUpdateBulkTasks = createHandlerWrapper(updateBulkTasksHandler, (tasks) => ({
108
- tasks,
109
- count: tasks.length
111
+ export const handleUpdateBulkTasks = createHandlerWrapper(updateBulkTasksHandler, (result) => ({
112
+ successful: result.successful,
113
+ failed: result.failed,
114
+ count: result.totals.total,
115
+ success_count: result.totals.success,
116
+ failure_count: result.totals.failure,
117
+ errors: result.failed.map(f => f.error)
110
118
  }));
111
- export const handleMoveBulkTasks = createHandlerWrapper(moveBulkTasksHandler, (tasks) => ({
112
- tasks,
113
- count: tasks.length
119
+ export const handleMoveBulkTasks = createHandlerWrapper(moveBulkTasksHandler, (result) => ({
120
+ successful: result.successful,
121
+ failed: result.failed,
122
+ count: result.totals.total,
123
+ success_count: result.totals.success,
124
+ failure_count: result.totals.failure,
125
+ errors: result.failed.map(f => f.error)
114
126
  }));
115
- export const handleDeleteBulkTasks = createHandlerWrapper(deleteBulkTasksHandler, (results) => ({
116
- success: true,
117
- count: results.length,
118
- results
127
+ export const handleDeleteBulkTasks = createHandlerWrapper(deleteBulkTasksHandler, (result) => ({
128
+ successful: result.successful,
129
+ failed: result.failed,
130
+ count: result.totals.total,
131
+ success_count: result.totals.success,
132
+ failure_count: result.totals.failure,
133
+ errors: result.failed.map(f => f.error)
119
134
  }));
120
135
  //=============================================================================
121
136
  // WORKSPACE TASK OPERATIONS - HANDLER IMPLEMENTATIONS
@@ -128,22 +143,101 @@ export const handleGetWorkspaceTasks = createHandlerWrapper(
128
143
  // TOOL DEFINITIONS AND HANDLERS EXPORT
129
144
  //=============================================================================
130
145
  // Tool definitions with their handler mappings
131
- export const taskTools = [
132
- // Single task operations
133
- { definition: createTaskTool, handler: handleCreateTask },
134
- { definition: getTaskTool, handler: handleGetTask },
135
- { definition: getTasksTool, handler: handleGetTasks },
136
- { definition: updateTaskTool, handler: handleUpdateTask },
137
- { definition: moveTaskTool, handler: handleMoveTask },
138
- { definition: duplicateTaskTool, handler: handleDuplicateTask },
139
- { definition: deleteTaskTool, handler: handleDeleteTask },
140
- { definition: getTaskCommentsTool, handler: handleGetTaskComments },
141
- { definition: createTaskCommentTool, handler: handleCreateTaskComment },
142
- // Bulk task operations
143
- { definition: createBulkTasksTool, handler: handleCreateBulkTasks },
144
- { definition: updateBulkTasksTool, handler: handleUpdateBulkTasks },
145
- { definition: moveBulkTasksTool, handler: handleMoveBulkTasks },
146
- { definition: deleteBulkTasksTool, handler: handleDeleteBulkTasks },
147
- // Team task operations
148
- { definition: getWorkspaceTasksTool, handler: handleGetWorkspaceTasks }
146
+ export const tools = [
147
+ {
148
+ definition: createTaskTool,
149
+ handler: createTaskHandler
150
+ },
151
+ {
152
+ definition: updateTaskTool,
153
+ handler: updateTaskHandler
154
+ },
155
+ {
156
+ definition: moveTaskTool,
157
+ handler: moveTaskHandler
158
+ },
159
+ {
160
+ definition: duplicateTaskTool,
161
+ handler: duplicateTaskHandler
162
+ },
163
+ {
164
+ definition: getTaskTool,
165
+ handler: getTaskHandler
166
+ },
167
+ {
168
+ definition: getTasksTool,
169
+ handler: getTasksHandler
170
+ },
171
+ {
172
+ definition: getTaskCommentsTool,
173
+ handler: getTaskCommentsHandler
174
+ },
175
+ {
176
+ definition: createTaskCommentTool,
177
+ handler: createTaskCommentHandler
178
+ },
179
+ {
180
+ definition: deleteTaskTool,
181
+ handler: deleteTaskHandler
182
+ },
183
+ {
184
+ definition: getWorkspaceTasksTool,
185
+ handler: getWorkspaceTasksHandler
186
+ },
187
+ {
188
+ definition: createBulkTasksTool,
189
+ handler: async (params) => {
190
+ const result = await createBulkTasksHandler(params);
191
+ return {
192
+ successful: result.successful,
193
+ failed: result.failed,
194
+ count: result.totals.total,
195
+ success_count: result.totals.success,
196
+ failure_count: result.totals.failure,
197
+ errors: result.failed.map(f => f.error)
198
+ };
199
+ }
200
+ },
201
+ {
202
+ definition: updateBulkTasksTool,
203
+ handler: async (params) => {
204
+ const result = await updateBulkTasksHandler(params);
205
+ return {
206
+ successful: result.successful,
207
+ failed: result.failed,
208
+ count: result.totals.total,
209
+ success_count: result.totals.success,
210
+ failure_count: result.totals.failure,
211
+ errors: result.failed.map(f => f.error)
212
+ };
213
+ }
214
+ },
215
+ {
216
+ definition: moveBulkTasksTool,
217
+ handler: async (params) => {
218
+ const result = await moveBulkTasksHandler(params);
219
+ return {
220
+ successful: result.successful,
221
+ failed: result.failed,
222
+ count: result.totals.total,
223
+ success_count: result.totals.success,
224
+ failure_count: result.totals.failure,
225
+ errors: result.failed.map(f => f.error)
226
+ };
227
+ }
228
+ },
229
+ {
230
+ definition: deleteBulkTasksTool,
231
+ handler: async (params) => {
232
+ const result = await deleteBulkTasksHandler(params);
233
+ return {
234
+ successful: result.successful,
235
+ failed: result.failed,
236
+ count: result.totals.total,
237
+ success_count: result.totals.success,
238
+ failure_count: result.totals.failure,
239
+ errors: result.failed.map(f => f.error)
240
+ };
241
+ }
242
+ }
149
243
  ];