@alanse/clickup-multi-mcp-server 1.0.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/Dockerfile +38 -0
- package/LICENSE +21 -0
- package/README.md +470 -0
- package/build/config.js +237 -0
- package/build/index.js +87 -0
- package/build/logger.js +163 -0
- package/build/middleware/security.js +231 -0
- package/build/server.js +288 -0
- package/build/services/clickup/base.js +432 -0
- package/build/services/clickup/bulk.js +180 -0
- package/build/services/clickup/document.js +159 -0
- package/build/services/clickup/folder.js +136 -0
- package/build/services/clickup/index.js +76 -0
- package/build/services/clickup/list.js +191 -0
- package/build/services/clickup/tag.js +239 -0
- package/build/services/clickup/task/index.js +32 -0
- package/build/services/clickup/task/task-attachments.js +105 -0
- package/build/services/clickup/task/task-comments.js +114 -0
- package/build/services/clickup/task/task-core.js +604 -0
- package/build/services/clickup/task/task-custom-fields.js +107 -0
- package/build/services/clickup/task/task-search.js +986 -0
- package/build/services/clickup/task/task-service.js +104 -0
- package/build/services/clickup/task/task-tags.js +113 -0
- package/build/services/clickup/time.js +244 -0
- package/build/services/clickup/types.js +33 -0
- package/build/services/clickup/workspace.js +397 -0
- package/build/services/shared.js +61 -0
- package/build/sse_server.js +277 -0
- package/build/tools/documents.js +489 -0
- package/build/tools/folder.js +331 -0
- package/build/tools/index.js +16 -0
- package/build/tools/list.js +428 -0
- package/build/tools/member.js +106 -0
- package/build/tools/tag.js +833 -0
- package/build/tools/task/attachments.js +357 -0
- package/build/tools/task/attachments.types.js +9 -0
- package/build/tools/task/bulk-operations.js +338 -0
- package/build/tools/task/handlers.js +919 -0
- package/build/tools/task/index.js +30 -0
- package/build/tools/task/main.js +233 -0
- package/build/tools/task/single-operations.js +469 -0
- package/build/tools/task/time-tracking.js +575 -0
- package/build/tools/task/utilities.js +310 -0
- package/build/tools/task/workspace-operations.js +258 -0
- package/build/tools/tool-enhancer.js +37 -0
- package/build/tools/utils.js +12 -0
- package/build/tools/workspace-helper.js +44 -0
- package/build/tools/workspace.js +73 -0
- package/build/utils/color-processor.js +183 -0
- package/build/utils/concurrency-utils.js +248 -0
- package/build/utils/date-utils.js +542 -0
- package/build/utils/resolver-utils.js +135 -0
- package/build/utils/sponsor-service.js +93 -0
- package/build/utils/token-utils.js +49 -0
- package/package.json +77 -0
- package/smithery.yaml +23 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPDX-FileCopyrightText: © 2025 Talib Kareem <taazkareem@icloud.com>
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*
|
|
5
|
+
* ClickUp MCP Folder Tools
|
|
6
|
+
*
|
|
7
|
+
* This module defines folder-related tools for creating, retrieving,
|
|
8
|
+
* updating, and deleting folders in the ClickUp workspace hierarchy.
|
|
9
|
+
*/
|
|
10
|
+
import { clickUpServices } from '../services/shared.js';
|
|
11
|
+
import { sponsorService } from '../utils/sponsor-service.js';
|
|
12
|
+
// Use shared services instance
|
|
13
|
+
const { folder: folderService, workspace: workspaceService } = clickUpServices;
|
|
14
|
+
/**
|
|
15
|
+
* Tool definition for creating a folder
|
|
16
|
+
*/
|
|
17
|
+
export const createFolderTool = {
|
|
18
|
+
name: "create_folder",
|
|
19
|
+
description: `Creates folder in ClickUp space. Use spaceId (preferred) or spaceName + folder name. Optional: override_statuses for folder-specific statuses. Use create_list_in_folder to add lists after creation.`,
|
|
20
|
+
inputSchema: {
|
|
21
|
+
type: "object",
|
|
22
|
+
properties: {
|
|
23
|
+
name: {
|
|
24
|
+
type: "string",
|
|
25
|
+
description: "Name of the folder"
|
|
26
|
+
},
|
|
27
|
+
spaceId: {
|
|
28
|
+
type: "string",
|
|
29
|
+
description: "ID of the space to create the folder in (preferred). Use this instead of spaceName if you have it."
|
|
30
|
+
},
|
|
31
|
+
spaceName: {
|
|
32
|
+
type: "string",
|
|
33
|
+
description: "Name of the space to create the folder in. Only use if you don't have spaceId."
|
|
34
|
+
},
|
|
35
|
+
override_statuses: {
|
|
36
|
+
type: "boolean",
|
|
37
|
+
description: "Whether to override space statuses with folder-specific statuses"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
required: ["name"]
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Tool definition for retrieving folder details
|
|
45
|
+
*/
|
|
46
|
+
export const getFolderTool = {
|
|
47
|
+
name: "get_folder",
|
|
48
|
+
description: `Gets folder details. Use folderId (preferred) or folderName + (spaceId/spaceName). Helps understand folder structure before creating/updating lists.`,
|
|
49
|
+
inputSchema: {
|
|
50
|
+
type: "object",
|
|
51
|
+
properties: {
|
|
52
|
+
folderId: {
|
|
53
|
+
type: "string",
|
|
54
|
+
description: "ID of folder to retrieve (preferred). Use this instead of folderName if you have it."
|
|
55
|
+
},
|
|
56
|
+
folderName: {
|
|
57
|
+
type: "string",
|
|
58
|
+
description: "Name of folder to retrieve. When using this, you MUST also provide spaceId or spaceName."
|
|
59
|
+
},
|
|
60
|
+
spaceId: {
|
|
61
|
+
type: "string",
|
|
62
|
+
description: "ID of space containing the folder (required with folderName). Use this instead of spaceName if you have it."
|
|
63
|
+
},
|
|
64
|
+
spaceName: {
|
|
65
|
+
type: "string",
|
|
66
|
+
description: "Name of space containing the folder (required with folderName). Only use if you don't have spaceId."
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
required: []
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Tool definition for updating a folder
|
|
74
|
+
*/
|
|
75
|
+
export const updateFolderTool = {
|
|
76
|
+
name: "update_folder",
|
|
77
|
+
description: `Updates folder properties. Use folderId (preferred) or folderName + (spaceId/spaceName). At least one update field (name/override_statuses) required. Changes apply to all lists in folder.`,
|
|
78
|
+
inputSchema: {
|
|
79
|
+
type: "object",
|
|
80
|
+
properties: {
|
|
81
|
+
folderId: {
|
|
82
|
+
type: "string",
|
|
83
|
+
description: "ID of folder to update (preferred). Use this instead of folderName if you have it."
|
|
84
|
+
},
|
|
85
|
+
folderName: {
|
|
86
|
+
type: "string",
|
|
87
|
+
description: "Name of folder to update. When using this, you MUST also provide spaceId or spaceName."
|
|
88
|
+
},
|
|
89
|
+
spaceId: {
|
|
90
|
+
type: "string",
|
|
91
|
+
description: "ID of space containing the folder (required with folderName). Use this instead of spaceName if you have it."
|
|
92
|
+
},
|
|
93
|
+
spaceName: {
|
|
94
|
+
type: "string",
|
|
95
|
+
description: "Name of space containing the folder (required with folderName). Only use if you don't have spaceId."
|
|
96
|
+
},
|
|
97
|
+
name: {
|
|
98
|
+
type: "string",
|
|
99
|
+
description: "New name for the folder"
|
|
100
|
+
},
|
|
101
|
+
override_statuses: {
|
|
102
|
+
type: "boolean",
|
|
103
|
+
description: "Whether to override space statuses with folder-specific statuses"
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
required: []
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* Tool definition for deleting a folder
|
|
111
|
+
*/
|
|
112
|
+
export const deleteFolderTool = {
|
|
113
|
+
name: "delete_folder",
|
|
114
|
+
description: `PERMANENTLY deletes folder and all contents. Use folderId (preferred/safest) or folderName + (spaceId/spaceName). WARNING: Cannot be undone, all lists/tasks deleted, folderName risky if not unique.`,
|
|
115
|
+
inputSchema: {
|
|
116
|
+
type: "object",
|
|
117
|
+
properties: {
|
|
118
|
+
folderId: {
|
|
119
|
+
type: "string",
|
|
120
|
+
description: "ID of folder to delete (preferred). Use this instead of folderName for safety."
|
|
121
|
+
},
|
|
122
|
+
folderName: {
|
|
123
|
+
type: "string",
|
|
124
|
+
description: "Name of folder to delete. When using this, you MUST also provide spaceId or spaceName."
|
|
125
|
+
},
|
|
126
|
+
spaceId: {
|
|
127
|
+
type: "string",
|
|
128
|
+
description: "ID of space containing the folder (required with folderName). Use this instead of spaceName if you have it."
|
|
129
|
+
},
|
|
130
|
+
spaceName: {
|
|
131
|
+
type: "string",
|
|
132
|
+
description: "Name of space containing the folder (required with folderName). Only use if you don't have spaceId."
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
required: []
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
/**
|
|
139
|
+
* Handler for the create_folder tool
|
|
140
|
+
* Creates a new folder in a space
|
|
141
|
+
*/
|
|
142
|
+
export async function handleCreateFolder(parameters) {
|
|
143
|
+
const { name, spaceId, spaceName, override_statuses } = parameters;
|
|
144
|
+
// Validate required fields
|
|
145
|
+
if (!name) {
|
|
146
|
+
throw new Error("Folder name is required");
|
|
147
|
+
}
|
|
148
|
+
let targetSpaceId = spaceId;
|
|
149
|
+
// If no spaceId but spaceName is provided, look up the space ID
|
|
150
|
+
if (!targetSpaceId && spaceName) {
|
|
151
|
+
const spaceIdResult = await workspaceService.findSpaceByName(spaceName);
|
|
152
|
+
if (!spaceIdResult) {
|
|
153
|
+
throw new Error(`Space "${spaceName}" not found`);
|
|
154
|
+
}
|
|
155
|
+
targetSpaceId = spaceIdResult.id;
|
|
156
|
+
}
|
|
157
|
+
if (!targetSpaceId) {
|
|
158
|
+
throw new Error("Either spaceId or spaceName must be provided");
|
|
159
|
+
}
|
|
160
|
+
// Prepare folder data
|
|
161
|
+
const folderData = {
|
|
162
|
+
name
|
|
163
|
+
};
|
|
164
|
+
// Add optional fields if provided
|
|
165
|
+
if (override_statuses !== undefined)
|
|
166
|
+
folderData.override_statuses = override_statuses;
|
|
167
|
+
try {
|
|
168
|
+
// Create the folder
|
|
169
|
+
const newFolder = await folderService.createFolder(targetSpaceId, folderData);
|
|
170
|
+
return sponsorService.createResponse({
|
|
171
|
+
id: newFolder.id,
|
|
172
|
+
name: newFolder.name,
|
|
173
|
+
space: {
|
|
174
|
+
id: newFolder.space.id,
|
|
175
|
+
name: newFolder.space.name
|
|
176
|
+
},
|
|
177
|
+
message: `Folder "${newFolder.name}" created successfully`
|
|
178
|
+
}, true);
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
return sponsorService.createErrorResponse(`Failed to create folder: ${error.message}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Handler for the get_folder tool
|
|
186
|
+
* Retrieves details about a specific folder
|
|
187
|
+
*/
|
|
188
|
+
export async function handleGetFolder(parameters) {
|
|
189
|
+
const { folderId, folderName, spaceId, spaceName } = parameters;
|
|
190
|
+
let targetFolderId = folderId;
|
|
191
|
+
// If no folderId provided but folderName is, look up the folder ID
|
|
192
|
+
if (!targetFolderId && folderName) {
|
|
193
|
+
let targetSpaceId = spaceId;
|
|
194
|
+
// If no spaceId provided but spaceName is, look up the space ID first
|
|
195
|
+
if (!targetSpaceId && spaceName) {
|
|
196
|
+
const spaceIdResult = await workspaceService.findSpaceByName(spaceName);
|
|
197
|
+
if (!spaceIdResult) {
|
|
198
|
+
throw new Error(`Space "${spaceName}" not found`);
|
|
199
|
+
}
|
|
200
|
+
targetSpaceId = spaceIdResult.id;
|
|
201
|
+
}
|
|
202
|
+
if (!targetSpaceId) {
|
|
203
|
+
throw new Error("Either spaceId or spaceName must be provided when using folderName");
|
|
204
|
+
}
|
|
205
|
+
const folderResult = await folderService.findFolderByName(targetSpaceId, folderName);
|
|
206
|
+
if (!folderResult) {
|
|
207
|
+
throw new Error(`Folder "${folderName}" not found in space`);
|
|
208
|
+
}
|
|
209
|
+
targetFolderId = folderResult.id;
|
|
210
|
+
}
|
|
211
|
+
if (!targetFolderId) {
|
|
212
|
+
throw new Error("Either folderId or folderName must be provided");
|
|
213
|
+
}
|
|
214
|
+
try {
|
|
215
|
+
// Get the folder
|
|
216
|
+
const folder = await folderService.getFolder(targetFolderId);
|
|
217
|
+
return sponsorService.createResponse({
|
|
218
|
+
id: folder.id,
|
|
219
|
+
name: folder.name,
|
|
220
|
+
space: {
|
|
221
|
+
id: folder.space.id,
|
|
222
|
+
name: folder.space.name
|
|
223
|
+
}
|
|
224
|
+
}, true);
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
return sponsorService.createErrorResponse(`Failed to retrieve folder: ${error.message}`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Handler for the update_folder tool
|
|
232
|
+
* Updates an existing folder's properties
|
|
233
|
+
*/
|
|
234
|
+
export async function handleUpdateFolder(parameters) {
|
|
235
|
+
const { folderId, folderName, name, override_statuses, spaceId, spaceName } = parameters;
|
|
236
|
+
let targetFolderId = folderId;
|
|
237
|
+
// If no folderId provided but folderName is, look up the folder ID
|
|
238
|
+
if (!targetFolderId && folderName) {
|
|
239
|
+
let targetSpaceId = spaceId;
|
|
240
|
+
// If no spaceId provided but spaceName is, look up the space ID first
|
|
241
|
+
if (!targetSpaceId && spaceName) {
|
|
242
|
+
const spaceIdResult = await workspaceService.findSpaceByName(spaceName);
|
|
243
|
+
if (!spaceIdResult) {
|
|
244
|
+
throw new Error(`Space "${spaceName}" not found`);
|
|
245
|
+
}
|
|
246
|
+
targetSpaceId = spaceIdResult.id;
|
|
247
|
+
}
|
|
248
|
+
if (!targetSpaceId) {
|
|
249
|
+
throw new Error("Either spaceId or spaceName must be provided when using folderName");
|
|
250
|
+
}
|
|
251
|
+
const folderResult = await folderService.findFolderByName(targetSpaceId, folderName);
|
|
252
|
+
if (!folderResult) {
|
|
253
|
+
throw new Error(`Folder "${folderName}" not found in space`);
|
|
254
|
+
}
|
|
255
|
+
targetFolderId = folderResult.id;
|
|
256
|
+
}
|
|
257
|
+
if (!targetFolderId) {
|
|
258
|
+
throw new Error("Either folderId or folderName must be provided");
|
|
259
|
+
}
|
|
260
|
+
// Ensure at least one update field is provided
|
|
261
|
+
if (!name && override_statuses === undefined) {
|
|
262
|
+
throw new Error("At least one of name or override_statuses must be provided for update");
|
|
263
|
+
}
|
|
264
|
+
// Prepare update data
|
|
265
|
+
const updateData = {};
|
|
266
|
+
if (name)
|
|
267
|
+
updateData.name = name;
|
|
268
|
+
if (override_statuses !== undefined)
|
|
269
|
+
updateData.override_statuses = override_statuses;
|
|
270
|
+
try {
|
|
271
|
+
// Update the folder
|
|
272
|
+
const updatedFolder = await folderService.updateFolder(targetFolderId, updateData);
|
|
273
|
+
return sponsorService.createResponse({
|
|
274
|
+
id: updatedFolder.id,
|
|
275
|
+
name: updatedFolder.name,
|
|
276
|
+
space: {
|
|
277
|
+
id: updatedFolder.space.id,
|
|
278
|
+
name: updatedFolder.space.name
|
|
279
|
+
},
|
|
280
|
+
message: `Folder "${updatedFolder.name}" updated successfully`
|
|
281
|
+
}, true);
|
|
282
|
+
}
|
|
283
|
+
catch (error) {
|
|
284
|
+
return sponsorService.createErrorResponse(`Failed to update folder: ${error.message}`);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Handler for the delete_folder tool
|
|
289
|
+
* Permanently removes a folder from the workspace
|
|
290
|
+
*/
|
|
291
|
+
export async function handleDeleteFolder(parameters) {
|
|
292
|
+
const { folderId, folderName, spaceId, spaceName } = parameters;
|
|
293
|
+
let targetFolderId = folderId;
|
|
294
|
+
// If no folderId provided but folderName is, look up the folder ID
|
|
295
|
+
if (!targetFolderId && folderName) {
|
|
296
|
+
let targetSpaceId = spaceId;
|
|
297
|
+
// If no spaceId provided but spaceName is, look up the space ID first
|
|
298
|
+
if (!targetSpaceId && spaceName) {
|
|
299
|
+
const spaceIdResult = await workspaceService.findSpaceByName(spaceName);
|
|
300
|
+
if (!spaceIdResult) {
|
|
301
|
+
throw new Error(`Space "${spaceName}" not found`);
|
|
302
|
+
}
|
|
303
|
+
targetSpaceId = spaceIdResult.id;
|
|
304
|
+
}
|
|
305
|
+
if (!targetSpaceId) {
|
|
306
|
+
throw new Error("Either spaceId or spaceName must be provided when using folderName");
|
|
307
|
+
}
|
|
308
|
+
const folderResult = await folderService.findFolderByName(targetSpaceId, folderName);
|
|
309
|
+
if (!folderResult) {
|
|
310
|
+
throw new Error(`Folder "${folderName}" not found in space`);
|
|
311
|
+
}
|
|
312
|
+
targetFolderId = folderResult.id;
|
|
313
|
+
}
|
|
314
|
+
if (!targetFolderId) {
|
|
315
|
+
throw new Error("Either folderId or folderName must be provided");
|
|
316
|
+
}
|
|
317
|
+
try {
|
|
318
|
+
// Get folder details before deletion for confirmation message
|
|
319
|
+
const folder = await folderService.getFolder(targetFolderId);
|
|
320
|
+
const folderName = folder.name;
|
|
321
|
+
// Delete the folder
|
|
322
|
+
await folderService.deleteFolder(targetFolderId);
|
|
323
|
+
return sponsorService.createResponse({
|
|
324
|
+
success: true,
|
|
325
|
+
message: `Folder "${folderName || targetFolderId}" deleted successfully`
|
|
326
|
+
}, true);
|
|
327
|
+
}
|
|
328
|
+
catch (error) {
|
|
329
|
+
return sponsorService.createErrorResponse(`Failed to delete folder: ${error.message}`);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPDX-FileCopyrightText: © 2025 Talib Kareem <taazkareem@icloud.com>
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*
|
|
5
|
+
* Tools Index
|
|
6
|
+
*
|
|
7
|
+
* This file exports all tool definitions and handlers for the ClickUp MCP server.
|
|
8
|
+
* Each tool category (workspace, task, list, folder) is organized into its own module
|
|
9
|
+
* for better maintainability.
|
|
10
|
+
*/
|
|
11
|
+
export * from './workspace.js';
|
|
12
|
+
export * from './task/index.js';
|
|
13
|
+
export * from './list.js';
|
|
14
|
+
export * from './folder.js';
|
|
15
|
+
export * from './tag.js';
|
|
16
|
+
export * from './member.js';
|