@taazkareem/clickup-mcp-server 0.4.58 → 0.4.62
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 +11 -2
- package/build/config.js +10 -0
- package/build/index.js +9 -1482
- package/build/server.js +120 -0
- package/build/services/clickup/base.js +253 -0
- package/build/services/clickup/bulk.js +116 -0
- package/build/services/clickup/folder.js +133 -0
- package/build/services/clickup/index.js +43 -0
- package/build/services/clickup/initialization.js +28 -0
- package/build/services/clickup/list.js +188 -0
- package/build/services/clickup/task.js +492 -0
- package/build/services/clickup/types.js +4 -0
- package/build/services/clickup/workspace.js +314 -0
- package/build/services/shared.js +15 -0
- package/build/tools/folder.js +356 -0
- package/build/tools/index.js +11 -0
- package/build/tools/list.js +452 -0
- package/build/tools/task.js +1486 -0
- package/build/tools/utils.js +95 -0
- package/build/tools/workspace.js +132 -0
- package/package.json +3 -1
- package/build/services/clickup.js +0 -768
- package/build/types/clickup.js +0 -1
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ClickUp MCP List Tools
|
|
3
|
+
*
|
|
4
|
+
* This module defines list-related tools including creating, updating,
|
|
5
|
+
* retrieving, and deleting lists. It supports creating lists both in spaces
|
|
6
|
+
* and in folders.
|
|
7
|
+
*/
|
|
8
|
+
import { createClickUpServices } from '../services/clickup/index.js';
|
|
9
|
+
import config from '../config.js';
|
|
10
|
+
// Initialize ClickUp services using the factory function
|
|
11
|
+
const services = createClickUpServices({
|
|
12
|
+
apiKey: config.clickupApiKey,
|
|
13
|
+
teamId: config.clickupTeamId
|
|
14
|
+
});
|
|
15
|
+
// Extract the services we need for list operations
|
|
16
|
+
const { list: listService, workspace: workspaceService } = services;
|
|
17
|
+
/**
|
|
18
|
+
* Tool definition for creating a list directly in a space
|
|
19
|
+
*/
|
|
20
|
+
export const createListTool = {
|
|
21
|
+
name: "create_list",
|
|
22
|
+
description: "Create a new list directly in a ClickUp space (not in a folder). You MUST provide either spaceId or spaceName. For creating lists inside folders, use create_list_in_folder instead.",
|
|
23
|
+
inputSchema: {
|
|
24
|
+
type: "object",
|
|
25
|
+
properties: {
|
|
26
|
+
name: {
|
|
27
|
+
type: "string",
|
|
28
|
+
description: "Name of the list"
|
|
29
|
+
},
|
|
30
|
+
spaceId: {
|
|
31
|
+
type: "string",
|
|
32
|
+
description: "ID of the space to create the list in. Use this instead of spaceName if you have the ID."
|
|
33
|
+
},
|
|
34
|
+
spaceName: {
|
|
35
|
+
type: "string",
|
|
36
|
+
description: "Name of the space to create the list in. Alternative to spaceId - one of them MUST be provided."
|
|
37
|
+
},
|
|
38
|
+
content: {
|
|
39
|
+
type: "string",
|
|
40
|
+
description: "Description or content of the list"
|
|
41
|
+
},
|
|
42
|
+
dueDate: {
|
|
43
|
+
type: "string",
|
|
44
|
+
description: "Due date for the list (Unix timestamp in milliseconds)"
|
|
45
|
+
},
|
|
46
|
+
priority: {
|
|
47
|
+
type: "number",
|
|
48
|
+
description: "Priority level: 1 (urgent), 2 (high), 3 (normal), 4 (low)"
|
|
49
|
+
},
|
|
50
|
+
assignee: {
|
|
51
|
+
type: "number",
|
|
52
|
+
description: "User ID to assign the list to"
|
|
53
|
+
},
|
|
54
|
+
status: {
|
|
55
|
+
type: "string",
|
|
56
|
+
description: "Status of the list"
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
required: ["name"]
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Tool definition for creating a list within a folder
|
|
64
|
+
*/
|
|
65
|
+
export const createListInFolderTool = {
|
|
66
|
+
name: "create_list_in_folder",
|
|
67
|
+
description: "Create a new list within a ClickUp folder. You MUST provide either: 1) folderId alone, or 2) folderName WITH either spaceName or spaceId. Folder names may not be unique across spaces, which is why space information is required when using folderName.",
|
|
68
|
+
inputSchema: {
|
|
69
|
+
type: "object",
|
|
70
|
+
properties: {
|
|
71
|
+
name: {
|
|
72
|
+
type: "string",
|
|
73
|
+
description: "Name of the list"
|
|
74
|
+
},
|
|
75
|
+
folderId: {
|
|
76
|
+
type: "string",
|
|
77
|
+
description: "ID of the folder to create the list in. If you have this, you don't need folderName or space information."
|
|
78
|
+
},
|
|
79
|
+
folderName: {
|
|
80
|
+
type: "string",
|
|
81
|
+
description: "Name of the folder to create the list in. When using this, you MUST also provide either spaceName or spaceId."
|
|
82
|
+
},
|
|
83
|
+
spaceId: {
|
|
84
|
+
type: "string",
|
|
85
|
+
description: "ID of the space containing the folder. Required when using folderName instead of folderId."
|
|
86
|
+
},
|
|
87
|
+
spaceName: {
|
|
88
|
+
type: "string",
|
|
89
|
+
description: "Name of the space containing the folder. Required when using folderName instead of folderId."
|
|
90
|
+
},
|
|
91
|
+
content: {
|
|
92
|
+
type: "string",
|
|
93
|
+
description: "Description or content of the list"
|
|
94
|
+
},
|
|
95
|
+
status: {
|
|
96
|
+
type: "string",
|
|
97
|
+
description: "Status of the list (uses folder default if not specified)"
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
required: ["name"]
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Tool definition for retrieving list details
|
|
105
|
+
*/
|
|
106
|
+
export const getListTool = {
|
|
107
|
+
name: "get_list",
|
|
108
|
+
description: "Retrieve details about a specific ClickUp list. You MUST provide either listId or listName. Using listId is more reliable as list names might not be unique.",
|
|
109
|
+
inputSchema: {
|
|
110
|
+
type: "object",
|
|
111
|
+
properties: {
|
|
112
|
+
listId: {
|
|
113
|
+
type: "string",
|
|
114
|
+
description: "ID of the list to retrieve. Use this instead of listName if you have the ID."
|
|
115
|
+
},
|
|
116
|
+
listName: {
|
|
117
|
+
type: "string",
|
|
118
|
+
description: "Name of the list to retrieve. May be ambiguous if multiple lists have the same name."
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
required: []
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* Tool definition for updating a list
|
|
126
|
+
*/
|
|
127
|
+
export const updateListTool = {
|
|
128
|
+
name: "update_list",
|
|
129
|
+
description: "Update an existing ClickUp list's properties. You MUST provide either listId or listName, and at least one field to update (name, content, or status).",
|
|
130
|
+
inputSchema: {
|
|
131
|
+
type: "object",
|
|
132
|
+
properties: {
|
|
133
|
+
listId: {
|
|
134
|
+
type: "string",
|
|
135
|
+
description: "ID of the list to update. Use this instead of listName if you have the ID."
|
|
136
|
+
},
|
|
137
|
+
listName: {
|
|
138
|
+
type: "string",
|
|
139
|
+
description: "Name of the list to update. May be ambiguous if multiple lists have the same name."
|
|
140
|
+
},
|
|
141
|
+
name: {
|
|
142
|
+
type: "string",
|
|
143
|
+
description: "New name for the list"
|
|
144
|
+
},
|
|
145
|
+
content: {
|
|
146
|
+
type: "string",
|
|
147
|
+
description: "New description or content for the list"
|
|
148
|
+
},
|
|
149
|
+
status: {
|
|
150
|
+
type: "string",
|
|
151
|
+
description: "New status for the list"
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
required: []
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
/**
|
|
158
|
+
* Tool definition for deleting a list
|
|
159
|
+
*/
|
|
160
|
+
export const deleteListTool = {
|
|
161
|
+
name: "delete_list",
|
|
162
|
+
description: "Permanently delete a ClickUp list and all its tasks. You MUST provide either listId or listName. WARNING: This action cannot be undone.",
|
|
163
|
+
inputSchema: {
|
|
164
|
+
type: "object",
|
|
165
|
+
properties: {
|
|
166
|
+
listId: {
|
|
167
|
+
type: "string",
|
|
168
|
+
description: "ID of the list to delete. Use this instead of listName if you have the ID."
|
|
169
|
+
},
|
|
170
|
+
listName: {
|
|
171
|
+
type: "string",
|
|
172
|
+
description: "Name of the list to delete. May be ambiguous if multiple lists have the same name."
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
required: []
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* Helper function to find a list ID by name
|
|
180
|
+
* Uses the ClickUp service's global list search functionality
|
|
181
|
+
*/
|
|
182
|
+
export async function findListIDByName(workspaceService, listName) {
|
|
183
|
+
// Use workspace service to find the list in the hierarchy
|
|
184
|
+
const hierarchy = await workspaceService.getWorkspaceHierarchy();
|
|
185
|
+
const listInfo = workspaceService.findIDByNameInHierarchy(hierarchy, listName, 'list');
|
|
186
|
+
if (!listInfo)
|
|
187
|
+
return null;
|
|
188
|
+
return { id: listInfo.id, name: listName };
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Handler for the create_list tool
|
|
192
|
+
* Creates a new list directly in a space
|
|
193
|
+
*/
|
|
194
|
+
export async function handleCreateList(parameters) {
|
|
195
|
+
const { name, spaceId, spaceName, content, dueDate, priority, assignee, status } = parameters;
|
|
196
|
+
// Validate required fields
|
|
197
|
+
if (!name) {
|
|
198
|
+
throw new Error("List name is required");
|
|
199
|
+
}
|
|
200
|
+
let targetSpaceId = spaceId;
|
|
201
|
+
// If no spaceId but spaceName is provided, look up the space ID
|
|
202
|
+
if (!targetSpaceId && spaceName) {
|
|
203
|
+
const spaceIdResult = await workspaceService.findSpaceIDByName(spaceName);
|
|
204
|
+
if (!spaceIdResult) {
|
|
205
|
+
throw new Error(`Space "${spaceName}" not found`);
|
|
206
|
+
}
|
|
207
|
+
targetSpaceId = spaceIdResult;
|
|
208
|
+
}
|
|
209
|
+
if (!targetSpaceId) {
|
|
210
|
+
throw new Error("Either spaceId or spaceName must be provided");
|
|
211
|
+
}
|
|
212
|
+
// Prepare list data
|
|
213
|
+
const listData = {
|
|
214
|
+
name
|
|
215
|
+
};
|
|
216
|
+
// Add optional fields if provided
|
|
217
|
+
if (content)
|
|
218
|
+
listData.content = content;
|
|
219
|
+
if (dueDate)
|
|
220
|
+
listData.due_date = parseInt(dueDate);
|
|
221
|
+
if (priority)
|
|
222
|
+
listData.priority = priority;
|
|
223
|
+
if (assignee)
|
|
224
|
+
listData.assignee = assignee;
|
|
225
|
+
if (status)
|
|
226
|
+
listData.status = status;
|
|
227
|
+
try {
|
|
228
|
+
// Create the list
|
|
229
|
+
const newList = await listService.createList(targetSpaceId, listData);
|
|
230
|
+
return {
|
|
231
|
+
content: [{
|
|
232
|
+
type: "text",
|
|
233
|
+
text: JSON.stringify({
|
|
234
|
+
id: newList.id,
|
|
235
|
+
name: newList.name,
|
|
236
|
+
content: newList.content,
|
|
237
|
+
space: {
|
|
238
|
+
id: newList.space.id,
|
|
239
|
+
name: newList.space.name
|
|
240
|
+
},
|
|
241
|
+
message: `List "${newList.name}" created successfully`
|
|
242
|
+
}, null, 2)
|
|
243
|
+
}]
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
throw new Error(`Failed to create list: ${error.message}`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Handler for the create_list_in_folder tool
|
|
252
|
+
* Creates a new list inside a folder
|
|
253
|
+
*/
|
|
254
|
+
export async function handleCreateListInFolder(parameters) {
|
|
255
|
+
const { name, folderId, folderName, spaceId, spaceName, content, status } = parameters;
|
|
256
|
+
// Validate required fields
|
|
257
|
+
if (!name) {
|
|
258
|
+
throw new Error("List name is required");
|
|
259
|
+
}
|
|
260
|
+
let targetFolderId = folderId;
|
|
261
|
+
// If no folderId but folderName is provided, look up the folder ID
|
|
262
|
+
if (!targetFolderId && folderName) {
|
|
263
|
+
let targetSpaceId = spaceId;
|
|
264
|
+
// If no spaceId provided but spaceName is, look up the space ID first
|
|
265
|
+
if (!targetSpaceId && spaceName) {
|
|
266
|
+
const spaceIdResult = await workspaceService.findSpaceByName(spaceName);
|
|
267
|
+
if (!spaceIdResult) {
|
|
268
|
+
throw new Error(`Space "${spaceName}" not found`);
|
|
269
|
+
}
|
|
270
|
+
targetSpaceId = spaceIdResult.id;
|
|
271
|
+
}
|
|
272
|
+
if (!targetSpaceId) {
|
|
273
|
+
throw new Error("When using folderName to identify a folder, you must also provide either spaceId or spaceName to locate the correct folder. This is because folder names might not be unique across different spaces.");
|
|
274
|
+
}
|
|
275
|
+
// Find the folder in the workspace hierarchy
|
|
276
|
+
const hierarchy = await workspaceService.getWorkspaceHierarchy();
|
|
277
|
+
const folderInfo = workspaceService.findIDByNameInHierarchy(hierarchy, folderName, 'folder');
|
|
278
|
+
if (!folderInfo) {
|
|
279
|
+
throw new Error(`Folder "${folderName}" not found in space`);
|
|
280
|
+
}
|
|
281
|
+
targetFolderId = folderInfo.id;
|
|
282
|
+
}
|
|
283
|
+
if (!targetFolderId) {
|
|
284
|
+
throw new Error("Either folderId or folderName must be provided");
|
|
285
|
+
}
|
|
286
|
+
// Prepare list data
|
|
287
|
+
const listData = {
|
|
288
|
+
name
|
|
289
|
+
};
|
|
290
|
+
// Add optional fields if provided
|
|
291
|
+
if (content)
|
|
292
|
+
listData.content = content;
|
|
293
|
+
if (status)
|
|
294
|
+
listData.status = status;
|
|
295
|
+
try {
|
|
296
|
+
// Create the list in the folder
|
|
297
|
+
const newList = await listService.createListInFolder(targetFolderId, listData);
|
|
298
|
+
return {
|
|
299
|
+
content: [{
|
|
300
|
+
type: "text",
|
|
301
|
+
text: JSON.stringify({
|
|
302
|
+
id: newList.id,
|
|
303
|
+
name: newList.name,
|
|
304
|
+
content: newList.content,
|
|
305
|
+
space: {
|
|
306
|
+
id: newList.space.id,
|
|
307
|
+
name: newList.space.name
|
|
308
|
+
},
|
|
309
|
+
message: `List "${newList.name}" created successfully in folder`
|
|
310
|
+
}, null, 2)
|
|
311
|
+
}]
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
catch (error) {
|
|
315
|
+
throw new Error(`Failed to create list in folder: ${error.message}`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Handler for the get_list tool
|
|
320
|
+
* Retrieves details about a specific list
|
|
321
|
+
*/
|
|
322
|
+
export async function handleGetList(parameters) {
|
|
323
|
+
const { listId, listName } = parameters;
|
|
324
|
+
let targetListId = listId;
|
|
325
|
+
// If no listId provided but listName is, look up the list ID
|
|
326
|
+
if (!targetListId && listName) {
|
|
327
|
+
const listResult = await findListIDByName(workspaceService, listName);
|
|
328
|
+
if (!listResult) {
|
|
329
|
+
throw new Error(`List "${listName}" not found`);
|
|
330
|
+
}
|
|
331
|
+
targetListId = listResult.id;
|
|
332
|
+
}
|
|
333
|
+
if (!targetListId) {
|
|
334
|
+
throw new Error("Either listId or listName must be provided");
|
|
335
|
+
}
|
|
336
|
+
try {
|
|
337
|
+
// Get the list
|
|
338
|
+
const list = await listService.getList(targetListId);
|
|
339
|
+
return {
|
|
340
|
+
content: [{
|
|
341
|
+
type: "text",
|
|
342
|
+
text: JSON.stringify({
|
|
343
|
+
id: list.id,
|
|
344
|
+
name: list.name,
|
|
345
|
+
content: list.content,
|
|
346
|
+
space: {
|
|
347
|
+
id: list.space.id,
|
|
348
|
+
name: list.space.name
|
|
349
|
+
},
|
|
350
|
+
status: list.status,
|
|
351
|
+
url: `https://app.clickup.com/${config.clickupTeamId}/v/l/${list.id}`
|
|
352
|
+
}, null, 2)
|
|
353
|
+
}]
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
catch (error) {
|
|
357
|
+
throw new Error(`Failed to retrieve list: ${error.message}`);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Handler for the update_list tool
|
|
362
|
+
* Updates an existing list's properties
|
|
363
|
+
*/
|
|
364
|
+
export async function handleUpdateList(parameters) {
|
|
365
|
+
const { listId, listName, name, content, status } = parameters;
|
|
366
|
+
let targetListId = listId;
|
|
367
|
+
// If no listId provided but listName is, look up the list ID
|
|
368
|
+
if (!targetListId && listName) {
|
|
369
|
+
const listResult = await findListIDByName(workspaceService, listName);
|
|
370
|
+
if (!listResult) {
|
|
371
|
+
throw new Error(`List "${listName}" not found`);
|
|
372
|
+
}
|
|
373
|
+
targetListId = listResult.id;
|
|
374
|
+
}
|
|
375
|
+
if (!targetListId) {
|
|
376
|
+
throw new Error("Either listId or listName must be provided");
|
|
377
|
+
}
|
|
378
|
+
// Ensure at least one update field is provided
|
|
379
|
+
if (!name && !content && !status) {
|
|
380
|
+
throw new Error("At least one of name, content, or status must be provided for update");
|
|
381
|
+
}
|
|
382
|
+
// Prepare update data
|
|
383
|
+
const updateData = {};
|
|
384
|
+
if (name)
|
|
385
|
+
updateData.name = name;
|
|
386
|
+
if (content)
|
|
387
|
+
updateData.content = content;
|
|
388
|
+
if (status)
|
|
389
|
+
updateData.status = status;
|
|
390
|
+
try {
|
|
391
|
+
// Update the list
|
|
392
|
+
const updatedList = await listService.updateList(targetListId, updateData);
|
|
393
|
+
return {
|
|
394
|
+
content: [{
|
|
395
|
+
type: "text",
|
|
396
|
+
text: JSON.stringify({
|
|
397
|
+
id: updatedList.id,
|
|
398
|
+
name: updatedList.name,
|
|
399
|
+
content: updatedList.content,
|
|
400
|
+
space: {
|
|
401
|
+
id: updatedList.space.id,
|
|
402
|
+
name: updatedList.space.name
|
|
403
|
+
},
|
|
404
|
+
status: updatedList.status,
|
|
405
|
+
url: `https://app.clickup.com/${config.clickupTeamId}/v/l/${updatedList.id}`,
|
|
406
|
+
message: `List "${updatedList.name}" updated successfully`
|
|
407
|
+
}, null, 2)
|
|
408
|
+
}]
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
catch (error) {
|
|
412
|
+
throw new Error(`Failed to update list: ${error.message}`);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Handler for the delete_list tool
|
|
417
|
+
* Permanently removes a list from the workspace
|
|
418
|
+
*/
|
|
419
|
+
export async function handleDeleteList(parameters) {
|
|
420
|
+
const { listId, listName } = parameters;
|
|
421
|
+
let targetListId = listId;
|
|
422
|
+
// If no listId provided but listName is, look up the list ID
|
|
423
|
+
if (!targetListId && listName) {
|
|
424
|
+
const listResult = await findListIDByName(workspaceService, listName);
|
|
425
|
+
if (!listResult) {
|
|
426
|
+
throw new Error(`List "${listName}" not found`);
|
|
427
|
+
}
|
|
428
|
+
targetListId = listResult.id;
|
|
429
|
+
}
|
|
430
|
+
if (!targetListId) {
|
|
431
|
+
throw new Error("Either listId or listName must be provided");
|
|
432
|
+
}
|
|
433
|
+
try {
|
|
434
|
+
// Get list details before deletion for confirmation message
|
|
435
|
+
const list = await listService.getList(targetListId);
|
|
436
|
+
const listName = list.name;
|
|
437
|
+
// Delete the list
|
|
438
|
+
await listService.deleteList(targetListId);
|
|
439
|
+
return {
|
|
440
|
+
content: [{
|
|
441
|
+
type: "text",
|
|
442
|
+
text: JSON.stringify({
|
|
443
|
+
message: `List "${listName}" deleted successfully`,
|
|
444
|
+
url: `https://app.clickup.com/${config.clickupTeamId}/v/l/${targetListId}`
|
|
445
|
+
}, null, 2)
|
|
446
|
+
}]
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
catch (error) {
|
|
450
|
+
throw new Error(`Failed to delete list: ${error.message}`);
|
|
451
|
+
}
|
|
452
|
+
}
|