@taazkareem/clickup-mcp-server 0.4.48 → 0.4.51
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.smithery +36 -0
- package/LICENSE +8 -2
- package/README.md +74 -211
- package/build/config.js +4 -16
- package/build/index.js +529 -302
- package/build/services/clickup.js +637 -80
- package/package.json +10 -10
- package/smithery.yaml +23 -0
- package/build/handlers/prompts.js +0 -88
- package/build/handlers/tools.js +0 -38
- package/build/utils/resolvers.js +0 -48
package/package.json
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taazkareem/clickup-mcp-server",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.51",
|
|
4
4
|
"description": "ClickUp MCP Server - Integrate ClickUp tasks with AI through Model Context Protocol",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "
|
|
6
|
+
"main": "build/index.js",
|
|
7
7
|
"bin": {
|
|
8
|
-
"clickup-mcp-server": "build/index.js"
|
|
9
|
-
"@taazkareem/clickup-mcp-server": "build/index.js"
|
|
8
|
+
"clickup-mcp-server": "build/index.js"
|
|
10
9
|
},
|
|
11
10
|
"files": [
|
|
12
11
|
"build",
|
|
13
12
|
"README.md",
|
|
14
|
-
"LICENSE"
|
|
13
|
+
"LICENSE",
|
|
14
|
+
"Dockerfile.smithery",
|
|
15
|
+
"smithery.yaml"
|
|
15
16
|
],
|
|
16
17
|
"scripts": {
|
|
17
18
|
"build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
|
|
18
19
|
"start": "node build/index.js",
|
|
19
|
-
"serve": "node build/index.js --stdio",
|
|
20
20
|
"dev": "tsc -w",
|
|
21
21
|
"prepare": "npm run build",
|
|
22
22
|
"prepublishOnly": "npm test",
|
|
@@ -36,18 +36,18 @@
|
|
|
36
36
|
"license": "MIT",
|
|
37
37
|
"repository": {
|
|
38
38
|
"type": "git",
|
|
39
|
-
"url": "
|
|
39
|
+
"url": "https://github.com/taazkareem/clickup-mcp-server.git"
|
|
40
40
|
},
|
|
41
41
|
"bugs": {
|
|
42
42
|
"url": "https://github.com/taazkareem/clickup-mcp-server/issues"
|
|
43
43
|
},
|
|
44
44
|
"homepage": "https://github.com/taazkareem/clickup-mcp-server#readme",
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@modelcontextprotocol/sdk": "
|
|
47
|
-
"@
|
|
46
|
+
"@modelcontextprotocol/sdk": "0.6.0",
|
|
47
|
+
"@types/express": "^5.0.0",
|
|
48
48
|
"axios": "^1.6.7",
|
|
49
49
|
"dotenv": "^16.4.1",
|
|
50
|
-
"
|
|
50
|
+
"express": "^4.21.2"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@types/node": "^20.11.16",
|
package/smithery.yaml
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml
|
|
2
|
+
|
|
3
|
+
dockerfile: Dockerfile.smithery
|
|
4
|
+
|
|
5
|
+
startCommand:
|
|
6
|
+
type: stdio
|
|
7
|
+
configSchema:
|
|
8
|
+
# JSON Schema defining the configuration options for the MCP.
|
|
9
|
+
type: object
|
|
10
|
+
required:
|
|
11
|
+
- clickupApiKey
|
|
12
|
+
- clickupTeamId
|
|
13
|
+
properties:
|
|
14
|
+
clickupApiKey:
|
|
15
|
+
type: string
|
|
16
|
+
description: Your ClickUp API key.
|
|
17
|
+
clickupTeamId:
|
|
18
|
+
type: string
|
|
19
|
+
description: Your ClickUp Team ID.
|
|
20
|
+
commandFunction:
|
|
21
|
+
# A function that produces the CLI command to start the MCP on stdio.
|
|
22
|
+
|-
|
|
23
|
+
(config) => ({ command: 'node', args: ['build/index.js'], env: { CLICKUP_API_KEY: config.clickupApiKey, CLICKUP_TEAM_ID: config.clickupTeamId } })
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { getAllTasks } from '../utils/resolvers.js';
|
|
2
|
-
export async function handleSummarizeTasks(clickup, teamId) {
|
|
3
|
-
const { tasks } = await getAllTasks(clickup, teamId);
|
|
4
|
-
let output = "Summarized Tasks:\n\n";
|
|
5
|
-
for (const task of tasks) {
|
|
6
|
-
output += `- ${task.name}: ${task.description}\n`;
|
|
7
|
-
}
|
|
8
|
-
return output;
|
|
9
|
-
}
|
|
10
|
-
export async function handleAnalyzeTaskPriorities(clickup, teamId) {
|
|
11
|
-
const { tasks } = await getAllTasks(clickup, teamId);
|
|
12
|
-
const priorities = tasks.map(task => task.priority?.priority);
|
|
13
|
-
const uniquePriorities = [...new Set(priorities.filter(p => p !== undefined))];
|
|
14
|
-
const priorityCounts = uniquePriorities.map(priority => ({
|
|
15
|
-
priority,
|
|
16
|
-
count: priorities.filter(p => p === priority).length
|
|
17
|
-
}));
|
|
18
|
-
let output = "Task Priorities Analysis:\n\n";
|
|
19
|
-
output += "Available Priorities: " + uniquePriorities.join(', ') + "\n\n";
|
|
20
|
-
output += "Priority Counts:\n";
|
|
21
|
-
for (const priority of priorityCounts) {
|
|
22
|
-
output += `- Priority ${priority.priority}: ${priority.count}\n`;
|
|
23
|
-
}
|
|
24
|
-
return output;
|
|
25
|
-
}
|
|
26
|
-
export async function handleGenerateDescription(clickup, taskId) {
|
|
27
|
-
try {
|
|
28
|
-
const task = await clickup.getTask(taskId);
|
|
29
|
-
// Generate a structured description based on task properties
|
|
30
|
-
let description = "# Task Description\n\n";
|
|
31
|
-
// Basic Information
|
|
32
|
-
description += "## Overview\n";
|
|
33
|
-
description += `${task.name}\n\n`;
|
|
34
|
-
// Status and Priority
|
|
35
|
-
description += "## Status & Priority\n";
|
|
36
|
-
description += `- Status: ${task.status?.status || 'Not set'}\n`;
|
|
37
|
-
description += `- Priority: ${task.priority?.priority || 'Not set'}\n\n`;
|
|
38
|
-
// Time Information
|
|
39
|
-
description += "## Timeline\n";
|
|
40
|
-
description += `- Created: ${new Date(task.date_created).toLocaleDateString()}\n`;
|
|
41
|
-
if (task.due_date) {
|
|
42
|
-
description += `- Due Date: ${new Date(task.due_date).toLocaleDateString()}\n`;
|
|
43
|
-
}
|
|
44
|
-
description += "\n";
|
|
45
|
-
// Dependencies and Relationships
|
|
46
|
-
description += "## Dependencies & Relationships\n";
|
|
47
|
-
const dependencies = task.dependencies || [];
|
|
48
|
-
if (dependencies.length > 0) {
|
|
49
|
-
description += "### Dependencies:\n";
|
|
50
|
-
dependencies.forEach(dep => {
|
|
51
|
-
description += `- ${dep.task_id}\n`;
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
description += "No dependencies identified.\n";
|
|
56
|
-
}
|
|
57
|
-
description += "\n";
|
|
58
|
-
// Success Criteria
|
|
59
|
-
description += "## Success Criteria\n";
|
|
60
|
-
const checkList = task.check_list || [];
|
|
61
|
-
if (checkList.length > 0) {
|
|
62
|
-
checkList.forEach(item => {
|
|
63
|
-
description += `- [ ] ${item.name}\n`;
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
description += "- Task completion requirements to be defined\n";
|
|
68
|
-
}
|
|
69
|
-
description += "\n";
|
|
70
|
-
// Resources
|
|
71
|
-
description += "## Required Resources\n";
|
|
72
|
-
const assignees = task.assignees || [];
|
|
73
|
-
if (assignees.length > 0) {
|
|
74
|
-
description += "### Assigned Team Members:\n";
|
|
75
|
-
assignees.forEach(assignee => {
|
|
76
|
-
description += `- ${assignee.username}\n`;
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
else {
|
|
80
|
-
description += "No team members assigned yet.\n";
|
|
81
|
-
}
|
|
82
|
-
return description;
|
|
83
|
-
}
|
|
84
|
-
catch (error) {
|
|
85
|
-
console.error('Error generating description:', error);
|
|
86
|
-
throw error;
|
|
87
|
-
}
|
|
88
|
-
}
|
package/build/handlers/tools.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { resolveListId } from '../utils/resolvers.js';
|
|
2
|
-
export async function handleWorkspaceHierarchy(clickup, teamId) {
|
|
3
|
-
const spaces = await clickup.getSpaces(teamId);
|
|
4
|
-
const allLists = await clickup.getAllLists(teamId);
|
|
5
|
-
let output = "ClickUp Workspace Hierarchy:\n\n";
|
|
6
|
-
for (const space of spaces) {
|
|
7
|
-
output += `Space: ${space.name} (ID: ${space.id})\n`;
|
|
8
|
-
const folders = await clickup.getFolders(space.id);
|
|
9
|
-
for (const folder of folders) {
|
|
10
|
-
output += ` ├─ Folder: ${folder.name} (ID: ${folder.id})\n`;
|
|
11
|
-
const folderLists = folder.lists || [];
|
|
12
|
-
for (const list of folderLists) {
|
|
13
|
-
const { statuses } = await clickup.getTasks(list.id);
|
|
14
|
-
output += ` │ └─ List: ${list.name} (ID: ${list.id})\n`;
|
|
15
|
-
output += ` │ Available Statuses: ${statuses.join(', ')}\n`;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
const spaceLists = allLists.filter(list => list.space &&
|
|
19
|
-
list.space.id === space.id &&
|
|
20
|
-
!folders.some(folder => folder.lists?.some(fl => fl.id === list.id)));
|
|
21
|
-
if (spaceLists.length > 0) {
|
|
22
|
-
output += " ├─ Lists (not in folders):\n";
|
|
23
|
-
for (const list of spaceLists) {
|
|
24
|
-
const { statuses } = await clickup.getTasks(list.id);
|
|
25
|
-
output += ` │ └─ List: ${list.name} (ID: ${list.id})\n`;
|
|
26
|
-
output += ` │ Available Statuses: ${statuses.join(', ')}\n`;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
output += "\n";
|
|
30
|
-
}
|
|
31
|
-
return output;
|
|
32
|
-
}
|
|
33
|
-
export async function handleCreateTask(clickup, teamId, args) {
|
|
34
|
-
const listId = await resolveListId(clickup, teamId, args.listId, args.listName);
|
|
35
|
-
const { listId: _, listName: __, ...taskData } = args;
|
|
36
|
-
return await clickup.createTask(listId, taskData);
|
|
37
|
-
}
|
|
38
|
-
// Add other handler functions for each tool...
|
package/build/utils/resolvers.js
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
export async function resolveListId(clickup, teamId, listId, listName) {
|
|
2
|
-
if (listId)
|
|
3
|
-
return listId;
|
|
4
|
-
if (!listName) {
|
|
5
|
-
throw new Error("Either listId or listName is required");
|
|
6
|
-
}
|
|
7
|
-
const result = await clickup.findListByNameGlobally(teamId, listName);
|
|
8
|
-
if (!result) {
|
|
9
|
-
throw new Error(`List with name "${listName}" not found`);
|
|
10
|
-
}
|
|
11
|
-
return result.list.id;
|
|
12
|
-
}
|
|
13
|
-
export async function resolveSpaceId(clickup, teamId, spaceId, spaceName) {
|
|
14
|
-
if (spaceId)
|
|
15
|
-
return spaceId;
|
|
16
|
-
if (!spaceName) {
|
|
17
|
-
throw new Error("Either spaceId or spaceName is required");
|
|
18
|
-
}
|
|
19
|
-
const space = await clickup.findSpaceByName(teamId, spaceName);
|
|
20
|
-
if (!space) {
|
|
21
|
-
throw new Error(`Space with name "${spaceName}" not found`);
|
|
22
|
-
}
|
|
23
|
-
return space.id;
|
|
24
|
-
}
|
|
25
|
-
export async function resolveFolderId(clickup, teamId, folderId, folderName) {
|
|
26
|
-
if (folderId)
|
|
27
|
-
return folderId;
|
|
28
|
-
if (!folderName) {
|
|
29
|
-
throw new Error("Either folderId or folderName is required");
|
|
30
|
-
}
|
|
31
|
-
const result = await clickup.findFolderByNameGlobally(teamId, folderName);
|
|
32
|
-
if (!result) {
|
|
33
|
-
throw new Error(`Folder with name "${folderName}" not found`);
|
|
34
|
-
}
|
|
35
|
-
return result.folder.id;
|
|
36
|
-
}
|
|
37
|
-
export async function getAllTasks(clickup, teamId) {
|
|
38
|
-
const spaces = await clickup.getSpaces(teamId);
|
|
39
|
-
const spacePromises = spaces.map(async (space) => {
|
|
40
|
-
const lists = await clickup.getLists(space.id);
|
|
41
|
-
const listPromises = lists.map(list => clickup.getTasks(list.id));
|
|
42
|
-
const listResults = await Promise.all(listPromises);
|
|
43
|
-
return listResults.flatMap(result => result.tasks);
|
|
44
|
-
});
|
|
45
|
-
const tasksPerSpace = await Promise.all(spacePromises);
|
|
46
|
-
const allTasks = tasksPerSpace.flat();
|
|
47
|
-
return { tasks: allTasks, spaces };
|
|
48
|
-
}
|