@taazkareem/clickup-mcp-server 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -9
- package/build/index.js +214 -141
- package/build/services/clickup.js +37 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -16,18 +16,24 @@ A Model Context Protocol (MCP) server for integrating ClickUp tasks with AI appl
|
|
|
16
16
|
- Flexible identification using IDs or names
|
|
17
17
|
|
|
18
18
|
- ✨ **Task Operations**
|
|
19
|
-
- Create
|
|
19
|
+
- Create single or bulk tasks
|
|
20
20
|
- Move tasks between lists
|
|
21
21
|
- Duplicate tasks
|
|
22
22
|
- Set priorities and due dates
|
|
23
23
|
- Assign team members
|
|
24
24
|
|
|
25
25
|
- 📊 **Information Retrieval**
|
|
26
|
-
- Get spaces and lists with
|
|
26
|
+
- Get complete hierarchy of spaces, folders, and lists with IDs
|
|
27
27
|
- List available statuses
|
|
28
28
|
- Find items by name (case-insensitive)
|
|
29
29
|
- View task relationships
|
|
30
30
|
|
|
31
|
+
- 🔍 **Smart Name Resolution**
|
|
32
|
+
- Use names instead of IDs for lists and folders
|
|
33
|
+
- Global search across all spaces
|
|
34
|
+
- Case-insensitive matching
|
|
35
|
+
- Automatic location of items
|
|
36
|
+
|
|
31
37
|
- 📝 **AI Integration**
|
|
32
38
|
- Generate task descriptions with AI
|
|
33
39
|
- Summarize tasks and analyze priorities
|
|
@@ -81,17 +87,20 @@ You can get these values from:
|
|
|
81
87
|
|
|
82
88
|
### Available Tools
|
|
83
89
|
|
|
84
|
-
1. **
|
|
85
|
-
- Lists
|
|
90
|
+
1. **workspace_hierarchy**
|
|
91
|
+
- Lists complete hierarchy of the ClickUp workspace
|
|
92
|
+
- Shows spaces, folders, and lists with their IDs
|
|
86
93
|
- Shows available statuses for each list
|
|
94
|
+
- Provides a tree view of your workspace organization
|
|
87
95
|
- No parameters required
|
|
88
96
|
|
|
89
97
|
2. **create_task**
|
|
90
98
|
- Creates a new task in ClickUp
|
|
91
99
|
- Required parameters:
|
|
92
|
-
- `listId`: ID of the list to create the task in
|
|
93
100
|
- `name`: Name of the task
|
|
94
101
|
- Optional parameters:
|
|
102
|
+
- `listId`: ID of the list (optional if listName provided)
|
|
103
|
+
- `listName`: Name of the list (optional if listId provided)
|
|
95
104
|
- `description`: Task description
|
|
96
105
|
- `status`: Task status
|
|
97
106
|
- `priority`: Priority level (1-4)
|
|
@@ -101,7 +110,6 @@ You can get these values from:
|
|
|
101
110
|
3. **create_bulk_tasks**
|
|
102
111
|
- Creates multiple tasks simultaneously in a list
|
|
103
112
|
- Required parameters:
|
|
104
|
-
- `listId`: ID of the list to create the tasks in
|
|
105
113
|
- `tasks`: Array of task objects, each containing:
|
|
106
114
|
- `name`: Name of the task (required)
|
|
107
115
|
- `description`: Task description (optional)
|
|
@@ -109,6 +117,9 @@ You can get these values from:
|
|
|
109
117
|
- `priority`: Priority level 1-4 (optional)
|
|
110
118
|
- `dueDate`: Due date ISO string (optional)
|
|
111
119
|
- `assignees`: Array of user IDs (optional)
|
|
120
|
+
- Optional parameters:
|
|
121
|
+
- `listId`: ID of the list (optional if listName provided)
|
|
122
|
+
- `listName`: Name of the list (optional if listId provided)
|
|
112
123
|
|
|
113
124
|
4. **create_list**
|
|
114
125
|
- Creates a new list in a space
|
|
@@ -147,13 +158,17 @@ You can get these values from:
|
|
|
147
158
|
- Moves a task to a different list
|
|
148
159
|
- Required parameters:
|
|
149
160
|
- `taskId`: ID of the task to move
|
|
150
|
-
|
|
161
|
+
- Optional parameters:
|
|
162
|
+
- `listId`: ID of destination list (optional if listName provided)
|
|
163
|
+
- `listName`: Name of destination list (optional if listId provided)
|
|
151
164
|
|
|
152
165
|
8. **duplicate_task**
|
|
153
166
|
- Creates a copy of a task in a specified list
|
|
154
167
|
- Required parameters:
|
|
155
168
|
- `taskId`: ID of the task to duplicate
|
|
156
|
-
|
|
169
|
+
- Optional parameters:
|
|
170
|
+
- `listId`: ID of destination list (optional if listName provided)
|
|
171
|
+
- `listName`: Name of destination list (optional if listId provided)
|
|
157
172
|
|
|
158
173
|
9. **update_task**
|
|
159
174
|
- Updates an existing task
|
|
@@ -231,4 +246,4 @@ Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md)
|
|
|
231
246
|
|
|
232
247
|
## License
|
|
233
248
|
|
|
234
|
-
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
249
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
package/build/index.js
CHANGED
|
@@ -101,8 +101,8 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
101
101
|
return {
|
|
102
102
|
tools: [
|
|
103
103
|
{
|
|
104
|
-
name: "
|
|
105
|
-
description: "List
|
|
104
|
+
name: "workspace_hierarchy",
|
|
105
|
+
description: "List complete hierarchy of the ClickUp workspace, including spaces, folders, lists, and their IDs and available statuses",
|
|
106
106
|
inputSchema: {
|
|
107
107
|
type: "object",
|
|
108
108
|
properties: {},
|
|
@@ -117,7 +117,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
117
117
|
properties: {
|
|
118
118
|
listId: {
|
|
119
119
|
type: "string",
|
|
120
|
-
description: "ID of the list to create the task in"
|
|
120
|
+
description: "ID of the list to create the task in (optional if listName is provided)"
|
|
121
|
+
},
|
|
122
|
+
listName: {
|
|
123
|
+
type: "string",
|
|
124
|
+
description: "Name of the list to create the task in (optional if listId is provided)"
|
|
121
125
|
},
|
|
122
126
|
name: {
|
|
123
127
|
type: "string",
|
|
@@ -140,7 +144,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
140
144
|
description: "Due date of the task (ISO string)"
|
|
141
145
|
}
|
|
142
146
|
},
|
|
143
|
-
required: ["
|
|
147
|
+
required: ["name"]
|
|
144
148
|
}
|
|
145
149
|
},
|
|
146
150
|
{
|
|
@@ -151,7 +155,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
151
155
|
properties: {
|
|
152
156
|
listId: {
|
|
153
157
|
type: "string",
|
|
154
|
-
description: "ID of the list to create the tasks in"
|
|
158
|
+
description: "ID of the list to create the tasks in (optional if listName is provided)"
|
|
159
|
+
},
|
|
160
|
+
listName: {
|
|
161
|
+
type: "string",
|
|
162
|
+
description: "Name of the list to create the tasks in (optional if listId is provided)"
|
|
155
163
|
},
|
|
156
164
|
tasks: {
|
|
157
165
|
type: "array",
|
|
@@ -177,7 +185,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
177
185
|
},
|
|
178
186
|
dueDate: {
|
|
179
187
|
type: "string",
|
|
180
|
-
description: "Due date
|
|
188
|
+
description: "Due date (ISO string)"
|
|
181
189
|
},
|
|
182
190
|
assignees: {
|
|
183
191
|
type: "array",
|
|
@@ -191,7 +199,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
191
199
|
}
|
|
192
200
|
}
|
|
193
201
|
},
|
|
194
|
-
required: ["
|
|
202
|
+
required: ["tasks"]
|
|
195
203
|
}
|
|
196
204
|
},
|
|
197
205
|
{
|
|
@@ -312,10 +320,14 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
312
320
|
},
|
|
313
321
|
listId: {
|
|
314
322
|
type: "string",
|
|
315
|
-
description: "ID of the destination list"
|
|
323
|
+
description: "ID of the destination list (optional if listName is provided)"
|
|
324
|
+
},
|
|
325
|
+
listName: {
|
|
326
|
+
type: "string",
|
|
327
|
+
description: "Name of the destination list (optional if listId is provided)"
|
|
316
328
|
}
|
|
317
329
|
},
|
|
318
|
-
required: ["taskId"
|
|
330
|
+
required: ["taskId"]
|
|
319
331
|
}
|
|
320
332
|
},
|
|
321
333
|
{
|
|
@@ -330,10 +342,14 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
330
342
|
},
|
|
331
343
|
listId: {
|
|
332
344
|
type: "string",
|
|
333
|
-
description: "ID of the list
|
|
345
|
+
description: "ID of the destination list (optional if listName is provided)"
|
|
346
|
+
},
|
|
347
|
+
listName: {
|
|
348
|
+
type: "string",
|
|
349
|
+
description: "Name of the destination list (optional if listId is provided)"
|
|
334
350
|
}
|
|
335
351
|
},
|
|
336
|
-
required: ["taskId"
|
|
352
|
+
required: ["taskId"]
|
|
337
353
|
}
|
|
338
354
|
},
|
|
339
355
|
{
|
|
@@ -380,17 +396,35 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
380
396
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
381
397
|
try {
|
|
382
398
|
switch (request.params.name) {
|
|
383
|
-
case "
|
|
399
|
+
case "workspace_hierarchy": {
|
|
384
400
|
const spaces = await clickup.getSpaces(config.teamId);
|
|
385
401
|
const allLists = await clickup.getAllLists(config.teamId);
|
|
386
|
-
let output = "
|
|
402
|
+
let output = "ClickUp Workspace Hierarchy:\n\n";
|
|
387
403
|
for (const space of spaces) {
|
|
388
404
|
output += `Space: ${space.name} (ID: ${space.id})\n`;
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
output += `
|
|
393
|
-
|
|
405
|
+
// Get folders in this space
|
|
406
|
+
const folders = await clickup.getFolders(space.id);
|
|
407
|
+
for (const folder of folders) {
|
|
408
|
+
output += ` ├─ Folder: ${folder.name} (ID: ${folder.id})\n`;
|
|
409
|
+
// Get lists in this folder
|
|
410
|
+
const folderLists = folder.lists || [];
|
|
411
|
+
for (const list of folderLists) {
|
|
412
|
+
const { statuses } = await clickup.getTasks(list.id);
|
|
413
|
+
output += ` │ └─ List: ${list.name} (ID: ${list.id})\n`;
|
|
414
|
+
output += ` │ Available Statuses: ${statuses.join(', ')}\n`;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
// Get lists directly in space (not in folders)
|
|
418
|
+
const spaceLists = allLists.filter(list => list.space &&
|
|
419
|
+
list.space.id === space.id &&
|
|
420
|
+
!folders.some(folder => folder.lists?.some(fl => fl.id === list.id)));
|
|
421
|
+
if (spaceLists.length > 0) {
|
|
422
|
+
output += " ├─ Lists (not in folders):\n";
|
|
423
|
+
for (const list of spaceLists) {
|
|
424
|
+
const { statuses } = await clickup.getTasks(list.id);
|
|
425
|
+
output += ` │ └─ List: ${list.name} (ID: ${list.id})\n`;
|
|
426
|
+
output += ` │ Available Statuses: ${statuses.join(', ')}\n`;
|
|
427
|
+
}
|
|
394
428
|
}
|
|
395
429
|
output += "\n";
|
|
396
430
|
}
|
|
@@ -413,10 +447,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
413
447
|
}
|
|
414
448
|
case "create_task": {
|
|
415
449
|
const args = request.params.arguments;
|
|
416
|
-
|
|
417
|
-
|
|
450
|
+
let listId = args.listId;
|
|
451
|
+
if (!listId && args.listName) {
|
|
452
|
+
const result = await clickup.findListByNameGlobally(config.teamId, args.listName);
|
|
453
|
+
if (!result) {
|
|
454
|
+
throw new Error(`List with name "${args.listName}" not found`);
|
|
455
|
+
}
|
|
456
|
+
listId = result.list.id;
|
|
457
|
+
}
|
|
458
|
+
if (!listId) {
|
|
459
|
+
throw new Error("Either listId or listName is required");
|
|
418
460
|
}
|
|
419
|
-
const { listId, ...taskData } = args;
|
|
461
|
+
const { listId: _, listName: __, ...taskData } = args;
|
|
420
462
|
const task = await clickup.createTask(listId, taskData);
|
|
421
463
|
return {
|
|
422
464
|
content: [{
|
|
@@ -427,10 +469,21 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
427
469
|
}
|
|
428
470
|
case "create_bulk_tasks": {
|
|
429
471
|
const args = request.params.arguments;
|
|
430
|
-
|
|
431
|
-
|
|
472
|
+
let listId = args.listId;
|
|
473
|
+
if (!listId && args.listName) {
|
|
474
|
+
const result = await clickup.findListByNameGlobally(config.teamId, args.listName);
|
|
475
|
+
if (!result) {
|
|
476
|
+
throw new Error(`List with name "${args.listName}" not found`);
|
|
477
|
+
}
|
|
478
|
+
listId = result.list.id;
|
|
479
|
+
}
|
|
480
|
+
if (!listId) {
|
|
481
|
+
throw new Error("Either listId or listName is required");
|
|
482
|
+
}
|
|
483
|
+
if (!args.tasks || !args.tasks.length) {
|
|
484
|
+
throw new Error("At least one task is required");
|
|
432
485
|
}
|
|
433
|
-
const { listId, tasks } = args;
|
|
486
|
+
const { listId: _, listName: __, tasks } = args;
|
|
434
487
|
const createdTasks = await clickup.createBulkTasks(listId, { tasks });
|
|
435
488
|
return {
|
|
436
489
|
content: [{
|
|
@@ -444,6 +497,29 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
444
497
|
if (!args.name) {
|
|
445
498
|
throw new Error("name is required");
|
|
446
499
|
}
|
|
500
|
+
// If folder is specified, create list in folder
|
|
501
|
+
if (args.folderName || args.folderId) {
|
|
502
|
+
let folderId = args.folderId;
|
|
503
|
+
if (!folderId && args.folderName) {
|
|
504
|
+
const result = await clickup.findFolderByNameGlobally(config.teamId, args.folderName);
|
|
505
|
+
if (!result) {
|
|
506
|
+
throw new Error(`Folder with name "${args.folderName}" not found`);
|
|
507
|
+
}
|
|
508
|
+
folderId = result.folder.id;
|
|
509
|
+
}
|
|
510
|
+
if (!folderId) {
|
|
511
|
+
throw new Error("Either folderId or folderName must be provided");
|
|
512
|
+
}
|
|
513
|
+
const { spaceId: _, spaceName: __, folderName: ___, folderId: ____, ...listData } = args;
|
|
514
|
+
const list = await clickup.createListInFolder(folderId, listData);
|
|
515
|
+
return {
|
|
516
|
+
content: [{
|
|
517
|
+
type: "text",
|
|
518
|
+
text: `Created list ${list.id}: ${list.name} in folder`
|
|
519
|
+
}]
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
// Otherwise, create list in space
|
|
447
523
|
let spaceId = args.spaceId;
|
|
448
524
|
if (!spaceId && args.spaceName) {
|
|
449
525
|
const space = await clickup.findSpaceByName(config.teamId, args.spaceName);
|
|
@@ -455,7 +531,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
455
531
|
if (!spaceId) {
|
|
456
532
|
throw new Error("Either spaceId or spaceName must be provided");
|
|
457
533
|
}
|
|
458
|
-
const { spaceId: _, spaceName: __, ...listData } = args;
|
|
534
|
+
const { spaceId: _, spaceName: __, folderName: ___, folderId: ____, ...listData } = args;
|
|
459
535
|
const list = await clickup.createList(spaceId, listData);
|
|
460
536
|
return {
|
|
461
537
|
content: [{
|
|
@@ -505,18 +581,21 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
505
581
|
spaceId = space.id;
|
|
506
582
|
}
|
|
507
583
|
if (!spaceId) {
|
|
508
|
-
throw new Error("Either spaceId or spaceName must be provided
|
|
509
|
-
}
|
|
510
|
-
const folder = await clickup.findFolderByName(spaceId, args.folderName);
|
|
511
|
-
if (!folder) {
|
|
512
|
-
throw new Error(`Folder with name "${args.folderName}" not found`);
|
|
584
|
+
throw new Error("Either spaceId or spaceName must be provided");
|
|
513
585
|
}
|
|
514
|
-
|
|
586
|
+
const { spaceId: _, spaceName: ___, ...listData } = args;
|
|
587
|
+
const list = await clickup.createListInFolder(spaceId, listData);
|
|
588
|
+
return {
|
|
589
|
+
content: [{
|
|
590
|
+
type: "text",
|
|
591
|
+
text: `Created list ${list.id}: ${list.name} in folder`
|
|
592
|
+
}]
|
|
593
|
+
};
|
|
515
594
|
}
|
|
516
595
|
if (!folderId) {
|
|
517
|
-
throw new Error("Either folderId or folderName
|
|
596
|
+
throw new Error("Either folderId or folderName must be provided");
|
|
518
597
|
}
|
|
519
|
-
const {
|
|
598
|
+
const { spaceId: _, spaceName: ___, ...listData } = args;
|
|
520
599
|
const list = await clickup.createListInFolder(folderId, listData);
|
|
521
600
|
return {
|
|
522
601
|
content: [{
|
|
@@ -527,37 +606,64 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
527
606
|
}
|
|
528
607
|
case "move_task": {
|
|
529
608
|
const args = request.params.arguments;
|
|
530
|
-
if (!args.taskId
|
|
531
|
-
throw new Error("taskId
|
|
609
|
+
if (!args.taskId) {
|
|
610
|
+
throw new Error("taskId is required");
|
|
611
|
+
}
|
|
612
|
+
let listId = args.listId;
|
|
613
|
+
if (!listId && args.listName) {
|
|
614
|
+
const result = await clickup.findListByNameGlobally(config.teamId, args.listName);
|
|
615
|
+
if (!result) {
|
|
616
|
+
throw new Error(`List with name "${args.listName}" not found`);
|
|
617
|
+
}
|
|
618
|
+
listId = result.list.id;
|
|
532
619
|
}
|
|
533
|
-
|
|
620
|
+
if (!listId) {
|
|
621
|
+
throw new Error("Either listId or listName is required");
|
|
622
|
+
}
|
|
623
|
+
const task = await clickup.moveTask(args.taskId, listId);
|
|
534
624
|
return {
|
|
535
625
|
content: [{
|
|
536
626
|
type: "text",
|
|
537
|
-
text: `Moved task ${task.id} to list ${
|
|
627
|
+
text: `Moved task ${task.id} to list ${listId}`
|
|
538
628
|
}]
|
|
539
629
|
};
|
|
540
630
|
}
|
|
541
631
|
case "duplicate_task": {
|
|
542
632
|
const args = request.params.arguments;
|
|
543
|
-
if (!args.taskId
|
|
544
|
-
throw new Error("taskId
|
|
633
|
+
if (!args.taskId) {
|
|
634
|
+
throw new Error("taskId is required");
|
|
635
|
+
}
|
|
636
|
+
let listId = args.listId;
|
|
637
|
+
if (!listId && args.listName) {
|
|
638
|
+
const result = await clickup.findListByNameGlobally(config.teamId, args.listName);
|
|
639
|
+
if (!result) {
|
|
640
|
+
throw new Error(`List with name "${args.listName}" not found`);
|
|
641
|
+
}
|
|
642
|
+
listId = result.list.id;
|
|
545
643
|
}
|
|
546
|
-
|
|
644
|
+
if (!listId) {
|
|
645
|
+
throw new Error("Either listId or listName is required");
|
|
646
|
+
}
|
|
647
|
+
const task = await clickup.duplicateTask(args.taskId, listId);
|
|
547
648
|
return {
|
|
548
649
|
content: [{
|
|
549
650
|
type: "text",
|
|
550
|
-
text: `Duplicated task ${args.taskId} to new task ${task.id} in list ${
|
|
651
|
+
text: `Duplicated task ${args.taskId} to new task ${task.id} in list ${listId}`
|
|
551
652
|
}]
|
|
552
653
|
};
|
|
553
654
|
}
|
|
554
655
|
case "update_task": {
|
|
555
656
|
const args = request.params.arguments;
|
|
556
|
-
if (!args.taskId) {
|
|
557
|
-
throw new Error("
|
|
657
|
+
if (!args.taskId || !args.name || !args.description || !args.status || !args.priority || !args.due_date) {
|
|
658
|
+
throw new Error("All arguments are required");
|
|
558
659
|
}
|
|
559
|
-
const
|
|
560
|
-
|
|
660
|
+
const task = await clickup.updateTask(args.taskId, {
|
|
661
|
+
name: args.name,
|
|
662
|
+
description: args.description,
|
|
663
|
+
status: args.status,
|
|
664
|
+
priority: args.priority,
|
|
665
|
+
due_date: args.due_date ? new Date(args.due_date).getTime() : undefined
|
|
666
|
+
});
|
|
561
667
|
return {
|
|
562
668
|
content: [{
|
|
563
669
|
type: "text",
|
|
@@ -565,152 +671,119 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
565
671
|
}]
|
|
566
672
|
};
|
|
567
673
|
}
|
|
568
|
-
default:
|
|
569
|
-
throw new Error("Unknown tool");
|
|
570
674
|
}
|
|
675
|
+
throw new Error("Unknown tool");
|
|
571
676
|
}
|
|
572
677
|
catch (error) {
|
|
573
|
-
console.error('Error
|
|
678
|
+
console.error('Error executing tool:', error);
|
|
574
679
|
throw error;
|
|
575
680
|
}
|
|
576
681
|
});
|
|
577
682
|
/**
|
|
578
|
-
*
|
|
579
|
-
*
|
|
683
|
+
* Handler for listing available prompts.
|
|
684
|
+
* Exposes prompts for summarizing/analyzing ClickUp tasks.
|
|
580
685
|
*/
|
|
581
686
|
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
582
687
|
return {
|
|
583
688
|
prompts: [
|
|
584
689
|
{
|
|
585
690
|
name: "summarize_tasks",
|
|
586
|
-
description: "Summarize all tasks
|
|
691
|
+
description: "Summarize all ClickUp tasks"
|
|
587
692
|
},
|
|
588
693
|
{
|
|
589
|
-
name: "
|
|
590
|
-
description: "Analyze task priorities
|
|
694
|
+
name: "analyze_task_priorities",
|
|
695
|
+
description: "Analyze task priorities"
|
|
591
696
|
},
|
|
592
697
|
{
|
|
593
|
-
name: "
|
|
594
|
-
description: "Generate
|
|
698
|
+
name: "generate_task_descriptions",
|
|
699
|
+
description: "Generate detailed descriptions for tasks"
|
|
595
700
|
}
|
|
596
701
|
]
|
|
597
702
|
};
|
|
598
703
|
});
|
|
704
|
+
/**
|
|
705
|
+
* Handler for getting a specific prompt.
|
|
706
|
+
* Takes a prompt name and returns the prompt content.
|
|
707
|
+
*/
|
|
599
708
|
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
600
709
|
try {
|
|
601
710
|
switch (request.params.name) {
|
|
602
711
|
case "summarize_tasks": {
|
|
603
712
|
const spaces = await clickup.getSpaces(config.teamId);
|
|
604
|
-
|
|
605
|
-
// Gather all tasks
|
|
713
|
+
let output = "Summarized Tasks:\n\n";
|
|
606
714
|
for (const space of spaces) {
|
|
607
715
|
const lists = await clickup.getLists(space.id);
|
|
608
716
|
for (const list of lists) {
|
|
609
|
-
const { tasks
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
uri: `clickup://task/${task.id}`,
|
|
614
|
-
mimeType: "application/json",
|
|
615
|
-
text: JSON.stringify(task, null, 2)
|
|
616
|
-
}
|
|
617
|
-
})));
|
|
717
|
+
const { tasks } = await clickup.getTasks(list.id);
|
|
718
|
+
for (const task of tasks) {
|
|
719
|
+
output += `- ${task.name}: ${task.description}\n`;
|
|
720
|
+
}
|
|
618
721
|
}
|
|
619
722
|
}
|
|
620
723
|
return {
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
type: "text",
|
|
626
|
-
text: "Please provide a summary of the following ClickUp tasks:"
|
|
627
|
-
}
|
|
628
|
-
},
|
|
629
|
-
...tasks.map(task => ({
|
|
630
|
-
role: "user",
|
|
631
|
-
content: task
|
|
632
|
-
})),
|
|
633
|
-
{
|
|
634
|
-
role: "user",
|
|
635
|
-
content: {
|
|
636
|
-
type: "text",
|
|
637
|
-
text: "Please provide:\n1. A high-level overview of all tasks\n2. Group them by status\n3. Highlight any urgent or high-priority items\n4. Suggest any task dependencies or relationships"
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
]
|
|
724
|
+
content: [{
|
|
725
|
+
type: "text",
|
|
726
|
+
text: output
|
|
727
|
+
}]
|
|
641
728
|
};
|
|
642
729
|
}
|
|
643
|
-
case "
|
|
730
|
+
case "analyze_task_priorities": {
|
|
644
731
|
const spaces = await clickup.getSpaces(config.teamId);
|
|
645
|
-
const
|
|
732
|
+
const allTasks = [];
|
|
646
733
|
for (const space of spaces) {
|
|
647
734
|
const lists = await clickup.getLists(space.id);
|
|
648
735
|
for (const list of lists) {
|
|
649
|
-
const { tasks
|
|
650
|
-
|
|
651
|
-
type: "resource",
|
|
652
|
-
resource: {
|
|
653
|
-
uri: `clickup://task/${task.id}`,
|
|
654
|
-
mimeType: "application/json",
|
|
655
|
-
text: JSON.stringify(task, null, 2)
|
|
656
|
-
}
|
|
657
|
-
})));
|
|
736
|
+
const { tasks } = await clickup.getTasks(list.id);
|
|
737
|
+
allTasks.push(...tasks);
|
|
658
738
|
}
|
|
659
739
|
}
|
|
740
|
+
const priorities = allTasks.map((task) => task.priority?.priority);
|
|
741
|
+
const uniquePriorities = [...new Set(priorities.filter(p => p !== undefined))];
|
|
742
|
+
const priorityCounts = uniquePriorities.map(priority => ({
|
|
743
|
+
priority: priority,
|
|
744
|
+
count: priorities.filter((p) => p === priority).length
|
|
745
|
+
}));
|
|
746
|
+
let output = "Task Priorities Analysis:\n\n";
|
|
747
|
+
output += "Available Priorities: " + uniquePriorities.join(', ') + "\n\n";
|
|
748
|
+
output += "Priority Counts:\n";
|
|
749
|
+
for (const priority of priorityCounts) {
|
|
750
|
+
output += `- Priority ${priority.priority}: ${priority.count}\n`;
|
|
751
|
+
}
|
|
660
752
|
return {
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
type: "text",
|
|
666
|
-
text: "Please analyze the priorities of the following ClickUp tasks:"
|
|
667
|
-
}
|
|
668
|
-
},
|
|
669
|
-
...tasks.map(task => ({
|
|
670
|
-
role: "user",
|
|
671
|
-
content: task
|
|
672
|
-
})),
|
|
673
|
-
{
|
|
674
|
-
role: "user",
|
|
675
|
-
content: {
|
|
676
|
-
type: "text",
|
|
677
|
-
text: "Please provide:\n1. Analysis of current priority distribution\n2. Identify any misaligned priorities\n3. Suggest priority adjustments\n4. Recommend task sequencing based on priorities"
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
]
|
|
753
|
+
content: [{
|
|
754
|
+
type: "text",
|
|
755
|
+
text: output
|
|
756
|
+
}]
|
|
681
757
|
};
|
|
682
758
|
}
|
|
683
|
-
case "
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
}
|
|
759
|
+
case "generate_task_descriptions": {
|
|
760
|
+
const spaces = await clickup.getSpaces(config.teamId);
|
|
761
|
+
let output = "Generated Task Descriptions:\n\n";
|
|
762
|
+
for (const space of spaces) {
|
|
763
|
+
const lists = await clickup.getLists(space.id);
|
|
764
|
+
for (const list of lists) {
|
|
765
|
+
const { tasks } = await clickup.getTasks(list.id);
|
|
766
|
+
for (const task of tasks) {
|
|
767
|
+
output += `- ${task.name}: ${task.description}\n`;
|
|
692
768
|
}
|
|
693
|
-
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
return {
|
|
772
|
+
content: [{
|
|
773
|
+
type: "text",
|
|
774
|
+
text: output
|
|
775
|
+
}]
|
|
694
776
|
};
|
|
695
777
|
}
|
|
696
778
|
default:
|
|
697
|
-
throw new Error("
|
|
779
|
+
throw new Error("Prompt not found");
|
|
698
780
|
}
|
|
699
781
|
}
|
|
700
782
|
catch (error) {
|
|
701
|
-
console.error('Error
|
|
783
|
+
console.error('Error getting prompt:', error);
|
|
702
784
|
throw error;
|
|
703
785
|
}
|
|
704
786
|
});
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
*/
|
|
709
|
-
async function main() {
|
|
710
|
-
const transport = new StdioServerTransport();
|
|
711
|
-
await server.connect(transport);
|
|
712
|
-
}
|
|
713
|
-
main().catch((error) => {
|
|
714
|
-
console.error("Server error:", error);
|
|
715
|
-
process.exit(1);
|
|
716
|
-
});
|
|
787
|
+
// Start the server
|
|
788
|
+
const transport = new StdioServerTransport();
|
|
789
|
+
transport.start();
|
|
@@ -110,6 +110,43 @@ export class ClickUpService {
|
|
|
110
110
|
});
|
|
111
111
|
return response.data;
|
|
112
112
|
}
|
|
113
|
+
async findListByNameGlobally(teamId, listName) {
|
|
114
|
+
const spaces = await this.getSpaces(teamId);
|
|
115
|
+
for (const space of spaces) {
|
|
116
|
+
// Check lists in folders
|
|
117
|
+
const folders = await this.getFolders(space.id);
|
|
118
|
+
for (const folder of folders) {
|
|
119
|
+
const folderList = folder.lists?.find(list => list.name.toLowerCase() === listName.toLowerCase());
|
|
120
|
+
if (folderList) {
|
|
121
|
+
return { list: folderList, space, folder };
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// Check lists directly in space
|
|
125
|
+
const spaceLists = await this.getLists(space.id);
|
|
126
|
+
const spaceList = spaceLists.find(list => list.name.toLowerCase() === listName.toLowerCase());
|
|
127
|
+
if (spaceList) {
|
|
128
|
+
return { list: spaceList, space };
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// Check lists without spaces
|
|
132
|
+
const allLists = await this.getAllLists(teamId);
|
|
133
|
+
const list = allLists.find(list => list.name.toLowerCase() === listName.toLowerCase());
|
|
134
|
+
if (list) {
|
|
135
|
+
return { list };
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
async findFolderByNameGlobally(teamId, folderName) {
|
|
140
|
+
const spaces = await this.getSpaces(teamId);
|
|
141
|
+
for (const space of spaces) {
|
|
142
|
+
const folders = await this.getFolders(space.id);
|
|
143
|
+
const folder = folders.find(folder => folder.name.toLowerCase() === folderName.toLowerCase());
|
|
144
|
+
if (folder) {
|
|
145
|
+
return { folder, space };
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
113
150
|
async duplicateTask(taskId, listId) {
|
|
114
151
|
const response = await this.client.post(`/task/${taskId}/duplicate`, {
|
|
115
152
|
list: listId
|
|
@@ -123,8 +160,4 @@ export class ClickUpService {
|
|
|
123
160
|
const response = await this.client.put(`/list/${listId}`, data);
|
|
124
161
|
return response.data;
|
|
125
162
|
}
|
|
126
|
-
async findListByName(spaceId, listName) {
|
|
127
|
-
const lists = await this.getLists(spaceId);
|
|
128
|
-
return lists.find(list => list.name.toLowerCase() === listName.toLowerCase()) || null;
|
|
129
|
-
}
|
|
130
163
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taazkareem/clickup-mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
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",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"homepage": "https://github.com/taazkareem/clickup-mcp-server#readme",
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@modelcontextprotocol/sdk": "
|
|
44
|
+
"@modelcontextprotocol/sdk": "^1.4.1",
|
|
45
45
|
"axios": "^1.6.7",
|
|
46
46
|
"dotenv": "^16.4.1"
|
|
47
47
|
},
|