@taazkareem/clickup-mcp-server 0.4.53 → 0.4.56

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/Dockerfile ADDED
@@ -0,0 +1,38 @@
1
+ # Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile
2
+ # Use a Node.js base image
3
+ FROM node:18-alpine AS builder
4
+
5
+ # Set the working directory
6
+ WORKDIR /app
7
+
8
+ # Copy package files
9
+ COPY package.json package-lock.json ./
10
+
11
+ # Copy the source files and tsconfig BEFORE npm install
12
+ COPY src ./src
13
+ COPY tsconfig.json ./
14
+
15
+ # Install dependencies
16
+ RUN npm install
17
+
18
+ # Compile TypeScript to JavaScript
19
+ RUN npm run build
20
+
21
+ # Use a smaller image for the runtime
22
+ FROM node:18-alpine AS runtime
23
+
24
+ # Set the working directory
25
+ WORKDIR /app
26
+
27
+ # Copy the build output and node_modules from the builder stage
28
+ COPY --from=builder /app/build ./build
29
+ COPY --from=builder /app/node_modules ./node_modules
30
+
31
+ # Copy the entrypoint script if necessary
32
+ COPY --from=builder /app/package.json ./
33
+
34
+ # Expose the desired port (if the server binds to a port)
35
+ EXPOSE 8080
36
+
37
+ # Define the command to run the application
38
+ CMD ["node", "build/index.js"]
package/README.md CHANGED
@@ -21,6 +21,9 @@ npx -y @taazkareem/clickup-mcp-server \
21
21
  4. Replace the credentials and click Save
22
22
  5. Use Natural Language to interact with your ClickUp Workspace!
23
23
 
24
+ ## Smithery
25
+ [![smithery badge](https://smithery.ai/badge/@TaazKareem/clickup-mcp-server)](https://smithery.ai/server/@TaazKareem/clickup-mcp-server)
26
+
24
27
  ## Features
25
28
 
26
29
  - 🎯 **Task Management**
@@ -32,9 +35,10 @@ npx -y @taazkareem/clickup-mcp-server \
32
35
  - 📂 **Workspace Organization**
33
36
  - Complete workspace hierarchy (spaces, folders, lists)
34
37
  - Tree structure with clear relationships
35
- - Create and manage lists and folders
38
+ - Full CRUD operations for workspace components
36
39
  - Efficient path-based navigation
37
40
 
41
+
38
42
  - 🔄 **Integration Features**
39
43
  - Name or ID-based item lookup
40
44
  - Case-insensitive name matching
@@ -63,6 +67,12 @@ npx -y @taazkareem/clickup-mcp-server \
63
67
  | [create_list](docs/api-reference.md#list-management) | Create list in space | `name`, `spaceId`/`spaceName` |
64
68
  | [create_folder](docs/api-reference.md#folder-management) | Create folder | `name`, `spaceId`/`spaceName` |
65
69
  | [create_list_in_folder](docs/api-reference.md#list-management) | Create list in folder | `name`, `folderId`/`folderName` |
70
+ | [get_folder](docs/api-reference.md#folder-management) | Get folder details | `folderId`/`folderName` |
71
+ | [update_folder](docs/api-reference.md#folder-management) | Update folder properties | `folderId`/`folderName` |
72
+ | [delete_folder](docs/api-reference.md#folder-management) | Delete folder | `folderId`/`folderName` |
73
+ | [get_list](docs/api-reference.md#list-management) | Get list details | `listId`/`listName` |
74
+ | [update_list](docs/api-reference.md#list-management) | Update list properties | `listId`/`listName` |
75
+ | [delete_list](docs/api-reference.md#list-management) | Delete list | `listId`/`listName` |
66
76
 
67
77
  See [full documentation](docs/api-reference.md) for optional parameters and advanced usage.
68
78
 
package/build/index.js CHANGED
@@ -72,17 +72,17 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
72
72
  },
73
73
  {
74
74
  name: "create_task",
75
- description: "Create a single task in a ClickUp list. Use this tool for individual task creation only. For multiple tasks, use create_bulk_tasks instead. The tool finds lists by name (case-insensitive) so explicit list IDs aren't required. When creating a task, you must provide either a listId or listName.",
75
+ description: "Create a single task in a ClickUp list. Use this tool for individual task creation only. For multiple tasks, use create_bulk_tasks instead. Before calling this tool, check if you already have the necessary list ID from previous responses in the conversation history, as this avoids redundant lookups. When creating a task, you must provide either a listId or listName.",
76
76
  inputSchema: {
77
77
  type: "object",
78
78
  properties: {
79
79
  listId: {
80
80
  type: "string",
81
- description: "ID of the list to create the task in (optional if using listName instead)"
81
+ description: "ID of the list to create the task in (optional if using listName instead). If you have this ID from a previous response, use it directly rather than looking up by name."
82
82
  },
83
83
  listName: {
84
84
  type: "string",
85
- description: "Name of the list to create the task in - will automatically find the list by name (optional if using listId instead)"
85
+ description: "Name of the list to create the task in - will automatically find the list by name (optional if using listId instead). Only use this if you don't already have the list ID from previous responses."
86
86
  },
87
87
  name: {
88
88
  type: "string",
@@ -114,17 +114,17 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
114
114
  },
115
115
  {
116
116
  name: "create_bulk_tasks",
117
- description: "Create multiple tasks in a ClickUp list simultaneously. Use this tool when you need to add several related tasks in one operation. The tool finds lists by name (case-insensitive), so explicit list IDs aren't required. More efficient than creating tasks one by one for batch operations.",
117
+ description: "Create multiple tasks in a ClickUp list simultaneously. Use this tool when you need to add several related tasks in one operation. Before calling, check if you already have the necessary list ID from previous responses in the conversation, as this avoids redundant lookups. More efficient than creating tasks one by one for batch operations.",
118
118
  inputSchema: {
119
119
  type: "object",
120
120
  properties: {
121
121
  listId: {
122
122
  type: "string",
123
- description: "ID of the list to create the tasks in (optional if using listName instead)"
123
+ description: "ID of the list to create the tasks in (optional if using listName instead). If you have this ID from a previous response, use it directly rather than looking up by name."
124
124
  },
125
125
  listName: {
126
126
  type: "string",
127
- description: "Name of the list to create the tasks in - will automatically find the list by name (optional if using listId instead)"
127
+ description: "Name of the list to create the tasks in - will automatically find the list by name (optional if using listId instead). Only use this if you don't already have the list ID from previous responses."
128
128
  },
129
129
  tasks: {
130
130
  type: "array",
@@ -173,17 +173,17 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
173
173
  },
174
174
  {
175
175
  name: "create_list",
176
- description: "Create a new list directly in a ClickUp space. Use this tool when you need a top-level list not nested inside a folder. The tool can find spaces by name, so explicit space IDs aren't required. For creating lists inside folders, use create_list_in_folder instead.",
176
+ description: "Create a new list directly in a ClickUp space. Use this tool when you need a top-level list not nested inside a folder. Before calling, check if you already have the necessary space ID from previous responses in the conversation, as this avoids redundant lookups. For creating lists inside folders, use create_list_in_folder instead.",
177
177
  inputSchema: {
178
178
  type: "object",
179
179
  properties: {
180
180
  spaceId: {
181
181
  type: "string",
182
- description: "ID of the space to create the list in (optional if using spaceName instead)"
182
+ description: "ID of the space to create the list in (optional if using spaceName instead). If you have this ID from a previous response, use it directly rather than looking up by name."
183
183
  },
184
184
  spaceName: {
185
185
  type: "string",
186
- description: "Name of the space to create the list in - will automatically find the space by name (optional if using spaceId instead)"
186
+ description: "Name of the space to create the list in - will automatically find the space by name (optional if using spaceId instead). Only use this if you don't already have the space ID from previous responses."
187
187
  },
188
188
  name: {
189
189
  type: "string",
@@ -215,17 +215,17 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
215
215
  },
216
216
  {
217
217
  name: "create_folder",
218
- description: "Create a new folder in a ClickUp space for organizing related lists. Use this tool when you need to group multiple lists together. The tool can find spaces by name, so explicit space IDs aren't required. After creating a folder, you can add lists to it using create_list_in_folder.",
218
+ description: "Create a new folder in a ClickUp space for organizing related lists. Use this tool when you need to group multiple lists together. Before calling, check if you already have the necessary space ID from previous responses in the conversation, as this avoids redundant lookups. After creating a folder, you can add lists to it using create_list_in_folder.",
219
219
  inputSchema: {
220
220
  type: "object",
221
221
  properties: {
222
222
  spaceId: {
223
223
  type: "string",
224
- description: "ID of the space to create the folder in (optional if using spaceName instead)"
224
+ description: "ID of the space to create the folder in (optional if using spaceName instead). If you have this ID from a previous response, use it directly rather than looking up by name."
225
225
  },
226
226
  spaceName: {
227
227
  type: "string",
228
- description: "Name of the space to create the folder in - will automatically find the space by name (optional if using spaceId instead)"
228
+ description: "Name of the space to create the folder in - will automatically find the space by name (optional if using spaceId instead). Only use this if you don't already have the space ID from previous responses."
229
229
  },
230
230
  name: {
231
231
  type: "string",
@@ -241,25 +241,25 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
241
241
  },
242
242
  {
243
243
  name: "create_list_in_folder",
244
- description: "Create a new list within a ClickUp folder. Use this tool when you need to add a list to an existing folder structure. The tool can find folders and spaces by name, so explicit IDs aren't required. For top-level lists not in folders, use create_list instead.",
244
+ description: "Create a new list within a ClickUp folder. Use this tool when you need to add a list to an existing folder structure. Before calling, check if you already have the necessary folder ID and space ID from previous responses in the conversation, as this avoids redundant lookups. For top-level lists not in folders, use create_list instead.",
245
245
  inputSchema: {
246
246
  type: "object",
247
247
  properties: {
248
248
  folderId: {
249
249
  type: "string",
250
- description: "ID of the folder to create the list in (optional if using folderName instead)"
250
+ description: "ID of the folder to create the list in (optional if using folderName instead). If you have this ID from a previous response, use it directly rather than looking up by name."
251
251
  },
252
252
  folderName: {
253
253
  type: "string",
254
- description: "Name of the folder to create the list in - will automatically find the folder by name (optional if using folderId instead)"
254
+ description: "Name of the folder to create the list in - will automatically find the folder by name (optional if using folderId instead). Only use this if you don't already have the folder ID from previous responses."
255
255
  },
256
256
  spaceId: {
257
257
  type: "string",
258
- description: "ID of the space containing the folder (optional if using spaceName instead)"
258
+ description: "ID of the space containing the folder (optional if using spaceName instead). If you have this ID from a previous response, use it directly rather than looking up by name."
259
259
  },
260
260
  spaceName: {
261
261
  type: "string",
262
- description: "Name of the space containing the folder - will automatically find the space by name (optional if using spaceId instead)"
262
+ description: "Name of the space containing the folder - will automatically find the space by name (optional if using spaceId instead). Only use this if you don't already have the space ID from previous responses."
263
263
  },
264
264
  name: {
265
265
  type: "string",
@@ -279,17 +279,17 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
279
279
  },
280
280
  {
281
281
  name: "move_task",
282
- description: "Move an existing task from its current list to a different list. Use this tool when you need to relocate a task within your workspace hierarchy. The tool can find tasks and lists by name, so explicit IDs aren't required. Task statuses may be reset if the destination list uses different status options.",
282
+ description: "Move an existing task from its current list to a different list. Use this tool when you need to relocate a task within your workspace hierarchy. Before calling, check if you already have the necessary task ID and list ID from previous responses in the conversation, as this avoids redundant lookups. Task statuses may be reset if the destination list uses different status options.",
283
283
  inputSchema: {
284
284
  type: "object",
285
285
  properties: {
286
286
  taskId: {
287
287
  type: "string",
288
- description: "ID of the task to move (optional if using taskName instead)"
288
+ description: "ID of the task to move (optional if using taskName instead). If you have this ID from a previous response, use it directly rather than looking up by name."
289
289
  },
290
290
  taskName: {
291
291
  type: "string",
292
- description: "Name of the task to move - will automatically find the task by name (optional if using taskId instead)"
292
+ description: "Name of the task to move - will automatically find the task by name (optional if using taskId instead). Only use this if you don't already have the task ID from previous responses."
293
293
  },
294
294
  sourceListName: {
295
295
  type: "string",
@@ -297,11 +297,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
297
297
  },
298
298
  listId: {
299
299
  type: "string",
300
- description: "ID of the destination list (optional if using listName instead)"
300
+ description: "ID of the destination list (optional if using listName instead). If you have this ID from a previous response, use it directly rather than looking up by name."
301
301
  },
302
302
  listName: {
303
303
  type: "string",
304
- description: "Name of the destination list - will automatically find the list by name (optional if using listId instead)"
304
+ description: "Name of the destination list - will automatically find the list by name (optional if using listId instead). Only use this if you don't already have the list ID from previous responses."
305
305
  }
306
306
  },
307
307
  required: ["taskName", "listName"]
@@ -309,17 +309,17 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
309
309
  },
310
310
  {
311
311
  name: "duplicate_task",
312
- description: "Create a copy of an existing task in the same or different list. Use this tool when you need to replicate a task's content and properties. The tool can find tasks and lists by name, so explicit IDs aren't required. The duplicate will preserve name, description, priority, and other attributes from the original task.",
312
+ description: "Create a copy of an existing task in the same or different list. Use this tool when you need to replicate a task's content and properties. Before calling, check if you already have the necessary task ID and list ID from previous responses in the conversation, as this avoids redundant lookups. The duplicate will preserve name, description, priority, and other attributes from the original task.",
313
313
  inputSchema: {
314
314
  type: "object",
315
315
  properties: {
316
316
  taskId: {
317
317
  type: "string",
318
- description: "ID of the task to duplicate (optional if using taskName instead)"
318
+ description: "ID of the task to duplicate (optional if using taskName instead). If you have this ID from a previous response, use it directly rather than looking up by name."
319
319
  },
320
320
  taskName: {
321
321
  type: "string",
322
- description: "Name of the task to duplicate - will automatically find the task by name (optional if using taskId instead)"
322
+ description: "Name of the task to duplicate - will automatically find the task by name (optional if using taskId instead). Only use this if you don't already have the task ID from previous responses."
323
323
  },
324
324
  sourceListName: {
325
325
  type: "string",
@@ -327,11 +327,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
327
327
  },
328
328
  listId: {
329
329
  type: "string",
330
- description: "ID of the list to create the duplicate in (optional if using listName instead)"
330
+ description: "ID of the list to create the duplicate in (optional if using listName instead). If you have this ID from a previous response, use it directly rather than looking up by name."
331
331
  },
332
332
  listName: {
333
333
  type: "string",
334
- description: "Name of the list to create the duplicate in - will automatically find the list by name (optional if using listId instead)"
334
+ description: "Name of the list to create the duplicate in - will automatically find the list by name (optional if using listId instead). Only use this if you don't already have the list ID from previous responses."
335
335
  }
336
336
  },
337
337
  required: ["taskName", "listName"]
@@ -339,17 +339,17 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
339
339
  },
340
340
  {
341
341
  name: "update_task",
342
- description: "Modify the properties of an existing task. Use this tool when you need to change a task's name, description, status, priority, or due date. The tool can find tasks by name, so explicit task IDs aren't required. Only the fields you specify will be updated; other fields will remain unchanged.",
342
+ description: "Modify the properties of an existing task. Use this tool when you need to change a task's name, description, status, priority, or due date. Before calling, check if you already have the necessary task ID from previous responses in the conversation, as this avoids redundant lookups. Only the fields you specify will be updated; other fields will remain unchanged.",
343
343
  inputSchema: {
344
344
  type: "object",
345
345
  properties: {
346
346
  taskId: {
347
347
  type: "string",
348
- description: "ID of the task to update (optional if using taskName instead)"
348
+ description: "ID of the task to update (optional if using taskName instead). If you have this ID from a previous response, use it directly rather than looking up by name."
349
349
  },
350
350
  taskName: {
351
351
  type: "string",
352
- description: "Name of the task to update - will automatically find the task by name (optional if using taskId instead)"
352
+ description: "Name of the task to update - will automatically find the task by name (optional if using taskId instead). Only use this if you don't already have the task ID from previous responses."
353
353
  },
354
354
  listName: {
355
355
  type: "string",
@@ -381,17 +381,17 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
381
381
  },
382
382
  {
383
383
  name: "get_tasks",
384
- description: "Retrieve tasks from a ClickUp list with optional filtering capabilities. Use this tool when you need to see existing tasks or analyze your current workload. The tool can find lists by name, eliminating the need for explicit list IDs. Results can be filtered by status, assignees, dates, and more.",
384
+ description: "Retrieve tasks from a ClickUp list with optional filtering capabilities. Use this tool when you need to see existing tasks or analyze your current workload. Before calling, check if you already have the necessary list ID from previous responses in the conversation, as this avoids redundant lookups. Results can be filtered by status, assignees, dates, and more.",
385
385
  inputSchema: {
386
386
  type: "object",
387
387
  properties: {
388
388
  listId: {
389
389
  type: "string",
390
- description: "ID of the list to get tasks from (optional if using listName instead)"
390
+ description: "ID of the list to get tasks from (optional if using listName instead). If you have this ID from a previous response, use it directly rather than looking up by name."
391
391
  },
392
392
  listName: {
393
393
  type: "string",
394
- description: "Name of the list to get tasks from - will automatically find the list by name (optional if using listId instead)"
394
+ description: "Name of the list to get tasks from - will automatically find the list by name (optional if using listId instead). Only use this if you don't already have the list ID from previous responses."
395
395
  },
396
396
  archived: {
397
397
  type: "boolean",
@@ -461,17 +461,17 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
461
461
  },
462
462
  {
463
463
  name: "get_task",
464
- description: "Retrieve comprehensive details about a specific ClickUp task. Use this tool when you need in-depth information about a particular task, including its description, custom fields, attachments, and other metadata. The tool can find tasks by name, eliminating the need for explicit task IDs.",
464
+ description: "Retrieve comprehensive details about a specific ClickUp task. Use this tool when you need in-depth information about a particular task, including its description, custom fields, attachments, and other metadata. Before calling, check if you already have the necessary task ID from previous responses in the conversation, as this avoids redundant lookups.",
465
465
  inputSchema: {
466
466
  type: "object",
467
467
  properties: {
468
468
  taskId: {
469
469
  type: "string",
470
- description: "ID of the task to retrieve (optional if using taskName instead)"
470
+ description: "ID of the task to retrieve (optional if using taskName instead). If you have this ID from a previous response, use it directly rather than looking up by name."
471
471
  },
472
472
  taskName: {
473
473
  type: "string",
474
- description: "Name of the task to retrieve - will automatically find the task by name (optional if using taskId instead)"
474
+ description: "Name of the task to retrieve - will automatically find the task by name (optional if using taskId instead). Only use this if you don't already have the task ID from previous responses."
475
475
  },
476
476
  listName: {
477
477
  type: "string",
@@ -483,17 +483,17 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
483
483
  },
484
484
  {
485
485
  name: "delete_task",
486
- description: "Permanently remove a task from your ClickUp workspace. Use this tool with caution as deletion cannot be undone. The tool requires an explicit task ID for safety reasons, which you can obtain by first using get_task or get_tasks to find the appropriate task ID.",
486
+ description: "Permanently remove a task from your ClickUp workspace. Use this tool with caution as deletion cannot be undone. Before calling, check if you already have the necessary task ID from previous responses in the conversation, as this avoids redundant lookups. For safety, the task ID is required.",
487
487
  inputSchema: {
488
488
  type: "object",
489
489
  properties: {
490
490
  taskId: {
491
491
  type: "string",
492
- description: "ID of the task to delete - this is required for safety to prevent accidental deletions"
492
+ description: "ID of the task to delete - this is required for safety to prevent accidental deletions. If you have this ID from a previous response, use it directly."
493
493
  },
494
494
  taskName: {
495
495
  type: "string",
496
- description: "Name of the task to delete - will automatically find the task by name (optional if using taskId instead)"
496
+ description: "Name of the task to delete - will automatically find the task by name (optional if using taskId instead). Only use this if you don't already have the task ID from previous responses."
497
497
  },
498
498
  listName: {
499
499
  type: "string",
@@ -502,6 +502,158 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
502
502
  },
503
503
  required: ["taskId"]
504
504
  }
505
+ },
506
+ {
507
+ name: "get_folder",
508
+ description: "Retrieve details about a specific ClickUp folder including its name, status, and other metadata. Before calling, check if you already have the necessary folder ID from previous responses in the conversation history, as this avoids redundant lookups. Helps you understand folder structure before creating or updating lists.",
509
+ inputSchema: {
510
+ type: "object",
511
+ properties: {
512
+ folderId: {
513
+ type: "string",
514
+ description: "ID of the folder to retrieve (optional if using folderName instead). If you have this ID from a previous response, use it directly rather than looking up by name."
515
+ },
516
+ folderName: {
517
+ type: "string",
518
+ description: "Name of the folder to retrieve - will automatically find the folder by name (optional if using folderId instead). Only use this if you don't already have the folder ID from previous responses."
519
+ },
520
+ spaceId: {
521
+ type: "string",
522
+ description: "ID of the space containing the folder (optional if using spaceName instead, and only needed when using folderName). If you have this ID from a previous response, use it directly rather than looking up by name."
523
+ },
524
+ spaceName: {
525
+ type: "string",
526
+ description: "Name of the space containing the folder (optional if using spaceId instead, and only needed when using folderName). Only use this if you don't already have the space ID from previous responses."
527
+ }
528
+ },
529
+ required: []
530
+ }
531
+ },
532
+ {
533
+ name: "update_folder",
534
+ description: "Modify an existing ClickUp folder's properties, such as name or status settings. Before calling, check if you already have the necessary folder ID from previous responses in the conversation history, as this avoids redundant lookups. Use when reorganizing or renaming workspace elements.",
535
+ inputSchema: {
536
+ type: "object",
537
+ properties: {
538
+ folderId: {
539
+ type: "string",
540
+ description: "ID of the folder to update (optional if using folderName instead). If you have this ID from a previous response, use it directly rather than looking up by name."
541
+ },
542
+ folderName: {
543
+ type: "string",
544
+ description: "Name of the folder to update - will automatically find the folder by name (optional if using folderId instead). Only use this if you don't already have the folder ID from previous responses."
545
+ },
546
+ spaceId: {
547
+ type: "string",
548
+ description: "ID of the space containing the folder (optional if using spaceName instead, and only needed when using folderName). If you have this ID from a previous response, use it directly rather than looking up by name."
549
+ },
550
+ spaceName: {
551
+ type: "string",
552
+ description: "Name of the space containing the folder (optional if using spaceId instead, and only needed when using folderName). Only use this if you don't already have the space ID from previous responses."
553
+ },
554
+ name: {
555
+ type: "string",
556
+ description: "New name for the folder"
557
+ },
558
+ override_statuses: {
559
+ type: "boolean",
560
+ description: "Whether to override space statuses with folder-specific statuses"
561
+ }
562
+ },
563
+ required: []
564
+ }
565
+ },
566
+ {
567
+ name: "delete_folder",
568
+ description: "Permanently remove a folder from your ClickUp workspace. Use with caution as deletion cannot be undone and will remove all lists and tasks within the folder. Before calling, check if you already have the necessary folder ID from previous responses in the conversation history, as this avoids redundant lookups.",
569
+ inputSchema: {
570
+ type: "object",
571
+ properties: {
572
+ folderId: {
573
+ type: "string",
574
+ description: "ID of the folder to delete (optional if using folderName instead). If you have this ID from a previous response, use it directly rather than looking up by name."
575
+ },
576
+ folderName: {
577
+ type: "string",
578
+ description: "Name of the folder to delete - will automatically find the folder by name (optional if using folderId instead). Only use this if you don't already have the folder ID from previous responses."
579
+ },
580
+ spaceId: {
581
+ type: "string",
582
+ description: "ID of the space containing the folder (optional if using spaceName instead, and only needed when using folderName). If you have this ID from a previous response, use it directly rather than looking up by name."
583
+ },
584
+ spaceName: {
585
+ type: "string",
586
+ description: "Name of the space containing the folder (optional if using spaceId instead, and only needed when using folderName). Only use this if you don't already have the space ID from previous responses."
587
+ }
588
+ },
589
+ required: []
590
+ }
591
+ },
592
+ {
593
+ name: "get_list",
594
+ description: "Retrieve details about a specific ClickUp list including its name, content, status options, and other metadata. Before calling, check if you already have the necessary list ID from previous responses in the conversation history, as this avoids redundant lookups. Useful to understand list structure before creating or updating tasks.",
595
+ inputSchema: {
596
+ type: "object",
597
+ properties: {
598
+ listId: {
599
+ type: "string",
600
+ description: "ID of the list to retrieve (optional if using listName instead). If you have this ID from a previous response, use it directly rather than looking up by name."
601
+ },
602
+ listName: {
603
+ type: "string",
604
+ description: "Name of the list to retrieve - will automatically find the list by name (optional if using listId instead). Only use this if you don't already have the list ID from previous responses."
605
+ }
606
+ },
607
+ required: []
608
+ }
609
+ },
610
+ {
611
+ name: "update_list",
612
+ description: "Modify an existing ClickUp list's properties, such as name, content, or status options. Before calling, check if you already have the necessary list ID from previous responses in the conversation history, as this avoids redundant lookups. Use when reorganizing or renaming workspace elements.",
613
+ inputSchema: {
614
+ type: "object",
615
+ properties: {
616
+ listId: {
617
+ type: "string",
618
+ description: "ID of the list to update (optional if using listName instead). If you have this ID from a previous response, use it directly rather than looking up by name."
619
+ },
620
+ listName: {
621
+ type: "string",
622
+ description: "Name of the list to update - will automatically find the list by name (optional if using listId instead). Only use this if you don't already have the list ID from previous responses."
623
+ },
624
+ name: {
625
+ type: "string",
626
+ description: "New name for the list"
627
+ },
628
+ content: {
629
+ type: "string",
630
+ description: "New description or content for the list"
631
+ },
632
+ status: {
633
+ type: "string",
634
+ description: "New status for the list"
635
+ }
636
+ },
637
+ required: []
638
+ }
639
+ },
640
+ {
641
+ name: "delete_list",
642
+ description: "Permanently remove a list from your ClickUp workspace. Use with caution as deletion cannot be undone and will remove all tasks within the list. Before calling, check if you already have the necessary list ID from previous responses in the conversation history, as this avoids redundant lookups.",
643
+ inputSchema: {
644
+ type: "object",
645
+ properties: {
646
+ listId: {
647
+ type: "string",
648
+ description: "ID of the list to delete (optional if using listName instead). If you have this ID from a previous response, use it directly rather than looking up by name."
649
+ },
650
+ listName: {
651
+ type: "string",
652
+ description: "Name of the list to delete - will automatically find the list by name (optional if using listId instead). Only use this if you don't already have the list ID from previous responses."
653
+ }
654
+ },
655
+ required: []
656
+ }
505
657
  }
506
658
  ]
507
659
  };
@@ -841,6 +993,243 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
841
993
  }]
842
994
  };
843
995
  }
996
+ case "get_folder": {
997
+ const args = request.params.arguments;
998
+ if (!args.folderId && !args.folderName) {
999
+ throw new Error("Either folderId or folderName is required");
1000
+ }
1001
+ let folderId = args.folderId;
1002
+ if (!folderId && args.folderName) {
1003
+ // If we need to look up by name, we might need the space
1004
+ let spaceId = args.spaceId;
1005
+ if (!spaceId && args.spaceName) {
1006
+ const foundId = await clickup.findSpaceIDByName(args.spaceName);
1007
+ if (!foundId) {
1008
+ throw new Error(`Space with name "${args.spaceName}" not found`);
1009
+ }
1010
+ spaceId = foundId;
1011
+ }
1012
+ if (!spaceId) {
1013
+ // Try to find folder directly by name (will search across all spaces)
1014
+ const result = await clickup.findFolderIDByName(args.folderName);
1015
+ if (!result) {
1016
+ throw new Error(`Folder with name "${args.folderName}" not found`);
1017
+ }
1018
+ folderId = result.id;
1019
+ }
1020
+ else {
1021
+ // Look in a specific space
1022
+ const folder = await clickup.findFolderByName(spaceId, args.folderName);
1023
+ if (!folder) {
1024
+ throw new Error(`Folder with name "${args.folderName}" not found in specified space`);
1025
+ }
1026
+ folderId = folder.id;
1027
+ }
1028
+ }
1029
+ // Ensure folderId is defined at this point
1030
+ if (!folderId) {
1031
+ throw new Error("Failed to determine folder ID");
1032
+ }
1033
+ const folder = await clickup.getFolder(folderId);
1034
+ return {
1035
+ content: [{
1036
+ type: "text",
1037
+ text: JSON.stringify(folder, null, 2)
1038
+ }]
1039
+ };
1040
+ }
1041
+ case "update_folder": {
1042
+ const args = request.params.arguments;
1043
+ if (!args.folderId && !args.folderName) {
1044
+ throw new Error("Either folderId or folderName is required");
1045
+ }
1046
+ let folderId = args.folderId;
1047
+ if (!folderId && args.folderName) {
1048
+ // If we need to look up by name, we might need the space
1049
+ let spaceId = args.spaceId;
1050
+ if (!spaceId && args.spaceName) {
1051
+ const foundId = await clickup.findSpaceIDByName(args.spaceName);
1052
+ if (!foundId) {
1053
+ throw new Error(`Space with name "${args.spaceName}" not found`);
1054
+ }
1055
+ spaceId = foundId;
1056
+ }
1057
+ if (!spaceId) {
1058
+ // Try to find folder directly by name (will search across all spaces)
1059
+ const result = await clickup.findFolderIDByName(args.folderName);
1060
+ if (!result) {
1061
+ throw new Error(`Folder with name "${args.folderName}" not found`);
1062
+ }
1063
+ folderId = result.id;
1064
+ }
1065
+ else {
1066
+ // Look in a specific space
1067
+ const folder = await clickup.findFolderByName(spaceId, args.folderName);
1068
+ if (!folder) {
1069
+ throw new Error(`Folder with name "${args.folderName}" not found in specified space`);
1070
+ }
1071
+ folderId = folder.id;
1072
+ }
1073
+ }
1074
+ // Ensure folderId is defined at this point
1075
+ if (!folderId) {
1076
+ throw new Error("Failed to determine folder ID");
1077
+ }
1078
+ // Extract update data
1079
+ const { folderId: _, folderName: __, spaceId: ___, spaceName: ____, ...updateData } = args;
1080
+ // Call the updateFolder method
1081
+ const updatedFolder = await clickup.updateFolder(folderId, updateData);
1082
+ return {
1083
+ content: [{
1084
+ type: "text",
1085
+ text: `Updated folder ${updatedFolder.id}: ${updatedFolder.name}`
1086
+ }]
1087
+ };
1088
+ }
1089
+ case "delete_folder": {
1090
+ const args = request.params.arguments;
1091
+ if (!args.folderId && !args.folderName) {
1092
+ throw new Error("Either folderId or folderName is required");
1093
+ }
1094
+ let folderId = args.folderId;
1095
+ if (!folderId && args.folderName) {
1096
+ // If we need to look up by name, we might need the space
1097
+ let spaceId = args.spaceId;
1098
+ if (!spaceId && args.spaceName) {
1099
+ const foundId = await clickup.findSpaceIDByName(args.spaceName);
1100
+ if (!foundId) {
1101
+ throw new Error(`Space with name "${args.spaceName}" not found`);
1102
+ }
1103
+ spaceId = foundId;
1104
+ }
1105
+ if (!spaceId) {
1106
+ // Try to find folder directly by name (will search across all spaces)
1107
+ const result = await clickup.findFolderIDByName(args.folderName);
1108
+ if (!result) {
1109
+ throw new Error(`Folder with name "${args.folderName}" not found`);
1110
+ }
1111
+ folderId = result.id;
1112
+ }
1113
+ else {
1114
+ // Look in a specific space
1115
+ const folder = await clickup.findFolderByName(spaceId, args.folderName);
1116
+ if (!folder) {
1117
+ throw new Error(`Folder with name "${args.folderName}" not found in specified space`);
1118
+ }
1119
+ folderId = folder.id;
1120
+ }
1121
+ }
1122
+ // Ensure folderId is defined at this point
1123
+ if (!folderId) {
1124
+ throw new Error("Failed to determine folder ID");
1125
+ }
1126
+ // Store the folder name before deletion for the response message
1127
+ let folderName = args.folderName;
1128
+ if (!folderName) {
1129
+ try {
1130
+ const folderDetails = await clickup.getFolder(folderId);
1131
+ folderName = folderDetails.name;
1132
+ }
1133
+ catch (error) {
1134
+ // If we can't get the folder details, just use the ID in the response
1135
+ }
1136
+ }
1137
+ await clickup.deleteFolder(folderId);
1138
+ return {
1139
+ content: [{
1140
+ type: "text",
1141
+ text: `Successfully deleted folder ${folderName || folderId}`
1142
+ }]
1143
+ };
1144
+ }
1145
+ case "get_list": {
1146
+ const args = request.params.arguments;
1147
+ if (!args.listId && !args.listName) {
1148
+ throw new Error("Either listId or listName is required");
1149
+ }
1150
+ let listId = args.listId;
1151
+ if (!listId && args.listName) {
1152
+ const result = await clickup.findListIDByName(args.listName);
1153
+ if (!result) {
1154
+ throw new Error(`List with name "${args.listName}" not found`);
1155
+ }
1156
+ listId = result.id;
1157
+ }
1158
+ // Ensure listId is defined at this point
1159
+ if (!listId) {
1160
+ throw new Error("Failed to determine list ID");
1161
+ }
1162
+ const listDetails = await clickup.getList(listId);
1163
+ return {
1164
+ content: [{
1165
+ type: "text",
1166
+ text: JSON.stringify(listDetails, null, 2)
1167
+ }]
1168
+ };
1169
+ }
1170
+ case "update_list": {
1171
+ const args = request.params.arguments;
1172
+ if (!args.listId && !args.listName) {
1173
+ throw new Error("Either listId or listName is required");
1174
+ }
1175
+ let listId = args.listId;
1176
+ if (!listId && args.listName) {
1177
+ const result = await clickup.findListIDByName(args.listName);
1178
+ if (!result) {
1179
+ throw new Error(`List with name "${args.listName}" not found`);
1180
+ }
1181
+ listId = result.id;
1182
+ }
1183
+ // Ensure listId is defined at this point
1184
+ if (!listId) {
1185
+ throw new Error("Failed to determine list ID");
1186
+ }
1187
+ // Extract update data
1188
+ const { listId: _, listName: __, ...updateData } = args;
1189
+ const updatedList = await clickup.updateList(listId, updateData);
1190
+ return {
1191
+ content: [{
1192
+ type: "text",
1193
+ text: `Updated list ${updatedList.id}: ${updatedList.name}`
1194
+ }]
1195
+ };
1196
+ }
1197
+ case "delete_list": {
1198
+ const args = request.params.arguments;
1199
+ if (!args.listId && !args.listName) {
1200
+ throw new Error("Either listId or listName is required");
1201
+ }
1202
+ let listId = args.listId;
1203
+ if (!listId && args.listName) {
1204
+ const result = await clickup.findListIDByName(args.listName);
1205
+ if (!result) {
1206
+ throw new Error(`List with name "${args.listName}" not found`);
1207
+ }
1208
+ listId = result.id;
1209
+ }
1210
+ // Ensure listId is defined at this point
1211
+ if (!listId) {
1212
+ throw new Error("Failed to determine list ID");
1213
+ }
1214
+ // Store the list name before deletion for the response message
1215
+ let listName = args.listName;
1216
+ if (!listName) {
1217
+ try {
1218
+ const listDetails = await clickup.getList(listId);
1219
+ listName = listDetails.name;
1220
+ }
1221
+ catch (error) {
1222
+ // If we can't get the list details, just use the ID in the response
1223
+ }
1224
+ }
1225
+ await clickup.deleteList(listId);
1226
+ return {
1227
+ content: [{
1228
+ type: "text",
1229
+ text: `Successfully deleted list ${listName || listId}`
1230
+ }]
1231
+ };
1232
+ }
844
1233
  default:
845
1234
  throw new Error(`Unknown tool: ${request.params.name}`);
846
1235
  }
@@ -309,9 +309,16 @@ export class ClickUpService {
309
309
  return response.data;
310
310
  });
311
311
  }
312
- async createFolder(spaceId, data) {
312
+ /**
313
+ * Updates an existing folder with new data.
314
+ * @param folderId - ID of the folder to update
315
+ * @param data - Data to update the folder with (name, override_statuses)
316
+ * @returns Promise resolving to the updated ClickUpFolder
317
+ * @throws Error if the API request fails
318
+ */
319
+ async updateFolder(folderId, data) {
313
320
  return this.makeRequest(async () => {
314
- const response = await this.client.post(`/space/${spaceId}/folder`, data);
321
+ const response = await this.client.put(`/folder/${folderId}`, data);
315
322
  return response.data;
316
323
  });
317
324
  }
@@ -337,6 +344,19 @@ export class ClickUpService {
337
344
  const folders = await this.getFolders(spaceId);
338
345
  return folders.find(folder => folder.name.toLowerCase() === folderName.toLowerCase()) || null;
339
346
  }
347
+ /**
348
+ * Creates a new folder in a space.
349
+ * @param spaceId - ID of the space to create the folder in
350
+ * @param data - Folder creation data (name, override_statuses)
351
+ * @returns Promise resolving to the created ClickUpFolder
352
+ * @throws Error if the API request fails
353
+ */
354
+ async createFolder(spaceId, data) {
355
+ return this.makeRequest(async () => {
356
+ const response = await this.client.post(`/space/${spaceId}/folder`, data);
357
+ return response.data;
358
+ });
359
+ }
340
360
  // Additional helper methods
341
361
  /**
342
362
  * Moves a task to a different list.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taazkareem/clickup-mcp-server",
3
- "version": "0.4.53",
3
+ "version": "0.4.56",
4
4
  "description": "ClickUp MCP Server - Integrate ClickUp tasks with AI through Model Context Protocol",
5
5
  "type": "module",
6
6
  "main": "build/index.js",
@@ -11,7 +11,7 @@
11
11
  "build",
12
12
  "README.md",
13
13
  "LICENSE",
14
- "Dockerfile.smithery",
14
+ "Dockerfile",
15
15
  "smithery.yaml"
16
16
  ],
17
17
  "scripts": {
@@ -36,7 +36,7 @@
36
36
  "license": "MIT",
37
37
  "repository": {
38
38
  "type": "git",
39
- "url": "https://github.com/taazkareem/clickup-mcp-server.git"
39
+ "url": "git+https://github.com/taazkareem/clickup-mcp-server.git"
40
40
  },
41
41
  "bugs": {
42
42
  "url": "https://github.com/taazkareem/clickup-mcp-server/issues"
package/smithery.yaml CHANGED
@@ -1,6 +1,6 @@
1
1
  # Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml
2
2
 
3
- dockerfile: Dockerfile.smithery
3
+ dockerfile: Dockerfile
4
4
 
5
5
  startCommand:
6
6
  type: stdio