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