@taazkareem/clickup-mcp-server 0.6.0 → 0.6.1
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 +14 -23
- package/build/config.js +34 -2
- package/build/index.js +3 -1
- package/build/logger.js +8 -38
- package/build/server.js +33 -8
- package/build/services/clickup/base.js +3 -0
- package/build/services/clickup/bulk.js +3 -0
- package/build/services/clickup/folder.js +3 -0
- package/build/services/clickup/index.js +9 -1
- package/build/services/clickup/list.js +3 -0
- package/build/services/clickup/tag.js +190 -0
- package/build/services/clickup/task.js +138 -0
- package/build/services/clickup/types.js +3 -0
- package/build/services/clickup/workspace.js +3 -0
- package/build/services/shared.js +3 -0
- package/build/tools/folder.js +3 -0
- package/build/tools/index.js +4 -0
- package/build/tools/list.js +3 -0
- package/build/tools/tag.js +824 -0
- package/build/tools/task/attachments.js +3 -0
- package/build/tools/task/bulk-operations.js +10 -0
- package/build/tools/task/handlers.js +61 -2
- package/build/tools/task/index.js +8 -1
- package/build/tools/task/main.js +18 -2
- package/build/tools/task/single-operations.js +10 -0
- package/build/tools/task/utilities.js +40 -3
- package/build/tools/task/workspace-operations.js +222 -0
- package/build/tools/utils.js +3 -0
- package/build/tools/workspace.js +3 -0
- package/build/utils/color-processor.js +183 -0
- package/build/utils/concurrency-utils.js +3 -0
- package/build/utils/date-utils.js +3 -0
- package/build/utils/resolver-utils.js +3 -0
- package/build/utils/sponsor-service.js +3 -0
- package/build/utils/token-utils.js +49 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
A Model Context Protocol (MCP) server for integrating ClickUp tasks with AI applications. This server allows AI agents to interact with ClickUp tasks, spaces, lists, and folders through a standardized protocol.
|
|
8
8
|
|
|
9
|
-
> 🚧 **Status Update:** v0.
|
|
9
|
+
> 🚧 **Status Update:** Rolling out v0.6.1 which will add Complete Tag Support including natural language tag color commands, Subtasks Support, Custom ID Support, and Logging Fixes
|
|
10
10
|
|
|
11
11
|
## Setup
|
|
12
12
|
|
|
@@ -22,7 +22,6 @@ A Model Context Protocol (MCP) server for integrating ClickUp tasks with AI appl
|
|
|
22
22
|
|
|
23
23
|
The server is hosted on [Smithery](https://smithery.ai/server/@TaazKareem/clickup-mcp-server). There, you can preview the available tools or copy the commands to run on your specific client app.
|
|
24
24
|
|
|
25
|
-
|
|
26
25
|
## NPX Installation
|
|
27
26
|
|
|
28
27
|
[](https://www.npmjs.com/package/@taazkareem/clickup-mcp-server)
|
|
@@ -55,26 +54,11 @@ Or use this npx command:
|
|
|
55
54
|
|
|
56
55
|
## Features
|
|
57
56
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
- Get task comments
|
|
64
|
-
- Set due dates using natural language and relative time expressions
|
|
65
|
-
- Attach files to tasks using local file paths, URL, base64, or chunked uploads
|
|
66
|
-
- Create and manage subtasks with support for multi-level nesting
|
|
67
|
-
|
|
68
|
-
- 📂 **Workspace Organization**
|
|
69
|
-
- Complete workspace hierarchy (spaces, folders, lists)
|
|
70
|
-
- Tree structure with clear relationships
|
|
71
|
-
- Efficient path-based navigation
|
|
72
|
-
|
|
73
|
-
- 🔄 **Integration Features**
|
|
74
|
-
- Name or ID-based item lookup
|
|
75
|
-
- Case-insensitive name matching
|
|
76
|
-
- Markdown formatting support
|
|
77
|
-
- Built-in API rate limiting
|
|
57
|
+
| 📝 Task Management | 🏷️ Tag Management |
|
|
58
|
+
|----------------------------|----------------------------|
|
|
59
|
+
| • Create, update, and delete tasks<br>• Move and duplicate tasks anywhere<br>• Support for single and bulk operations<br>• Set due dates with natural language<br>• Create and manage subtasks<br>• Add comments and attachments | • Create, update, and delete space tags<br>• Add and remove tags from tasks<br>• Use natural language color commands<br>• Automatic contrasting foreground colors<br>• View all space tags<br>• Tag-based task organization across workspace |
|
|
60
|
+
| 🌳 **Workspace Organization** | ⚡ **Integration Features** |
|
|
61
|
+
| • Navigate spaces, folders, and lists<br>• Create and manage folders<br>• Organize lists within spaces<br>• Create lists in folders<br>• View workspace hierarchy<br>• Efficient path navigation | • Name or ID-based lookups<br>• Case-insensitive matching<br>• Markdown formatting support<br>• Built-in rate limiting<br>• Error handling and validation<br>• Comprehensive API coverage |
|
|
78
62
|
|
|
79
63
|
## Available Tools
|
|
80
64
|
|
|
@@ -86,7 +70,8 @@ Or use this npx command:
|
|
|
86
70
|
| [update_task](docs/api-reference.md#task-management) | Modify task | `taskId`/`taskName` |
|
|
87
71
|
| [update_bulk_tasks](docs/api-reference.md#task-management) | Update multiple tasks | `tasks[]` with IDs or names |
|
|
88
72
|
| [get_tasks](docs/api-reference.md#task-management) | Get tasks from list | `listId`/`listName`, optional `subtasks` |
|
|
89
|
-
| [get_task](docs/api-reference.md#task-management) | Get task details | `taskId`/`taskName`, optional `subtasks` |
|
|
73
|
+
| [get_task](docs/api-reference.md#task-management) | Get single task details | `taskId`/`taskName`, optional `subtasks` |
|
|
74
|
+
| [get_workspace_tasks](docs/api-reference.md#task-management) | Get tasks with filtering | At least one filter (tags, list_ids, space_ids, etc.) |
|
|
90
75
|
| [get_task_comments](docs/api-reference.md#task-management) | Get comments on a task | `taskId`/`taskName` |
|
|
91
76
|
| [create_task_comment](docs/api-reference.md#task-management) | Add a comment to a task | `commentText`, (`taskId`/(`taskName`+`listName`)) |
|
|
92
77
|
| [attach_task_file](docs/api-reference.md#task-management) | Attach file to a task | `taskId`/`taskName`, (`file_data` or `file_url`) |
|
|
@@ -104,6 +89,12 @@ Or use this npx command:
|
|
|
104
89
|
| [get_list](docs/api-reference.md#list-management) | Get list details | `listId`/`listName` |
|
|
105
90
|
| [update_list](docs/api-reference.md#list-management) | Update list properties | `listId`/`listName` |
|
|
106
91
|
| [delete_list](docs/api-reference.md#list-management) | Delete list | `listId`/`listName` |
|
|
92
|
+
| [get_space_tags](docs/api-reference.md#tag-management) | Get space tags | `spaceId`/`spaceName` |
|
|
93
|
+
| [create_space_tag](docs/api-reference.md#tag-management) | Create tag | `tagName`, `spaceId`/`spaceName` |
|
|
94
|
+
| [update_space_tag](docs/api-reference.md#tag-management) | Update tag | `tagName`, `spaceId`/`spaceName` |
|
|
95
|
+
| [delete_space_tag](docs/api-reference.md#tag-management) | Delete tag | `tagName`, `spaceId`/`spaceName` |
|
|
96
|
+
| [add_tag_to_task](docs/api-reference.md#tag-management) | Add tag to task | `tagName`, `taskId`/(`taskName`+`listName`) |
|
|
97
|
+
| [remove_tag_from_task](docs/api-reference.md#tag-management) | Remove tag from task | `tagName`, `taskId`/(`taskName`+`listName`) |
|
|
107
98
|
|
|
108
99
|
See [full documentation](docs/api-reference.md) for optional parameters and advanced usage.
|
|
109
100
|
|
package/build/config.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* SPDX-FileCopyrightText: © 2025 Talib Kareem <taazkareem@icloud.com>
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*
|
|
5
|
+
* Configuration handling for ClickUp API credentials and application settings
|
|
3
6
|
*
|
|
4
7
|
* The required environment variables (CLICKUP_API_KEY and CLICKUP_TEAM_ID) are passed
|
|
5
8
|
* securely to this file when running the hosted server at smithery.ai. Optionally,
|
|
@@ -15,15 +18,44 @@ for (let i = 0; i < args.length; i++) {
|
|
|
15
18
|
envArgs.clickupApiKey = value;
|
|
16
19
|
if (key === 'CLICKUP_TEAM_ID')
|
|
17
20
|
envArgs.clickupTeamId = value;
|
|
21
|
+
if (key === 'LOG_LEVEL')
|
|
22
|
+
envArgs.logLevel = value;
|
|
18
23
|
i++;
|
|
19
24
|
}
|
|
20
25
|
}
|
|
26
|
+
// Log levels enum
|
|
27
|
+
export var LogLevel;
|
|
28
|
+
(function (LogLevel) {
|
|
29
|
+
LogLevel[LogLevel["TRACE"] = 0] = "TRACE";
|
|
30
|
+
LogLevel[LogLevel["DEBUG"] = 1] = "DEBUG";
|
|
31
|
+
LogLevel[LogLevel["INFO"] = 2] = "INFO";
|
|
32
|
+
LogLevel[LogLevel["WARN"] = 3] = "WARN";
|
|
33
|
+
LogLevel[LogLevel["ERROR"] = 4] = "ERROR";
|
|
34
|
+
})(LogLevel || (LogLevel = {}));
|
|
35
|
+
// Parse LOG_LEVEL string to LogLevel enum
|
|
36
|
+
export const parseLogLevel = (levelStr) => {
|
|
37
|
+
if (!levelStr)
|
|
38
|
+
return LogLevel.ERROR; // Default to ERROR if not specified
|
|
39
|
+
switch (levelStr.toUpperCase()) {
|
|
40
|
+
case 'TRACE': return LogLevel.TRACE;
|
|
41
|
+
case 'DEBUG': return LogLevel.DEBUG;
|
|
42
|
+
case 'INFO': return LogLevel.INFO;
|
|
43
|
+
case 'WARN': return LogLevel.WARN;
|
|
44
|
+
case 'ERROR': return LogLevel.ERROR;
|
|
45
|
+
default:
|
|
46
|
+
console.error(`Invalid LOG_LEVEL: ${levelStr}, defaulting to ERROR`);
|
|
47
|
+
return LogLevel.ERROR;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
21
50
|
// Load configuration from command line args or environment variables
|
|
22
51
|
const configuration = {
|
|
23
52
|
clickupApiKey: envArgs.clickupApiKey || process.env.CLICKUP_API_KEY || '',
|
|
24
53
|
clickupTeamId: envArgs.clickupTeamId || process.env.CLICKUP_TEAM_ID || '',
|
|
25
|
-
enableSponsorMessage: process.env.ENABLE_SPONSOR_MESSAGE !== 'false'
|
|
54
|
+
enableSponsorMessage: process.env.ENABLE_SPONSOR_MESSAGE !== 'false',
|
|
55
|
+
logLevel: parseLogLevel(envArgs.logLevel || process.env.LOG_LEVEL)
|
|
26
56
|
};
|
|
57
|
+
// Log the configured log level (but only to console to avoid circular dependency)
|
|
58
|
+
console.debug(`Log level set to: ${LogLevel[configuration.logLevel]}`);
|
|
27
59
|
// Validate only the required variables are present
|
|
28
60
|
const requiredVars = ['clickupApiKey', 'clickupTeamId'];
|
|
29
61
|
const missingEnvVars = requiredVars
|
package/build/index.js
CHANGED
package/build/logger.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* SPDX-FileCopyrightText: © 2025 Talib Kareem <taazkareem@icloud.com>
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*
|
|
2
5
|
* Logger module for MCP Server
|
|
3
6
|
*
|
|
4
7
|
* This module provides logging functionality for the server,
|
|
@@ -7,6 +10,7 @@
|
|
|
7
10
|
import { createWriteStream } from 'fs';
|
|
8
11
|
import { join, dirname } from 'path';
|
|
9
12
|
import { fileURLToPath } from 'url';
|
|
13
|
+
import config, { LogLevel } from './config.js';
|
|
10
14
|
// Get the directory name of the current module
|
|
11
15
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
16
|
// Current process ID for logging
|
|
@@ -15,44 +19,10 @@ const pid = process.pid;
|
|
|
15
19
|
const logFileName = 'server.log';
|
|
16
20
|
const logStream = createWriteStream(join(__dirname, logFileName), { flags: 'w' });
|
|
17
21
|
console.error(`Logging to ${join(__dirname, logFileName)}`);
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
LogLevel[LogLevel["DEBUG"] = 1] = "DEBUG";
|
|
23
|
-
LogLevel[LogLevel["INFO"] = 2] = "INFO";
|
|
24
|
-
LogLevel[LogLevel["WARN"] = 3] = "WARN";
|
|
25
|
-
LogLevel[LogLevel["ERROR"] = 4] = "ERROR";
|
|
26
|
-
})(LogLevel || (LogLevel = {}));
|
|
27
|
-
// Parse LOG_LEVEL environment variable or command line argument
|
|
28
|
-
const parseLogLevel = (levelStr) => {
|
|
29
|
-
if (!levelStr)
|
|
30
|
-
return LogLevel.ERROR; // Default to ERROR if not specified
|
|
31
|
-
switch (levelStr.toUpperCase()) {
|
|
32
|
-
case 'TRACE': return LogLevel.TRACE;
|
|
33
|
-
case 'DEBUG': return LogLevel.DEBUG;
|
|
34
|
-
case 'INFO': return LogLevel.INFO;
|
|
35
|
-
case 'WARN': return LogLevel.WARN;
|
|
36
|
-
case 'ERROR': return LogLevel.ERROR;
|
|
37
|
-
default:
|
|
38
|
-
console.error(`Invalid LOG_LEVEL: ${levelStr}, defaulting to ERROR`);
|
|
39
|
-
return LogLevel.ERROR;
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
// Parse command line arguments for LOG_LEVEL
|
|
43
|
-
const args = process.argv.slice(2);
|
|
44
|
-
let argLogLevel;
|
|
45
|
-
for (let i = 0; i < args.length; i++) {
|
|
46
|
-
if (args[i] === '--env' && i + 1 < args.length) {
|
|
47
|
-
const [key, value] = args[i + 1].split('=');
|
|
48
|
-
if (key === 'LOG_LEVEL')
|
|
49
|
-
argLogLevel = value;
|
|
50
|
-
i++;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
// Get log level from environment variable or command line, default to ERROR
|
|
54
|
-
const configuredLevel = parseLogLevel(argLogLevel || process.env.LOG_LEVEL);
|
|
55
|
-
console.debug(`Log level set to: ${LogLevel[configuredLevel]}`);
|
|
22
|
+
// Use the configured log level from config.ts
|
|
23
|
+
const configuredLevel = config.logLevel;
|
|
24
|
+
// Re-export LogLevel enum
|
|
25
|
+
export { LogLevel };
|
|
56
26
|
/**
|
|
57
27
|
* Check if a log level is enabled based on the configured level
|
|
58
28
|
* @param level The log level to check
|
package/build/server.js
CHANGED
|
@@ -1,21 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPDX-FileCopyrightText: © 2025 Talib Kareem <taazkareem@icloud.com>
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*
|
|
5
|
+
* MCP Server for ClickUp integration
|
|
6
|
+
*/
|
|
1
7
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
8
|
import { CallToolRequestSchema, ListToolsRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
3
9
|
import { workspaceHierarchyTool, handleGetWorkspaceHierarchy } from "./tools/workspace.js";
|
|
4
|
-
import { createTaskTool, updateTaskTool, moveTaskTool, duplicateTaskTool, getTaskTool, getTasksTool, deleteTaskTool, getTaskCommentsTool, createTaskCommentTool, createBulkTasksTool, updateBulkTasksTool, moveBulkTasksTool, deleteBulkTasksTool, attachTaskFileTool, handleCreateTask, handleUpdateTask, handleMoveTask, handleDuplicateTask, handleGetTasks, handleDeleteTask, handleGetTaskComments, handleCreateTaskComment, handleCreateBulkTasks, handleUpdateBulkTasks, handleMoveBulkTasks, handleDeleteBulkTasks, handleGetTask, handleAttachTaskFile } from "./tools/task/index.js";
|
|
10
|
+
import { createTaskTool, updateTaskTool, moveTaskTool, duplicateTaskTool, getTaskTool, getTasksTool, deleteTaskTool, getTaskCommentsTool, createTaskCommentTool, createBulkTasksTool, updateBulkTasksTool, moveBulkTasksTool, deleteBulkTasksTool, attachTaskFileTool, getWorkspaceTasksTool, handleCreateTask, handleUpdateTask, handleMoveTask, handleDuplicateTask, handleGetTasks, handleDeleteTask, handleGetTaskComments, handleCreateTaskComment, handleCreateBulkTasks, handleUpdateBulkTasks, handleMoveBulkTasks, handleDeleteBulkTasks, handleGetTask, handleAttachTaskFile, handleGetWorkspaceTasks } from "./tools/task/index.js";
|
|
5
11
|
import { createListTool, handleCreateList, createListInFolderTool, handleCreateListInFolder, getListTool, handleGetList, updateListTool, handleUpdateList, deleteListTool, handleDeleteList } from "./tools/list.js";
|
|
6
12
|
import { createFolderTool, handleCreateFolder, getFolderTool, handleGetFolder, updateFolderTool, handleUpdateFolder, deleteFolderTool, handleDeleteFolder } from "./tools/folder.js";
|
|
13
|
+
import { getSpaceTagsTool, handleGetSpaceTags, createSpaceTagTool, handleCreateSpaceTag, updateSpaceTagTool, handleUpdateSpaceTag, deleteSpaceTagTool, handleDeleteSpaceTag, addTagToTaskTool, handleAddTagToTask, removeTagFromTaskTool, handleRemoveTagFromTask } from "./tools/tag.js";
|
|
7
14
|
import { Logger } from "./logger.js";
|
|
8
15
|
import { clickUpServices } from "./services/shared.js";
|
|
9
16
|
// Create a logger instance for server
|
|
10
17
|
const logger = new Logger('Server');
|
|
11
18
|
// Use existing services from shared module instead of creating new ones
|
|
12
19
|
const { workspace } = clickUpServices;
|
|
13
|
-
/**
|
|
14
|
-
* MCP Server for ClickUp integration
|
|
15
|
-
*/
|
|
16
20
|
export const server = new Server({
|
|
17
21
|
name: "clickup-mcp-server",
|
|
18
|
-
version: "0.6.
|
|
22
|
+
version: "0.6.1",
|
|
19
23
|
}, {
|
|
20
24
|
capabilities: {
|
|
21
25
|
tools: {},
|
|
@@ -47,6 +51,7 @@ export function configureServer() {
|
|
|
47
51
|
updateBulkTasksTool,
|
|
48
52
|
moveBulkTasksTool,
|
|
49
53
|
deleteBulkTasksTool,
|
|
54
|
+
getWorkspaceTasksTool,
|
|
50
55
|
createListTool,
|
|
51
56
|
createListInFolderTool,
|
|
52
57
|
getListTool,
|
|
@@ -55,14 +60,20 @@ export function configureServer() {
|
|
|
55
60
|
createFolderTool,
|
|
56
61
|
getFolderTool,
|
|
57
62
|
updateFolderTool,
|
|
58
|
-
deleteFolderTool
|
|
63
|
+
deleteFolderTool,
|
|
64
|
+
getSpaceTagsTool,
|
|
65
|
+
createSpaceTagTool,
|
|
66
|
+
updateSpaceTagTool,
|
|
67
|
+
deleteSpaceTagTool,
|
|
68
|
+
addTagToTaskTool,
|
|
69
|
+
removeTagFromTaskTool
|
|
59
70
|
]
|
|
60
71
|
};
|
|
61
72
|
});
|
|
62
73
|
// Register CallTool handler with proper logging
|
|
63
74
|
logger.info("Registering tool handlers", {
|
|
64
|
-
toolCount:
|
|
65
|
-
categories: ["workspace", "task", "list", "folder"]
|
|
75
|
+
toolCount: 31,
|
|
76
|
+
categories: ["workspace", "task", "list", "folder", "tag"]
|
|
66
77
|
});
|
|
67
78
|
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
68
79
|
const { name, arguments: params } = req.params;
|
|
@@ -103,6 +114,8 @@ export function configureServer() {
|
|
|
103
114
|
return handleMoveBulkTasks(params);
|
|
104
115
|
case "delete_bulk_tasks":
|
|
105
116
|
return handleDeleteBulkTasks(params);
|
|
117
|
+
case "get_workspace_tasks":
|
|
118
|
+
return handleGetWorkspaceTasks(params);
|
|
106
119
|
case "create_list":
|
|
107
120
|
return handleCreateList(params);
|
|
108
121
|
case "create_list_in_folder":
|
|
@@ -121,6 +134,18 @@ export function configureServer() {
|
|
|
121
134
|
return handleUpdateFolder(params);
|
|
122
135
|
case "delete_folder":
|
|
123
136
|
return handleDeleteFolder(params);
|
|
137
|
+
case "get_space_tags":
|
|
138
|
+
return handleGetSpaceTags(params);
|
|
139
|
+
case "create_space_tag":
|
|
140
|
+
return handleCreateSpaceTag(params);
|
|
141
|
+
case "update_space_tag":
|
|
142
|
+
return handleUpdateSpaceTag(params);
|
|
143
|
+
case "delete_space_tag":
|
|
144
|
+
return handleDeleteSpaceTag(params);
|
|
145
|
+
case "add_tag_to_task":
|
|
146
|
+
return handleAddTagToTask(params);
|
|
147
|
+
case "remove_tag_from_task":
|
|
148
|
+
return handleRemoveTagFromTask(params);
|
|
124
149
|
default:
|
|
125
150
|
logger.error(`Unknown tool requested: ${name}`);
|
|
126
151
|
throw new Error(`Unknown tool: ${name}`);
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* SPDX-FileCopyrightText: © 2025 Talib Kareem <taazkareem@icloud.com>
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*
|
|
2
5
|
* ClickUp Service Entry Point
|
|
3
6
|
*
|
|
4
7
|
* This file re-exports all service modules for the ClickUp API integration.
|
|
@@ -13,11 +16,13 @@ export { WorkspaceService } from './workspace.js';
|
|
|
13
16
|
export { TaskService } from './task.js';
|
|
14
17
|
export { ListService } from './list.js';
|
|
15
18
|
export { FolderService } from './folder.js';
|
|
19
|
+
export { ClickUpTagService } from './tag.js';
|
|
16
20
|
// Import service classes for the factory function
|
|
17
21
|
import { WorkspaceService } from './workspace.js';
|
|
18
22
|
import { TaskService } from './task.js';
|
|
19
23
|
import { ListService } from './list.js';
|
|
20
24
|
import { FolderService } from './folder.js';
|
|
25
|
+
import { ClickUpTagService } from './tag.js';
|
|
21
26
|
import { Logger } from '../../logger.js';
|
|
22
27
|
// Singleton logger for ClickUp services
|
|
23
28
|
const logger = new Logger('ClickUpServices');
|
|
@@ -43,11 +48,14 @@ export function createClickUpServices(config) {
|
|
|
43
48
|
const listService = new ListService(apiKey, teamId, baseUrl, workspaceService);
|
|
44
49
|
logger.info('Initializing ClickUp Folder service');
|
|
45
50
|
const folderService = new FolderService(apiKey, teamId, baseUrl, workspaceService);
|
|
51
|
+
logger.info('Initializing ClickUp Tag service');
|
|
52
|
+
const tagService = new ClickUpTagService(apiKey, teamId, baseUrl);
|
|
46
53
|
const services = {
|
|
47
54
|
workspace: workspaceService,
|
|
48
55
|
task: taskService,
|
|
49
56
|
list: listService,
|
|
50
|
-
folder: folderService
|
|
57
|
+
folder: folderService,
|
|
58
|
+
tag: tagService
|
|
51
59
|
};
|
|
52
60
|
// Log successful completion
|
|
53
61
|
logger.info('All ClickUp services initialized successfully', {
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPDX-FileCopyrightText: © 2025 Talib Kareem <taazkareem@icloud.com>
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*
|
|
5
|
+
* ClickUp Tag Service
|
|
6
|
+
*
|
|
7
|
+
* Provides access to ClickUp API endpoints for tag management:
|
|
8
|
+
* - Space tags (get, create, update, delete)
|
|
9
|
+
* - Task tags (add, remove)
|
|
10
|
+
*/
|
|
11
|
+
import { BaseClickUpService } from './base.js';
|
|
12
|
+
/**
|
|
13
|
+
* ClickUp Tag Service class for managing tags
|
|
14
|
+
*/
|
|
15
|
+
export class ClickUpTagService extends BaseClickUpService {
|
|
16
|
+
/**
|
|
17
|
+
* Get all tags in a space
|
|
18
|
+
* @param spaceId - ID of the space to get tags from
|
|
19
|
+
* @returns Promise with tags array
|
|
20
|
+
*/
|
|
21
|
+
async getSpaceTags(spaceId) {
|
|
22
|
+
try {
|
|
23
|
+
this.logger.debug(`Getting tags for space: ${spaceId}`);
|
|
24
|
+
const response = await this.client.get(`/space/${spaceId}/tag`);
|
|
25
|
+
return {
|
|
26
|
+
success: true,
|
|
27
|
+
data: response.data.tags
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
this.logger.error(`Failed to get tags for space: ${spaceId}`, error);
|
|
32
|
+
return {
|
|
33
|
+
success: false,
|
|
34
|
+
error: {
|
|
35
|
+
message: error.message || 'Failed to get space tags',
|
|
36
|
+
code: error.code,
|
|
37
|
+
details: error.data
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Create a new tag in a space
|
|
44
|
+
* @param spaceId - ID of the space
|
|
45
|
+
* @param tagData - Tag data (name, background color, foreground color)
|
|
46
|
+
* @returns Promise with created tag
|
|
47
|
+
*/
|
|
48
|
+
async createSpaceTag(spaceId, tagData) {
|
|
49
|
+
try {
|
|
50
|
+
this.logger.debug(`Creating tag "${tagData.tag_name}" in space: ${spaceId}`);
|
|
51
|
+
// Send tag data wrapped in a 'tag' object
|
|
52
|
+
const response = await this.client.post(`/space/${spaceId}/tag`, {
|
|
53
|
+
tag: {
|
|
54
|
+
name: tagData.tag_name,
|
|
55
|
+
tag_bg: tagData.tag_bg,
|
|
56
|
+
tag_fg: tagData.tag_fg
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
return {
|
|
60
|
+
success: true,
|
|
61
|
+
data: response.data.tag
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
this.logger.error(`Failed to create tag in space: ${spaceId}`, error);
|
|
66
|
+
return {
|
|
67
|
+
success: false,
|
|
68
|
+
error: {
|
|
69
|
+
message: error.message || 'Failed to create space tag',
|
|
70
|
+
code: error.code,
|
|
71
|
+
details: error.data
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Update an existing tag in a space
|
|
78
|
+
* @param spaceId - ID of the space
|
|
79
|
+
* @param tagName - Current name of the tag to update
|
|
80
|
+
* @param updateData - Tag data to update (name, colors)
|
|
81
|
+
* @returns Promise with updated tag
|
|
82
|
+
*/
|
|
83
|
+
async updateSpaceTag(spaceId, tagName, updateData) {
|
|
84
|
+
try {
|
|
85
|
+
this.logger.debug(`Updating tag "${tagName}" in space: ${spaceId}`);
|
|
86
|
+
// Encode the tag name in the URL
|
|
87
|
+
const encodedTagName = encodeURIComponent(tagName);
|
|
88
|
+
const response = await this.client.put(`/space/${spaceId}/tag/${encodedTagName}`, updateData);
|
|
89
|
+
return {
|
|
90
|
+
success: true,
|
|
91
|
+
data: response.data.tag
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
this.logger.error(`Failed to update tag "${tagName}" in space: ${spaceId}`, error);
|
|
96
|
+
return {
|
|
97
|
+
success: false,
|
|
98
|
+
error: {
|
|
99
|
+
message: error.message || 'Failed to update space tag',
|
|
100
|
+
code: error.code,
|
|
101
|
+
details: error.data
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Delete a tag from a space
|
|
108
|
+
* @param spaceId - ID of the space
|
|
109
|
+
* @param tagName - Name of the tag to delete
|
|
110
|
+
* @returns Promise with success status
|
|
111
|
+
*/
|
|
112
|
+
async deleteSpaceTag(spaceId, tagName) {
|
|
113
|
+
try {
|
|
114
|
+
this.logger.debug(`Deleting tag "${tagName}" from space: ${spaceId}`);
|
|
115
|
+
// Encode the tag name in the URL
|
|
116
|
+
const encodedTagName = encodeURIComponent(tagName);
|
|
117
|
+
await this.client.delete(`/space/${spaceId}/tag/${encodedTagName}`);
|
|
118
|
+
return {
|
|
119
|
+
success: true
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
this.logger.error(`Failed to delete tag "${tagName}" from space: ${spaceId}`, error);
|
|
124
|
+
return {
|
|
125
|
+
success: false,
|
|
126
|
+
error: {
|
|
127
|
+
message: error.message || 'Failed to delete space tag',
|
|
128
|
+
code: error.code,
|
|
129
|
+
details: error.data
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Add a tag to a task
|
|
136
|
+
* @param taskId - ID of the task
|
|
137
|
+
* @param tagName - Name of the tag to add
|
|
138
|
+
* @returns Promise with success status
|
|
139
|
+
*/
|
|
140
|
+
async addTagToTask(taskId, tagName) {
|
|
141
|
+
try {
|
|
142
|
+
this.logger.debug(`Adding tag "${tagName}" to task: ${taskId}`);
|
|
143
|
+
// Encode the tag name in the URL
|
|
144
|
+
const encodedTagName = encodeURIComponent(tagName);
|
|
145
|
+
await this.client.post(`/task/${taskId}/tag/${encodedTagName}`, {});
|
|
146
|
+
return {
|
|
147
|
+
success: true
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
this.logger.error(`Failed to add tag "${tagName}" to task: ${taskId}`, error);
|
|
152
|
+
return {
|
|
153
|
+
success: false,
|
|
154
|
+
error: {
|
|
155
|
+
message: error.message || 'Failed to add tag to task',
|
|
156
|
+
code: error.code,
|
|
157
|
+
details: error.data
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Remove a tag from a task
|
|
164
|
+
* @param taskId - ID of the task
|
|
165
|
+
* @param tagName - Name of the tag to remove
|
|
166
|
+
* @returns Promise with success status
|
|
167
|
+
*/
|
|
168
|
+
async removeTagFromTask(taskId, tagName) {
|
|
169
|
+
try {
|
|
170
|
+
this.logger.debug(`Removing tag "${tagName}" from task: ${taskId}`);
|
|
171
|
+
// Encode the tag name in the URL
|
|
172
|
+
const encodedTagName = encodeURIComponent(tagName);
|
|
173
|
+
await this.client.delete(`/task/${taskId}/tag/${encodedTagName}`);
|
|
174
|
+
return {
|
|
175
|
+
success: true
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
this.logger.error(`Failed to remove tag "${tagName}" from task: ${taskId}`, error);
|
|
180
|
+
return {
|
|
181
|
+
success: false,
|
|
182
|
+
error: {
|
|
183
|
+
message: error.message || 'Failed to remove tag from task',
|
|
184
|
+
code: error.code,
|
|
185
|
+
details: error.data
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|