@taazkareem/clickup-mcp-server 0.6.5 → 0.6.7
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 +4 -4
- package/build/server.js +3 -6
- package/build/services/clickup/base.js +156 -45
- package/build/services/clickup/bulk.js +10 -22
- package/build/services/clickup/tag.js +51 -2
- package/build/services/clickup/task/task-core.js +76 -11
- package/build/services/clickup/task/task-search.js +378 -25
- package/build/services/clickup/workspace.js +14 -12
- package/build/tools/folder.js +5 -53
- package/build/tools/list.js +5 -66
- package/build/tools/tag.js +92 -76
- package/build/tools/task/attachments.js +16 -29
- package/build/tools/task/bulk-operations.js +7 -52
- package/build/tools/task/handlers.js +253 -136
- package/build/tools/task/main.js +9 -33
- package/build/tools/task/single-operations.js +15 -106
- package/build/tools/task/utilities.js +59 -12
- package/build/tools/task/workspace-operations.js +15 -8
- package/build/tools/workspace.js +1 -12
- package/build/utils/date-utils.js +7 -4
- package/build/utils/resolver-utils.js +102 -29
- package/package.json +1 -1
package/build/tools/folder.js
CHANGED
|
@@ -16,19 +16,7 @@ const { folder: folderService, workspace: workspaceService } = clickUpServices;
|
|
|
16
16
|
*/
|
|
17
17
|
export const createFolderTool = {
|
|
18
18
|
name: "create_folder",
|
|
19
|
-
description: `
|
|
20
|
-
|
|
21
|
-
Valid Usage:
|
|
22
|
-
1. Provide spaceId (preferred) + folder name
|
|
23
|
-
2. Provide spaceName + folder name
|
|
24
|
-
|
|
25
|
-
Requirements:
|
|
26
|
-
- name: REQUIRED
|
|
27
|
-
- EITHER spaceId OR spaceName: REQUIRED
|
|
28
|
-
|
|
29
|
-
Notes:
|
|
30
|
-
- After creating a folder, you can add lists to it using create_list_in_folder
|
|
31
|
-
- Use override_statuses to set folder-specific statuses`,
|
|
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.`,
|
|
32
20
|
inputSchema: {
|
|
33
21
|
type: "object",
|
|
34
22
|
properties: {
|
|
@@ -53,22 +41,11 @@ Notes:
|
|
|
53
41
|
}
|
|
54
42
|
};
|
|
55
43
|
/**
|
|
56
|
-
* Tool definition for
|
|
44
|
+
* Tool definition for retrieving folder details
|
|
57
45
|
*/
|
|
58
46
|
export const getFolderTool = {
|
|
59
47
|
name: "get_folder",
|
|
60
|
-
description: `
|
|
61
|
-
|
|
62
|
-
Valid Usage:
|
|
63
|
-
1. Use folderId alone (preferred)
|
|
64
|
-
2. Use folderName + (spaceId or spaceName)
|
|
65
|
-
|
|
66
|
-
Requirements:
|
|
67
|
-
- EITHER folderId OR (folderName + space information) is REQUIRED
|
|
68
|
-
- When using folderName, you MUST provide EITHER spaceId OR spaceName
|
|
69
|
-
|
|
70
|
-
Notes:
|
|
71
|
-
- Helps you understand folder structure before creating or updating lists`,
|
|
48
|
+
description: `Gets folder details. Use folderId (preferred) or folderName + (spaceId/spaceName). Helps understand folder structure before creating/updating lists.`,
|
|
72
49
|
inputSchema: {
|
|
73
50
|
type: "object",
|
|
74
51
|
properties: {
|
|
@@ -97,19 +74,7 @@ Notes:
|
|
|
97
74
|
*/
|
|
98
75
|
export const updateFolderTool = {
|
|
99
76
|
name: "update_folder",
|
|
100
|
-
description: `
|
|
101
|
-
|
|
102
|
-
Valid Usage:
|
|
103
|
-
1. Use folderId alone (preferred)
|
|
104
|
-
2. Use folderName + (spaceId or spaceName)
|
|
105
|
-
|
|
106
|
-
Requirements:
|
|
107
|
-
- At least one update field (name or override_statuses) must be provided
|
|
108
|
-
- EITHER folderId OR (folderName + space information) is REQUIRED
|
|
109
|
-
- When using folderName, you MUST provide EITHER spaceId OR spaceName
|
|
110
|
-
|
|
111
|
-
Notes:
|
|
112
|
-
- Changes apply immediately to all lists within the 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.`,
|
|
113
78
|
inputSchema: {
|
|
114
79
|
type: "object",
|
|
115
80
|
properties: {
|
|
@@ -146,20 +111,7 @@ Notes:
|
|
|
146
111
|
*/
|
|
147
112
|
export const deleteFolderTool = {
|
|
148
113
|
name: "delete_folder",
|
|
149
|
-
description: `
|
|
150
|
-
|
|
151
|
-
Valid Usage:
|
|
152
|
-
1. Use folderId alone (preferred and safest)
|
|
153
|
-
2. Use folderName + (spaceId or spaceName)
|
|
154
|
-
|
|
155
|
-
Requirements:
|
|
156
|
-
- EITHER folderId OR (folderName + space information) is REQUIRED
|
|
157
|
-
- When using folderName, you MUST provide EITHER spaceId OR spaceName
|
|
158
|
-
|
|
159
|
-
Warning:
|
|
160
|
-
- This action CANNOT be undone
|
|
161
|
-
- All lists and tasks within the folder will also be permanently deleted
|
|
162
|
-
- Using folderName is risky as names may not be unique across different spaces`,
|
|
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.`,
|
|
163
115
|
inputSchema: {
|
|
164
116
|
type: "object",
|
|
165
117
|
properties: {
|
package/build/tools/list.js
CHANGED
|
@@ -16,19 +16,7 @@ import { sponsorService } from '../utils/sponsor-service.js';
|
|
|
16
16
|
*/
|
|
17
17
|
export const createListTool = {
|
|
18
18
|
name: "create_list",
|
|
19
|
-
description: `
|
|
20
|
-
|
|
21
|
-
Valid Usage:
|
|
22
|
-
1. Provide spaceId + list name (preferred)
|
|
23
|
-
2. Provide spaceName + list name
|
|
24
|
-
|
|
25
|
-
Requirements:
|
|
26
|
-
- name: REQUIRED
|
|
27
|
-
- EITHER spaceId OR spaceName: REQUIRED
|
|
28
|
-
|
|
29
|
-
Notes:
|
|
30
|
-
- For creating lists inside folders, use create_list_in_folder instead
|
|
31
|
-
- Optional fields include content, dueDate, priority, assignee, and status`,
|
|
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.`,
|
|
32
20
|
inputSchema: {
|
|
33
21
|
type: "object",
|
|
34
22
|
properties: {
|
|
@@ -73,21 +61,7 @@ Notes:
|
|
|
73
61
|
*/
|
|
74
62
|
export const createListInFolderTool = {
|
|
75
63
|
name: "create_list_in_folder",
|
|
76
|
-
description: `
|
|
77
|
-
|
|
78
|
-
Valid Usage:
|
|
79
|
-
1. Provide folderId + list name (preferred)
|
|
80
|
-
2. Provide folderName + (spaceId OR spaceName) + list name
|
|
81
|
-
|
|
82
|
-
Requirements:
|
|
83
|
-
- name: REQUIRED
|
|
84
|
-
- EITHER folderId OR (folderName + space information): REQUIRED
|
|
85
|
-
- When using folderName, EITHER spaceId OR spaceName is REQUIRED
|
|
86
|
-
|
|
87
|
-
Notes:
|
|
88
|
-
- Folder names may not be unique across spaces, which is why space information
|
|
89
|
-
is required when using folderName
|
|
90
|
-
- Optional fields include content and status`,
|
|
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.`,
|
|
91
65
|
inputSchema: {
|
|
92
66
|
type: "object",
|
|
93
67
|
properties: {
|
|
@@ -128,18 +102,7 @@ Notes:
|
|
|
128
102
|
*/
|
|
129
103
|
export const getListTool = {
|
|
130
104
|
name: "get_list",
|
|
131
|
-
description: `
|
|
132
|
-
|
|
133
|
-
Valid Usage:
|
|
134
|
-
1. Provide listId (preferred)
|
|
135
|
-
2. Provide listName
|
|
136
|
-
|
|
137
|
-
Requirements:
|
|
138
|
-
- EITHER listId OR listName: REQUIRED
|
|
139
|
-
|
|
140
|
-
Notes:
|
|
141
|
-
- Using listId is more reliable as list names might not be unique
|
|
142
|
-
- Returns list details including name, content, and space information`,
|
|
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.`,
|
|
143
106
|
inputSchema: {
|
|
144
107
|
type: "object",
|
|
145
108
|
properties: {
|
|
@@ -160,19 +123,7 @@ Notes:
|
|
|
160
123
|
*/
|
|
161
124
|
export const updateListTool = {
|
|
162
125
|
name: "update_list",
|
|
163
|
-
description: `
|
|
164
|
-
|
|
165
|
-
Valid Usage:
|
|
166
|
-
1. Provide listId + update fields (preferred)
|
|
167
|
-
2. Provide listName + update fields
|
|
168
|
-
|
|
169
|
-
Requirements:
|
|
170
|
-
- EITHER listId OR listName: REQUIRED
|
|
171
|
-
- At least one field to update (name, content, or status): REQUIRED
|
|
172
|
-
|
|
173
|
-
Notes:
|
|
174
|
-
- Using listId is more reliable as list names might not be unique
|
|
175
|
-
- Only specified fields will be updated`,
|
|
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.`,
|
|
176
127
|
inputSchema: {
|
|
177
128
|
type: "object",
|
|
178
129
|
properties: {
|
|
@@ -205,19 +156,7 @@ Notes:
|
|
|
205
156
|
*/
|
|
206
157
|
export const deleteListTool = {
|
|
207
158
|
name: "delete_list",
|
|
208
|
-
description: `
|
|
209
|
-
|
|
210
|
-
Valid Usage:
|
|
211
|
-
1. Provide listId (preferred and safest)
|
|
212
|
-
2. Provide listName
|
|
213
|
-
|
|
214
|
-
Requirements:
|
|
215
|
-
- EITHER listId OR listName: REQUIRED
|
|
216
|
-
|
|
217
|
-
Warning:
|
|
218
|
-
- This action CANNOT be undone
|
|
219
|
-
- All tasks within the list will also be permanently deleted
|
|
220
|
-
- Using listName is risky as names may not be unique`,
|
|
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.`,
|
|
221
160
|
inputSchema: {
|
|
222
161
|
type: "object",
|
|
223
162
|
properties: {
|
package/build/tools/tag.js
CHANGED
|
@@ -13,24 +13,20 @@ import { clickUpServices } from '../services/shared.js';
|
|
|
13
13
|
import { Logger } from '../logger.js';
|
|
14
14
|
import { sponsorService } from '../utils/sponsor-service.js';
|
|
15
15
|
import { processColorCommand } from '../utils/color-processor.js';
|
|
16
|
+
import { validateTaskIdentification } from './task/utilities.js';
|
|
16
17
|
// Create a logger specific to tag tools
|
|
17
18
|
const logger = new Logger('TagTools');
|
|
19
|
+
// Use shared services instance
|
|
20
|
+
const { task: taskService } = clickUpServices;
|
|
18
21
|
//=============================================================================
|
|
19
22
|
// TOOL DEFINITIONS
|
|
20
23
|
//=============================================================================
|
|
21
24
|
/**
|
|
22
|
-
* Tool definition for getting tags
|
|
25
|
+
* Tool definition for getting space tags
|
|
23
26
|
*/
|
|
24
27
|
export const getSpaceTagsTool = {
|
|
25
28
|
name: "get_space_tags",
|
|
26
|
-
description: `
|
|
27
|
-
|
|
28
|
-
Valid Usage:
|
|
29
|
-
1. Provide spaceId (preferred)
|
|
30
|
-
2. Provide spaceName
|
|
31
|
-
|
|
32
|
-
Requirements:
|
|
33
|
-
- Space identification: EITHER spaceId OR spaceName REQUIRED`,
|
|
29
|
+
description: `Gets all tags in a ClickUp space. Use spaceId (preferred) or spaceName. Tags are defined at space level - check available tags before adding to tasks.`,
|
|
34
30
|
inputSchema: {
|
|
35
31
|
type: "object",
|
|
36
32
|
properties: {
|
|
@@ -53,15 +49,18 @@ export const createSpaceTagTool = {
|
|
|
53
49
|
description: `Purpose: Create a new tag in a ClickUp space.
|
|
54
50
|
|
|
55
51
|
Valid Usage:
|
|
56
|
-
1. Provide spaceId
|
|
57
|
-
2. Provide spaceName
|
|
52
|
+
1. Provide spaceId (preferred if available)
|
|
53
|
+
2. Provide spaceName (will be resolved to a space ID)
|
|
58
54
|
|
|
59
55
|
Requirements:
|
|
60
56
|
- tagName: REQUIRED
|
|
61
|
-
-
|
|
57
|
+
- EITHER spaceId OR spaceName: REQUIRED
|
|
62
58
|
|
|
63
59
|
Notes:
|
|
64
|
-
-
|
|
60
|
+
- New tag will be available for all tasks in the space
|
|
61
|
+
- You can specify background and foreground colors in HEX format (e.g., #FF0000)
|
|
62
|
+
- You can also provide a color command (e.g., "blue tag") to automatically generate colors
|
|
63
|
+
- After creating a tag, you can add it to tasks using add_tag_to_task`,
|
|
65
64
|
inputSchema: {
|
|
66
65
|
type: "object",
|
|
67
66
|
properties: {
|
|
@@ -190,25 +189,7 @@ Warning:
|
|
|
190
189
|
*/
|
|
191
190
|
export const addTagToTaskTool = {
|
|
192
191
|
name: "add_tag_to_task",
|
|
193
|
-
description: `
|
|
194
|
-
|
|
195
|
-
Valid Usage:
|
|
196
|
-
1. Provide taskId (preferred if available)
|
|
197
|
-
2. Provide taskName + listName
|
|
198
|
-
|
|
199
|
-
Requirements:
|
|
200
|
-
- tagName: REQUIRED
|
|
201
|
-
- EITHER taskId OR (taskName + listName): REQUIRED
|
|
202
|
-
- The tag MUST exist in the space containing the task before calling this tool
|
|
203
|
-
|
|
204
|
-
Warning:
|
|
205
|
-
- The operation will fail if the tag does not exist in the space
|
|
206
|
-
- Always use get_space_tags first to verify the tag exists
|
|
207
|
-
- If the tag doesn't exist, create it using create_space_tag before adding it to the task
|
|
208
|
-
|
|
209
|
-
Notes:
|
|
210
|
-
- Use get_space_tags to see available tags
|
|
211
|
-
- Use create_space_tag to create a new tag if needed`,
|
|
192
|
+
description: `Adds existing tag to task. Use taskId (preferred) or taskName + optional listName. Tag must exist in space (use get_space_tags to verify, create_space_tag if needed). WARNING: Will fail if tag doesn't exist.`,
|
|
212
193
|
inputSchema: {
|
|
213
194
|
type: "object",
|
|
214
195
|
properties: {
|
|
@@ -222,11 +203,11 @@ Notes:
|
|
|
222
203
|
},
|
|
223
204
|
taskName: {
|
|
224
205
|
type: "string",
|
|
225
|
-
description: "Name of the task to add tag to.
|
|
206
|
+
description: "Name of the task to add tag to. Will search across all lists unless listName is provided."
|
|
226
207
|
},
|
|
227
208
|
listName: {
|
|
228
209
|
type: "string",
|
|
229
|
-
description: "Name of the list containing the task.
|
|
210
|
+
description: "Optional: Name of the list containing the task. Use to disambiguate when multiple tasks have the same name."
|
|
230
211
|
},
|
|
231
212
|
tagName: {
|
|
232
213
|
type: "string",
|
|
@@ -241,19 +222,7 @@ Notes:
|
|
|
241
222
|
*/
|
|
242
223
|
export const removeTagFromTaskTool = {
|
|
243
224
|
name: "remove_tag_from_task",
|
|
244
|
-
description: `
|
|
245
|
-
|
|
246
|
-
Valid Usage:
|
|
247
|
-
1. Provide taskId (preferred if available)
|
|
248
|
-
2. Provide taskName + listName
|
|
249
|
-
|
|
250
|
-
Requirements:
|
|
251
|
-
- tagName: REQUIRED
|
|
252
|
-
- EITHER taskId OR (taskName + listName): REQUIRED
|
|
253
|
-
|
|
254
|
-
Notes:
|
|
255
|
-
- This only removes the association between the tag and task
|
|
256
|
-
- The tag will still exist in the space`,
|
|
225
|
+
description: `Removes tag from task. Use taskId (preferred) or taskName + optional listName. Only removes tag-task association, tag remains in space. For multiple tasks, provide listName to disambiguate.`,
|
|
257
226
|
inputSchema: {
|
|
258
227
|
type: "object",
|
|
259
228
|
properties: {
|
|
@@ -267,11 +236,11 @@ Notes:
|
|
|
267
236
|
},
|
|
268
237
|
taskName: {
|
|
269
238
|
type: "string",
|
|
270
|
-
description: "Name of the task to remove tag from.
|
|
239
|
+
description: "Name of the task to remove tag from. Will search across all lists unless listName is provided."
|
|
271
240
|
},
|
|
272
241
|
listName: {
|
|
273
242
|
type: "string",
|
|
274
|
-
description: "Name of the list containing the task.
|
|
243
|
+
description: "Optional: Name of the list containing the task. Use to disambiguate when multiple tasks have the same name."
|
|
275
244
|
},
|
|
276
245
|
tagName: {
|
|
277
246
|
type: "string",
|
|
@@ -334,10 +303,18 @@ export const handleDeleteSpaceTag = createHandlerWrapper(deleteSpaceTag, () => (
|
|
|
334
303
|
/**
|
|
335
304
|
* Wrapper for addTagToTask handler
|
|
336
305
|
*/
|
|
337
|
-
export const handleAddTagToTask = createHandlerWrapper(addTagToTask, () =>
|
|
338
|
-
success
|
|
339
|
-
|
|
340
|
-
|
|
306
|
+
export const handleAddTagToTask = createHandlerWrapper(addTagToTask, (result) => {
|
|
307
|
+
if (!result.success) {
|
|
308
|
+
return {
|
|
309
|
+
success: false,
|
|
310
|
+
error: result.error
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
return {
|
|
314
|
+
success: true,
|
|
315
|
+
message: "Tag added to task successfully"
|
|
316
|
+
};
|
|
317
|
+
});
|
|
341
318
|
/**
|
|
342
319
|
* Wrapper for removeTagFromTask handler
|
|
343
320
|
*/
|
|
@@ -666,28 +643,42 @@ export async function deleteSpaceTag(params) {
|
|
|
666
643
|
*/
|
|
667
644
|
async function resolveTaskId(params) {
|
|
668
645
|
const { taskId, customTaskId, taskName, listName } = params;
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
646
|
+
try {
|
|
647
|
+
// First validate task identification with global lookup enabled
|
|
648
|
+
const validationResult = validateTaskIdentification({ taskId, customTaskId, taskName, listName }, { useGlobalLookup: true });
|
|
649
|
+
if (!validationResult.isValid) {
|
|
650
|
+
return {
|
|
651
|
+
success: false,
|
|
652
|
+
error: { message: validationResult.errorMessage }
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
const result = await taskService.findTasks({
|
|
656
|
+
taskId,
|
|
657
|
+
customTaskId,
|
|
658
|
+
taskName,
|
|
659
|
+
listName,
|
|
660
|
+
allowMultipleMatches: false,
|
|
661
|
+
useSmartDisambiguation: true,
|
|
662
|
+
includeFullDetails: false
|
|
663
|
+
});
|
|
664
|
+
if (!result || Array.isArray(result)) {
|
|
665
|
+
return {
|
|
666
|
+
success: false,
|
|
667
|
+
error: { message: 'Task not found with the provided identification' }
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
return { success: true, taskId: result.id };
|
|
676
671
|
}
|
|
677
|
-
|
|
678
|
-
if (taskName && listName) {
|
|
679
|
-
// Implementation would go here
|
|
672
|
+
catch (error) {
|
|
680
673
|
return {
|
|
681
674
|
success: false,
|
|
682
|
-
error: {
|
|
675
|
+
error: {
|
|
676
|
+
message: error.message || 'Failed to resolve task ID',
|
|
677
|
+
code: error.code,
|
|
678
|
+
details: error.data
|
|
679
|
+
}
|
|
683
680
|
};
|
|
684
681
|
}
|
|
685
|
-
return {
|
|
686
|
-
success: false,
|
|
687
|
-
error: {
|
|
688
|
-
message: 'Task identifier is required (taskId, customTaskId, or taskName+listName)'
|
|
689
|
-
}
|
|
690
|
-
};
|
|
691
682
|
}
|
|
692
683
|
/**
|
|
693
684
|
* Add a tag to a task
|
|
@@ -705,12 +696,12 @@ export async function addTagToTask(params) {
|
|
|
705
696
|
}
|
|
706
697
|
};
|
|
707
698
|
}
|
|
708
|
-
if (!taskId && !customTaskId && !
|
|
709
|
-
logger.error('addTagToTask called without
|
|
699
|
+
if (!taskId && !customTaskId && !taskName) {
|
|
700
|
+
logger.error('addTagToTask called without task identifier');
|
|
710
701
|
return {
|
|
711
702
|
success: false,
|
|
712
703
|
error: {
|
|
713
|
-
message: 'Either taskId, customTaskId, or
|
|
704
|
+
message: 'Either taskId, customTaskId, or taskName is required'
|
|
714
705
|
}
|
|
715
706
|
};
|
|
716
707
|
}
|
|
@@ -728,6 +719,31 @@ export async function addTagToTask(params) {
|
|
|
728
719
|
const result = await clickUpServices.tag.addTagToTask(taskIdResult.taskId, tagName);
|
|
729
720
|
if (!result.success) {
|
|
730
721
|
logger.error('Failed to add tag to task', result.error);
|
|
722
|
+
// Provide more specific error messages based on error code
|
|
723
|
+
if (result.error?.code === 'TAG_NOT_FOUND') {
|
|
724
|
+
return {
|
|
725
|
+
success: false,
|
|
726
|
+
error: {
|
|
727
|
+
message: `The tag "${tagName}" does not exist in the space. Please create it first using create_space_tag.`
|
|
728
|
+
}
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
else if (result.error?.code === 'SPACE_NOT_FOUND') {
|
|
732
|
+
return {
|
|
733
|
+
success: false,
|
|
734
|
+
error: {
|
|
735
|
+
message: 'Could not determine which space the task belongs to.'
|
|
736
|
+
}
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
else if (result.error?.code === 'TAG_VERIFICATION_FAILED') {
|
|
740
|
+
return {
|
|
741
|
+
success: false,
|
|
742
|
+
error: {
|
|
743
|
+
message: 'The tag addition could not be verified. Please check if the tag was added manually.'
|
|
744
|
+
}
|
|
745
|
+
};
|
|
746
|
+
}
|
|
731
747
|
return {
|
|
732
748
|
success: false,
|
|
733
749
|
error: result.error || {
|
|
@@ -768,12 +784,12 @@ export async function removeTagFromTask(params) {
|
|
|
768
784
|
}
|
|
769
785
|
};
|
|
770
786
|
}
|
|
771
|
-
if (!taskId && !customTaskId && !
|
|
772
|
-
logger.error('removeTagFromTask called without
|
|
787
|
+
if (!taskId && !customTaskId && !taskName) {
|
|
788
|
+
logger.error('removeTagFromTask called without task identifier');
|
|
773
789
|
return {
|
|
774
790
|
success: false,
|
|
775
791
|
error: {
|
|
776
|
-
message: 'Either taskId, customTaskId, or
|
|
792
|
+
message: 'Either taskId, customTaskId, or taskName is required'
|
|
777
793
|
}
|
|
778
794
|
};
|
|
779
795
|
}
|
|
@@ -10,8 +10,11 @@
|
|
|
10
10
|
import { clickUpServices } from '../../services/shared.js';
|
|
11
11
|
import { validateTaskIdentification } from './utilities.js';
|
|
12
12
|
import { sponsorService } from '../../utils/sponsor-service.js';
|
|
13
|
+
import { Logger } from '../../logger.js';
|
|
13
14
|
// Use shared services instance
|
|
14
15
|
const { task: taskService } = clickUpServices;
|
|
16
|
+
// Create a logger instance for attachments
|
|
17
|
+
const logger = new Logger('TaskAttachments');
|
|
15
18
|
// Session storage for chunked uploads (in-memory for demonstration)
|
|
16
19
|
const chunkSessions = new Map();
|
|
17
20
|
// Clean up expired sessions periodically
|
|
@@ -21,7 +24,7 @@ setInterval(() => {
|
|
|
21
24
|
for (const [token, session] of chunkSessions.entries()) {
|
|
22
25
|
if (now - session.timestamp > expired) {
|
|
23
26
|
chunkSessions.delete(token);
|
|
24
|
-
|
|
27
|
+
logger.debug(`Cleaned up expired upload session: ${token}`);
|
|
25
28
|
}
|
|
26
29
|
}
|
|
27
30
|
}, 3600 * 1000); // Check every hour
|
|
@@ -30,26 +33,7 @@ setInterval(() => {
|
|
|
30
33
|
*/
|
|
31
34
|
export const attachTaskFileTool = {
|
|
32
35
|
name: "attach_task_file",
|
|
33
|
-
description: `
|
|
34
|
-
|
|
35
|
-
Valid Usage:
|
|
36
|
-
1. Use taskId (preferred) - works with both regular and custom IDs
|
|
37
|
-
2. Use taskName + listName for targeted search
|
|
38
|
-
|
|
39
|
-
File Sources:
|
|
40
|
-
- Base64: Provide file_data + file_name
|
|
41
|
-
- URL: Provide file_url starting with http:// or https://
|
|
42
|
-
- Local file: Provide file_url as absolute path
|
|
43
|
-
- Chunked upload: Use chunk_* parameters for large files
|
|
44
|
-
|
|
45
|
-
Requirements:
|
|
46
|
-
- Task identification: EITHER taskId OR taskName REQUIRED
|
|
47
|
-
- File source: EITHER file_data+file_name OR file_url OR chunk_session REQUIRED
|
|
48
|
-
|
|
49
|
-
Notes:
|
|
50
|
-
- System automatically selects best upload method based on file size
|
|
51
|
-
- Base64 uploads limited to 10MB
|
|
52
|
-
- Using taskName without listName may match multiple tasks`,
|
|
36
|
+
description: `Attaches file to task. Use taskId (preferred) or taskName + optional listName. File sources: 1) base64 + filename (≤10MB), 2) URL (http/https), 3) local path (absolute), 4) chunked for large files. WARNING: taskName without listName may match multiple tasks.`,
|
|
53
37
|
inputSchema: {
|
|
54
38
|
type: "object",
|
|
55
39
|
properties: {
|
|
@@ -106,9 +90,12 @@ Notes:
|
|
|
106
90
|
*/
|
|
107
91
|
async function attachTaskFileHandler(params) {
|
|
108
92
|
// Extract common parameters
|
|
109
|
-
const { taskId, taskName, listName, file_name, file_data, file_url, auth_header, chunk_total, chunk_size, chunk_index, session_id } = params;
|
|
93
|
+
const { taskId, taskName, listName, customTaskId, file_name, file_data, file_url, auth_header, chunk_total, chunk_size, chunk_index, session_id } = params;
|
|
110
94
|
// Validate task identification
|
|
111
|
-
validateTaskIdentification(
|
|
95
|
+
const validationResult = validateTaskIdentification({ taskId, taskName, listName, customTaskId }, { useGlobalLookup: true });
|
|
96
|
+
if (!validationResult.isValid) {
|
|
97
|
+
throw new Error(validationResult.errorMessage);
|
|
98
|
+
}
|
|
112
99
|
// Validate file source - either file_data or file_url must be provided
|
|
113
100
|
if (!file_data && !file_url && !session_id) {
|
|
114
101
|
throw new Error("Either file_data, file_url, or session_id must be provided");
|
|
@@ -134,13 +121,13 @@ async function attachTaskFileHandler(params) {
|
|
|
134
121
|
// CASE 2: URL-based upload or local file path
|
|
135
122
|
if (file_url) {
|
|
136
123
|
// Check if it's a local file path
|
|
137
|
-
|
|
124
|
+
logger.debug(`Checking if path is local: ${file_url}`);
|
|
138
125
|
if (file_url.startsWith('/') || /^[A-Za-z]:\\/.test(file_url)) {
|
|
139
|
-
|
|
126
|
+
logger.debug(`Detected as local path, proceeding to handle: ${file_url}`);
|
|
140
127
|
return await handleLocalFileUpload(resolvedTaskId, file_url, file_name);
|
|
141
128
|
}
|
|
142
129
|
else if (file_url.startsWith('http://') || file_url.startsWith('https://')) {
|
|
143
|
-
|
|
130
|
+
logger.debug(`Detected as URL, proceeding with URL upload: ${file_url}`);
|
|
144
131
|
return await handleUrlUpload(resolvedTaskId, file_url, file_name, auth_header);
|
|
145
132
|
}
|
|
146
133
|
else {
|
|
@@ -167,7 +154,7 @@ async function attachTaskFileHandler(params) {
|
|
|
167
154
|
throw new Error("Invalid parameters: Unable to determine upload method");
|
|
168
155
|
}
|
|
169
156
|
catch (error) {
|
|
170
|
-
|
|
157
|
+
logger.error(`Error attaching file to task:`, error);
|
|
171
158
|
throw error;
|
|
172
159
|
}
|
|
173
160
|
}
|
|
@@ -315,7 +302,7 @@ async function handleLocalFileUpload(taskId, filePath, fileName) {
|
|
|
315
302
|
// Import fs and path modules
|
|
316
303
|
const fs = await import('fs');
|
|
317
304
|
const path = await import('path');
|
|
318
|
-
|
|
305
|
+
logger.debug(`Processing absolute file path: ${filePath}`);
|
|
319
306
|
// Normalize the path to prevent directory traversal attacks
|
|
320
307
|
const normalizedPath = path.normalize(filePath);
|
|
321
308
|
// Check if file exists
|
|
@@ -332,7 +319,7 @@ async function handleLocalFileUpload(taskId, filePath, fileName) {
|
|
|
332
319
|
// Read file
|
|
333
320
|
const fileBuffer = fs.readFileSync(normalizedPath);
|
|
334
321
|
const fileSize = fileBuffer.length;
|
|
335
|
-
|
|
322
|
+
logger.debug(`Successfully read file: ${extractedFileName} (${fileSize} bytes)`);
|
|
336
323
|
// Choose upload method based on file size
|
|
337
324
|
if (fileSize > 10 * 1024 * 1024) {
|
|
338
325
|
// For large files, start chunked upload process
|
|
@@ -73,19 +73,7 @@ const taskIdentifierSchema = {
|
|
|
73
73
|
*/
|
|
74
74
|
export const createBulkTasksTool = {
|
|
75
75
|
name: "create_bulk_tasks",
|
|
76
|
-
description: `
|
|
77
|
-
|
|
78
|
-
Valid Usage:
|
|
79
|
-
1. Provide listId + array of tasks (preferred)
|
|
80
|
-
2. Provide listName + array of tasks
|
|
81
|
-
|
|
82
|
-
Requirements:
|
|
83
|
-
- tasks: REQUIRED (array of tasks, each with at least a name)
|
|
84
|
-
- List identification: EITHER listId OR listName REQUIRED
|
|
85
|
-
|
|
86
|
-
Notes:
|
|
87
|
-
- Configure batch processing via options parameter
|
|
88
|
-
- Custom fields supported for each task`,
|
|
76
|
+
description: `Creates multiple tasks in one list. Use listId (preferred) or listName + array of tasks (each needs name). Configure batch size/concurrency via options. Tasks can have custom fields as {id, value} array.`,
|
|
89
77
|
inputSchema: {
|
|
90
78
|
type: "object",
|
|
91
79
|
properties: {
|
|
@@ -161,23 +149,11 @@ Notes:
|
|
|
161
149
|
}
|
|
162
150
|
};
|
|
163
151
|
/**
|
|
164
|
-
* Tool definition for updating multiple tasks
|
|
152
|
+
* Tool definition for updating multiple tasks
|
|
165
153
|
*/
|
|
166
154
|
export const updateBulkTasksTool = {
|
|
167
155
|
name: "update_bulk_tasks",
|
|
168
|
-
description: `
|
|
169
|
-
|
|
170
|
-
Valid Usage:
|
|
171
|
-
1. Provide array of tasks with taskId (preferred)
|
|
172
|
-
2. Provide array of tasks with taskName + listName
|
|
173
|
-
|
|
174
|
-
Requirements:
|
|
175
|
-
- tasks: REQUIRED (array of tasks to update)
|
|
176
|
-
- Each task needs identification and update fields
|
|
177
|
-
|
|
178
|
-
Notes:
|
|
179
|
-
- Only specified fields will be updated for each task
|
|
180
|
-
- Configure batch processing via options parameter`,
|
|
156
|
+
description: `Updates multiple tasks efficiently. For each task: use taskId (preferred) or taskName + listName. At least one update field per task. Configure batch size/concurrency via options. WARNING: taskName without listName will fail.`,
|
|
181
157
|
inputSchema: {
|
|
182
158
|
type: "object",
|
|
183
159
|
properties: {
|
|
@@ -254,21 +230,11 @@ Notes:
|
|
|
254
230
|
}
|
|
255
231
|
};
|
|
256
232
|
/**
|
|
257
|
-
* Tool definition for moving multiple tasks
|
|
233
|
+
* Tool definition for moving multiple tasks
|
|
258
234
|
*/
|
|
259
235
|
export const moveBulkTasksTool = {
|
|
260
236
|
name: "move_bulk_tasks",
|
|
261
|
-
description: `
|
|
262
|
-
|
|
263
|
-
Valid Usage:
|
|
264
|
-
1. Provide tasks array + target list
|
|
265
|
-
|
|
266
|
-
Requirements:
|
|
267
|
-
- tasks: REQUIRED (array of task identifiers)
|
|
268
|
-
- Target list: EITHER targetListId OR targetListName REQUIRED
|
|
269
|
-
|
|
270
|
-
Warning:
|
|
271
|
-
- Task statuses may reset if destination list has different status options`,
|
|
237
|
+
description: `Moves multiple tasks to one list. For each task: use taskId (preferred) or taskName + listName. Target list: use targetListId/Name. Configure batch size/concurrency via options. WARNING: Task statuses may reset, taskName needs listName.`,
|
|
272
238
|
inputSchema: {
|
|
273
239
|
type: "object",
|
|
274
240
|
properties: {
|
|
@@ -311,22 +277,11 @@ Warning:
|
|
|
311
277
|
}
|
|
312
278
|
};
|
|
313
279
|
/**
|
|
314
|
-
* Tool definition for deleting multiple tasks
|
|
280
|
+
* Tool definition for deleting multiple tasks
|
|
315
281
|
*/
|
|
316
282
|
export const deleteBulkTasksTool = {
|
|
317
283
|
name: "delete_bulk_tasks",
|
|
318
|
-
description: `
|
|
319
|
-
|
|
320
|
-
Valid Usage:
|
|
321
|
-
1. Provide array of tasks with taskId (preferred)
|
|
322
|
-
2. Provide array of tasks with taskName + listName
|
|
323
|
-
|
|
324
|
-
Requirements:
|
|
325
|
-
- tasks: REQUIRED (array of task identifiers)
|
|
326
|
-
|
|
327
|
-
Warning:
|
|
328
|
-
- This action CANNOT be undone
|
|
329
|
-
- Always provide listName when using taskName`,
|
|
284
|
+
description: `PERMANENTLY deletes multiple tasks. For each task: use taskId (preferred/safest) or taskName + listName. Configure batch size/concurrency via options. WARNING: Cannot be undone, taskName without listName is dangerous.`,
|
|
330
285
|
inputSchema: {
|
|
331
286
|
type: "object",
|
|
332
287
|
properties: {
|