@taazkareem/clickup-mcp-server 0.4.72 → 0.4.74
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/build/config.js +1 -1
- package/build/server.js +3 -3
- package/build/server.log +688 -28
- package/build/services/clickup/task.js +122 -88
- package/build/services/clickup/types.js +16 -1
- package/build/services/shared.js +6 -6
- package/build/tools/folder.js +87 -61
- package/build/tools/index.js +1 -1
- package/build/tools/list.js +123 -82
- package/build/tools/task/bulk-operations.js +284 -0
- package/build/tools/task/handlers.js +213 -0
- package/build/tools/task/index.js +19 -0
- package/build/tools/task/main.js +89 -0
- package/build/tools/task/single-operations.js +421 -0
- package/build/tools/task/utilities.js +163 -0
- package/build/tools/task.js +369 -485
- package/build/tools/utils.js +0 -2
- package/build/tools/workspace.js +46 -32
- package/build/utils/sponsor-analytics.js +100 -0
- package/build/utils/sponsor-service.js +71 -0
- package/build/utils/sponsor-utils.js +12 -4
- package/package.json +1 -1
- package/build/tools/cache.js +0 -452
|
@@ -14,7 +14,6 @@ export class TaskService extends BaseClickUpService {
|
|
|
14
14
|
constructor(apiKey, teamId, baseUrl, workspaceService) {
|
|
15
15
|
super(apiKey, teamId, baseUrl);
|
|
16
16
|
this.workspaceService = null;
|
|
17
|
-
// Cache workspace service if provided
|
|
18
17
|
if (workspaceService) {
|
|
19
18
|
this.workspaceService = workspaceService;
|
|
20
19
|
this.logOperation('constructor', { usingSharedWorkspaceService: true });
|
|
@@ -35,6 +34,122 @@ export class TaskService extends BaseClickUpService {
|
|
|
35
34
|
}
|
|
36
35
|
return new ClickUpServiceError(message || `Task service error: ${error.message}`, ErrorCode.UNKNOWN, error);
|
|
37
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Build URL parameters from task filters
|
|
39
|
+
* @param filters Task filters to convert to URL parameters
|
|
40
|
+
* @returns URLSearchParams object
|
|
41
|
+
*/
|
|
42
|
+
buildTaskFilterParams(filters) {
|
|
43
|
+
const params = new URLSearchParams();
|
|
44
|
+
// Add all filters to the query parameters
|
|
45
|
+
if (filters.include_closed)
|
|
46
|
+
params.append('include_closed', String(filters.include_closed));
|
|
47
|
+
if (filters.subtasks)
|
|
48
|
+
params.append('subtasks', String(filters.subtasks));
|
|
49
|
+
if (filters.page)
|
|
50
|
+
params.append('page', String(filters.page));
|
|
51
|
+
if (filters.order_by)
|
|
52
|
+
params.append('order_by', filters.order_by);
|
|
53
|
+
if (filters.reverse)
|
|
54
|
+
params.append('reverse', String(filters.reverse));
|
|
55
|
+
// Array parameters
|
|
56
|
+
if (filters.statuses && filters.statuses.length > 0) {
|
|
57
|
+
filters.statuses.forEach(status => params.append('statuses[]', status));
|
|
58
|
+
}
|
|
59
|
+
if (filters.assignees && filters.assignees.length > 0) {
|
|
60
|
+
filters.assignees.forEach(assignee => params.append('assignees[]', assignee));
|
|
61
|
+
}
|
|
62
|
+
// Date filters
|
|
63
|
+
if (filters.due_date_gt)
|
|
64
|
+
params.append('due_date_gt', String(filters.due_date_gt));
|
|
65
|
+
if (filters.due_date_lt)
|
|
66
|
+
params.append('due_date_lt', String(filters.due_date_lt));
|
|
67
|
+
if (filters.date_created_gt)
|
|
68
|
+
params.append('date_created_gt', String(filters.date_created_gt));
|
|
69
|
+
if (filters.date_created_lt)
|
|
70
|
+
params.append('date_created_lt', String(filters.date_created_lt));
|
|
71
|
+
if (filters.date_updated_gt)
|
|
72
|
+
params.append('date_updated_gt', String(filters.date_updated_gt));
|
|
73
|
+
if (filters.date_updated_lt)
|
|
74
|
+
params.append('date_updated_lt', String(filters.date_updated_lt));
|
|
75
|
+
// Handle custom fields if present
|
|
76
|
+
if (filters.custom_fields) {
|
|
77
|
+
Object.entries(filters.custom_fields).forEach(([key, value]) => {
|
|
78
|
+
params.append(`custom_fields[${key}]`, String(value));
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return params;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Extract priority value from a task
|
|
85
|
+
* @param task The task to extract priority from
|
|
86
|
+
* @returns TaskPriority or null
|
|
87
|
+
*/
|
|
88
|
+
extractPriorityValue(task) {
|
|
89
|
+
if (!task.priority || !task.priority.id) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const priorityValue = parseInt(task.priority.id);
|
|
93
|
+
// Ensure it's in the valid range 1-4
|
|
94
|
+
if (isNaN(priorityValue) || priorityValue < 1 || priorityValue > 4) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
return priorityValue;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Extract task data for creation/duplication
|
|
101
|
+
* @param task The source task
|
|
102
|
+
* @param nameOverride Optional override for the task name
|
|
103
|
+
* @returns CreateTaskData object
|
|
104
|
+
*/
|
|
105
|
+
extractTaskData(task, nameOverride) {
|
|
106
|
+
return {
|
|
107
|
+
name: nameOverride || task.name,
|
|
108
|
+
description: task.description || '',
|
|
109
|
+
status: task.status?.status,
|
|
110
|
+
priority: this.extractPriorityValue(task),
|
|
111
|
+
due_date: task.due_date ? Number(task.due_date) : undefined,
|
|
112
|
+
assignees: task.assignees?.map(a => a.id) || []
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Find a matching task by name using different matching strategies
|
|
117
|
+
* @param tasks List of tasks to search
|
|
118
|
+
* @param taskName Name to search for
|
|
119
|
+
* @returns Matching task or null
|
|
120
|
+
*/
|
|
121
|
+
findMatchingTask(tasks, taskName) {
|
|
122
|
+
// Normalize the search term
|
|
123
|
+
const normalizedSearchTerm = taskName.toLowerCase().trim();
|
|
124
|
+
// First try exact match
|
|
125
|
+
let matchingTask = tasks.find(task => task.name.toLowerCase().trim() === normalizedSearchTerm);
|
|
126
|
+
// If no exact match, try substring match
|
|
127
|
+
if (!matchingTask) {
|
|
128
|
+
matchingTask = tasks.find(task => task.name.toLowerCase().trim().includes(normalizedSearchTerm) ||
|
|
129
|
+
normalizedSearchTerm.includes(task.name.toLowerCase().trim()));
|
|
130
|
+
}
|
|
131
|
+
// If still no match and there are emoji characters, try matching without emoji
|
|
132
|
+
if (!matchingTask && /[\p{Emoji}]/u.test(normalizedSearchTerm)) {
|
|
133
|
+
matchingTask = this.findMatchingTaskWithoutEmoji(tasks, normalizedSearchTerm);
|
|
134
|
+
}
|
|
135
|
+
return matchingTask || null;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Find matching task with emoji characters removed
|
|
139
|
+
* @param tasks List of tasks to search
|
|
140
|
+
* @param searchTerm Search term (with emoji)
|
|
141
|
+
* @returns Matching task or null
|
|
142
|
+
*/
|
|
143
|
+
findMatchingTaskWithoutEmoji(tasks, searchTerm) {
|
|
144
|
+
// Remove emoji and try again (simple approximation)
|
|
145
|
+
const withoutEmoji = searchTerm.replace(/[\p{Emoji}]/gu, '').trim();
|
|
146
|
+
return tasks.find(task => {
|
|
147
|
+
const taskNameWithoutEmoji = task.name.toLowerCase().replace(/[\p{Emoji}]/gu, '').trim();
|
|
148
|
+
return taskNameWithoutEmoji === withoutEmoji ||
|
|
149
|
+
taskNameWithoutEmoji.includes(withoutEmoji) ||
|
|
150
|
+
withoutEmoji.includes(taskNameWithoutEmoji);
|
|
151
|
+
}) || null;
|
|
152
|
+
}
|
|
38
153
|
/**
|
|
39
154
|
* Create a new task in the specified list
|
|
40
155
|
* @param listId The ID of the list to create the task in
|
|
@@ -63,42 +178,7 @@ export class TaskService extends BaseClickUpService {
|
|
|
63
178
|
this.logOperation('getTasks', { listId, filters });
|
|
64
179
|
try {
|
|
65
180
|
return await this.makeRequest(async () => {
|
|
66
|
-
const params =
|
|
67
|
-
// Add all filters to the query parameters
|
|
68
|
-
if (filters.include_closed)
|
|
69
|
-
params.append('include_closed', String(filters.include_closed));
|
|
70
|
-
if (filters.subtasks)
|
|
71
|
-
params.append('subtasks', String(filters.subtasks));
|
|
72
|
-
if (filters.page)
|
|
73
|
-
params.append('page', String(filters.page));
|
|
74
|
-
if (filters.order_by)
|
|
75
|
-
params.append('order_by', filters.order_by);
|
|
76
|
-
if (filters.reverse)
|
|
77
|
-
params.append('reverse', String(filters.reverse));
|
|
78
|
-
if (filters.statuses && filters.statuses.length > 0) {
|
|
79
|
-
filters.statuses.forEach(status => params.append('statuses[]', status));
|
|
80
|
-
}
|
|
81
|
-
if (filters.assignees && filters.assignees.length > 0) {
|
|
82
|
-
filters.assignees.forEach(assignee => params.append('assignees[]', assignee));
|
|
83
|
-
}
|
|
84
|
-
if (filters.due_date_gt)
|
|
85
|
-
params.append('due_date_gt', String(filters.due_date_gt));
|
|
86
|
-
if (filters.due_date_lt)
|
|
87
|
-
params.append('due_date_lt', String(filters.due_date_lt));
|
|
88
|
-
if (filters.date_created_gt)
|
|
89
|
-
params.append('date_created_gt', String(filters.date_created_gt));
|
|
90
|
-
if (filters.date_created_lt)
|
|
91
|
-
params.append('date_created_lt', String(filters.date_created_lt));
|
|
92
|
-
if (filters.date_updated_gt)
|
|
93
|
-
params.append('date_updated_gt', String(filters.date_updated_gt));
|
|
94
|
-
if (filters.date_updated_lt)
|
|
95
|
-
params.append('date_updated_lt', String(filters.date_updated_lt));
|
|
96
|
-
// Handle custom fields if present
|
|
97
|
-
if (filters.custom_fields) {
|
|
98
|
-
Object.entries(filters.custom_fields).forEach(([key, value]) => {
|
|
99
|
-
params.append(`custom_fields[${key}]`, String(value));
|
|
100
|
-
});
|
|
101
|
-
}
|
|
181
|
+
const params = this.buildTaskFilterParams(filters);
|
|
102
182
|
const response = await this.client.get(`/list/${listId}/task?${params.toString()}`);
|
|
103
183
|
return response.data.tasks;
|
|
104
184
|
});
|
|
@@ -171,27 +251,7 @@ export class TaskService extends BaseClickUpService {
|
|
|
171
251
|
this.logOperation('findTaskByName', { listId, taskName });
|
|
172
252
|
try {
|
|
173
253
|
const tasks = await this.getTasks(listId);
|
|
174
|
-
|
|
175
|
-
const normalizedSearchTerm = taskName.toLowerCase().trim();
|
|
176
|
-
// First try exact match
|
|
177
|
-
let matchingTask = tasks.find(task => task.name.toLowerCase().trim() === normalizedSearchTerm);
|
|
178
|
-
// If no exact match, try substring match
|
|
179
|
-
if (!matchingTask) {
|
|
180
|
-
matchingTask = tasks.find(task => task.name.toLowerCase().trim().includes(normalizedSearchTerm) ||
|
|
181
|
-
normalizedSearchTerm.includes(task.name.toLowerCase().trim()));
|
|
182
|
-
}
|
|
183
|
-
// If still no match and there are emoji characters, try matching without emoji
|
|
184
|
-
if (!matchingTask && /[\p{Emoji}]/u.test(normalizedSearchTerm)) {
|
|
185
|
-
// Remove emoji and try again (simple approximation)
|
|
186
|
-
const withoutEmoji = normalizedSearchTerm.replace(/[\p{Emoji}]/gu, '').trim();
|
|
187
|
-
matchingTask = tasks.find(task => {
|
|
188
|
-
const taskNameWithoutEmoji = task.name.toLowerCase().replace(/[\p{Emoji}]/gu, '').trim();
|
|
189
|
-
return taskNameWithoutEmoji === withoutEmoji ||
|
|
190
|
-
taskNameWithoutEmoji.includes(withoutEmoji) ||
|
|
191
|
-
withoutEmoji.includes(taskNameWithoutEmoji);
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
return matchingTask || null;
|
|
254
|
+
return this.findMatchingTask(tasks, taskName);
|
|
195
255
|
}
|
|
196
256
|
catch (error) {
|
|
197
257
|
throw this.handleError(error, `Failed to find task by name: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -217,28 +277,9 @@ export class TaskService extends BaseClickUpService {
|
|
|
217
277
|
let newStatus = availableStatuses.includes(currentStatus || '')
|
|
218
278
|
? currentStatus // Keep the same status if available in destination list
|
|
219
279
|
: destinationList.statuses?.[0]?.status; // Otherwise use the default (first) status
|
|
220
|
-
// Priority mapping: convert string priority to numeric value if needed
|
|
221
|
-
let priorityValue = null;
|
|
222
|
-
if (originalTask.priority) {
|
|
223
|
-
// If priority.id exists and is numeric, use that
|
|
224
|
-
if (originalTask.priority.id) {
|
|
225
|
-
priorityValue = parseInt(originalTask.priority.id);
|
|
226
|
-
// Ensure it's in the valid range 1-4
|
|
227
|
-
if (isNaN(priorityValue) || priorityValue < 1 || priorityValue > 4) {
|
|
228
|
-
priorityValue = null;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
280
|
// Prepare the task data for the new list
|
|
233
|
-
const taskData =
|
|
234
|
-
|
|
235
|
-
description: originalTask.description,
|
|
236
|
-
status: newStatus,
|
|
237
|
-
priority: priorityValue,
|
|
238
|
-
due_date: originalTask.due_date ? Number(originalTask.due_date) : undefined,
|
|
239
|
-
assignees: originalTask.assignees?.map(a => a.id) || [],
|
|
240
|
-
// Add any other relevant fields from the original task
|
|
241
|
-
};
|
|
281
|
+
const taskData = this.extractTaskData(originalTask);
|
|
282
|
+
taskData.status = newStatus;
|
|
242
283
|
// Create new task and delete old one in a single makeRequest call
|
|
243
284
|
return await this.makeRequest(async () => {
|
|
244
285
|
// First create the new task
|
|
@@ -269,15 +310,8 @@ export class TaskService extends BaseClickUpService {
|
|
|
269
310
|
try {
|
|
270
311
|
// Get the original task to duplicate
|
|
271
312
|
const originalTask = await this.getTask(taskId);
|
|
272
|
-
// Create a copy of the task data
|
|
273
|
-
const newTaskData = {
|
|
274
|
-
name: `${originalTask.name} (copy)`,
|
|
275
|
-
description: originalTask.description || '',
|
|
276
|
-
status: originalTask.status?.status,
|
|
277
|
-
priority: originalTask.priority?.id ? parseInt(originalTask.priority.id) : null,
|
|
278
|
-
due_date: originalTask.due_date ? new Date(originalTask.due_date).getTime() : undefined,
|
|
279
|
-
assignees: originalTask.assignees?.map(a => a.id) || []
|
|
280
|
-
};
|
|
313
|
+
// Create a copy of the task data with "(copy)" appended to the name
|
|
314
|
+
const newTaskData = this.extractTaskData(originalTask, `${originalTask.name} (copy)`);
|
|
281
315
|
// Create the new task in the specified list or original list
|
|
282
316
|
const targetListId = listId || originalTask.list.id;
|
|
283
317
|
return await this.createTask(targetListId, newTaskData);
|
|
@@ -1,4 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Common type definitions for ClickUp API entities
|
|
3
3
|
*/
|
|
4
|
-
|
|
4
|
+
// Helper function to validate and convert priority values
|
|
5
|
+
export function toTaskPriority(value) {
|
|
6
|
+
if (value === null)
|
|
7
|
+
return null;
|
|
8
|
+
if (value === undefined)
|
|
9
|
+
return undefined;
|
|
10
|
+
if (value === "null")
|
|
11
|
+
return null;
|
|
12
|
+
// Convert string to number if needed
|
|
13
|
+
const numValue = typeof value === 'string' ? parseInt(value, 10) : value;
|
|
14
|
+
// Validate it's a valid priority number
|
|
15
|
+
if (typeof numValue === 'number' && !isNaN(numValue) && [1, 2, 3, 4].includes(numValue)) {
|
|
16
|
+
return numValue;
|
|
17
|
+
}
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
package/build/services/shared.js
CHANGED
|
@@ -8,26 +8,26 @@ import { createClickUpServices } from './clickup/index.js';
|
|
|
8
8
|
import config from '../config.js';
|
|
9
9
|
import { Logger } from '../logger.js';
|
|
10
10
|
const logger = new Logger('SharedServices');
|
|
11
|
-
// Singleton
|
|
12
|
-
let
|
|
11
|
+
// Singleton instances
|
|
12
|
+
let clickUpServicesInstance = null;
|
|
13
13
|
/**
|
|
14
14
|
* Get or create the ClickUp services instance
|
|
15
15
|
*/
|
|
16
16
|
function getClickUpServices() {
|
|
17
|
-
if (!
|
|
17
|
+
if (!clickUpServicesInstance) {
|
|
18
18
|
logger.info('Creating shared ClickUp services singleton');
|
|
19
19
|
// Create the services instance
|
|
20
|
-
|
|
20
|
+
clickUpServicesInstance = createClickUpServices({
|
|
21
21
|
apiKey: config.clickupApiKey,
|
|
22
22
|
teamId: config.clickupTeamId
|
|
23
23
|
});
|
|
24
24
|
// Log what services were initialized with more clarity
|
|
25
25
|
logger.info('Services initialization complete', {
|
|
26
|
-
services: Object.keys(
|
|
26
|
+
services: Object.keys(clickUpServicesInstance).join(', '),
|
|
27
27
|
teamId: config.clickupTeamId
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
|
-
return
|
|
30
|
+
return clickUpServicesInstance;
|
|
31
31
|
}
|
|
32
32
|
// Create a single instance of ClickUp services to be shared
|
|
33
33
|
export const clickUpServices = getClickUpServices();
|
package/build/tools/folder.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* updating, and deleting folders in the ClickUp workspace hierarchy.
|
|
6
6
|
*/
|
|
7
7
|
import { clickUpServices } from '../services/shared.js';
|
|
8
|
+
import { sponsorService } from '../utils/sponsor-service.js';
|
|
8
9
|
// Use shared services instance
|
|
9
10
|
const { folder: folderService, workspace: workspaceService } = clickUpServices;
|
|
10
11
|
/**
|
|
@@ -12,7 +13,19 @@ const { folder: folderService, workspace: workspaceService } = clickUpServices;
|
|
|
12
13
|
*/
|
|
13
14
|
export const createFolderTool = {
|
|
14
15
|
name: "create_folder",
|
|
15
|
-
description:
|
|
16
|
+
description: `Purpose: Create a new folder in a ClickUp space for organizing related lists.
|
|
17
|
+
|
|
18
|
+
Valid Usage:
|
|
19
|
+
1. Provide spaceId (preferred) + folder name
|
|
20
|
+
2. Provide spaceName + folder name
|
|
21
|
+
|
|
22
|
+
Requirements:
|
|
23
|
+
- name: REQUIRED
|
|
24
|
+
- EITHER spaceId OR spaceName: REQUIRED
|
|
25
|
+
|
|
26
|
+
Notes:
|
|
27
|
+
- After creating a folder, you can add lists to it using create_list_in_folder
|
|
28
|
+
- Use override_statuses to set folder-specific statuses`,
|
|
16
29
|
inputSchema: {
|
|
17
30
|
type: "object",
|
|
18
31
|
properties: {
|
|
@@ -41,7 +54,18 @@ export const createFolderTool = {
|
|
|
41
54
|
*/
|
|
42
55
|
export const getFolderTool = {
|
|
43
56
|
name: "get_folder",
|
|
44
|
-
description:
|
|
57
|
+
description: `Purpose: Retrieve details about a specific folder including name, status, and metadata.
|
|
58
|
+
|
|
59
|
+
Valid Usage:
|
|
60
|
+
1. Use folderId alone (preferred)
|
|
61
|
+
2. Use folderName + (spaceId or spaceName)
|
|
62
|
+
|
|
63
|
+
Requirements:
|
|
64
|
+
- EITHER folderId OR (folderName + space information) is REQUIRED
|
|
65
|
+
- When using folderName, you MUST provide EITHER spaceId OR spaceName
|
|
66
|
+
|
|
67
|
+
Notes:
|
|
68
|
+
- Helps you understand folder structure before creating or updating lists`,
|
|
45
69
|
inputSchema: {
|
|
46
70
|
type: "object",
|
|
47
71
|
properties: {
|
|
@@ -70,7 +94,19 @@ export const getFolderTool = {
|
|
|
70
94
|
*/
|
|
71
95
|
export const updateFolderTool = {
|
|
72
96
|
name: "update_folder",
|
|
73
|
-
description:
|
|
97
|
+
description: `Purpose: Modify an existing folder's properties.
|
|
98
|
+
|
|
99
|
+
Valid Usage:
|
|
100
|
+
1. Use folderId alone (preferred)
|
|
101
|
+
2. Use folderName + (spaceId or spaceName)
|
|
102
|
+
|
|
103
|
+
Requirements:
|
|
104
|
+
- At least one update field (name or override_statuses) must be provided
|
|
105
|
+
- EITHER folderId OR (folderName + space information) is REQUIRED
|
|
106
|
+
- When using folderName, you MUST provide EITHER spaceId OR spaceName
|
|
107
|
+
|
|
108
|
+
Notes:
|
|
109
|
+
- Changes apply immediately to all lists within the folder`,
|
|
74
110
|
inputSchema: {
|
|
75
111
|
type: "object",
|
|
76
112
|
properties: {
|
|
@@ -107,7 +143,20 @@ export const updateFolderTool = {
|
|
|
107
143
|
*/
|
|
108
144
|
export const deleteFolderTool = {
|
|
109
145
|
name: "delete_folder",
|
|
110
|
-
description:
|
|
146
|
+
description: `Purpose: PERMANENTLY DELETE a folder and all its contents.
|
|
147
|
+
|
|
148
|
+
Valid Usage:
|
|
149
|
+
1. Use folderId alone (preferred and safest)
|
|
150
|
+
2. Use folderName + (spaceId or spaceName)
|
|
151
|
+
|
|
152
|
+
Requirements:
|
|
153
|
+
- EITHER folderId OR (folderName + space information) is REQUIRED
|
|
154
|
+
- When using folderName, you MUST provide EITHER spaceId OR spaceName
|
|
155
|
+
|
|
156
|
+
⚠️ CRITICAL WARNING:
|
|
157
|
+
- This action CANNOT be undone
|
|
158
|
+
- All lists and tasks within the folder will also be permanently deleted
|
|
159
|
+
- Using folderName is risky as names may not be unique across different spaces`,
|
|
111
160
|
inputSchema: {
|
|
112
161
|
type: "object",
|
|
113
162
|
properties: {
|
|
@@ -163,23 +212,18 @@ export async function handleCreateFolder(parameters) {
|
|
|
163
212
|
try {
|
|
164
213
|
// Create the folder
|
|
165
214
|
const newFolder = await folderService.createFolder(targetSpaceId, folderData);
|
|
166
|
-
return {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
},
|
|
176
|
-
message: `Folder "${newFolder.name}" created successfully`
|
|
177
|
-
}, null, 2)
|
|
178
|
-
}]
|
|
179
|
-
};
|
|
215
|
+
return sponsorService.createResponse({
|
|
216
|
+
id: newFolder.id,
|
|
217
|
+
name: newFolder.name,
|
|
218
|
+
space: {
|
|
219
|
+
id: newFolder.space.id,
|
|
220
|
+
name: newFolder.space.name
|
|
221
|
+
},
|
|
222
|
+
message: `Folder "${newFolder.name}" created successfully`
|
|
223
|
+
}, true);
|
|
180
224
|
}
|
|
181
225
|
catch (error) {
|
|
182
|
-
|
|
226
|
+
return sponsorService.createErrorResponse(`Failed to create folder: ${error.message}`);
|
|
183
227
|
}
|
|
184
228
|
}
|
|
185
229
|
/**
|
|
@@ -215,26 +259,17 @@ export async function handleGetFolder(parameters) {
|
|
|
215
259
|
try {
|
|
216
260
|
// Get the folder
|
|
217
261
|
const folder = await folderService.getFolder(targetFolderId);
|
|
218
|
-
return {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
name: list.name
|
|
227
|
-
})),
|
|
228
|
-
space: {
|
|
229
|
-
id: folder.space.id,
|
|
230
|
-
name: folder.space.name
|
|
231
|
-
}
|
|
232
|
-
}, null, 2)
|
|
233
|
-
}]
|
|
234
|
-
};
|
|
262
|
+
return sponsorService.createResponse({
|
|
263
|
+
id: folder.id,
|
|
264
|
+
name: folder.name,
|
|
265
|
+
space: {
|
|
266
|
+
id: folder.space.id,
|
|
267
|
+
name: folder.space.name
|
|
268
|
+
}
|
|
269
|
+
}, true);
|
|
235
270
|
}
|
|
236
271
|
catch (error) {
|
|
237
|
-
|
|
272
|
+
return sponsorService.createErrorResponse(`Failed to retrieve folder: ${error.message}`);
|
|
238
273
|
}
|
|
239
274
|
}
|
|
240
275
|
/**
|
|
@@ -280,23 +315,18 @@ export async function handleUpdateFolder(parameters) {
|
|
|
280
315
|
try {
|
|
281
316
|
// Update the folder
|
|
282
317
|
const updatedFolder = await folderService.updateFolder(targetFolderId, updateData);
|
|
283
|
-
return {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
},
|
|
293
|
-
message: `Folder "${updatedFolder.name}" updated successfully`
|
|
294
|
-
}, null, 2)
|
|
295
|
-
}]
|
|
296
|
-
};
|
|
318
|
+
return sponsorService.createResponse({
|
|
319
|
+
id: updatedFolder.id,
|
|
320
|
+
name: updatedFolder.name,
|
|
321
|
+
space: {
|
|
322
|
+
id: updatedFolder.space.id,
|
|
323
|
+
name: updatedFolder.space.name
|
|
324
|
+
},
|
|
325
|
+
message: `Folder "${updatedFolder.name}" updated successfully`
|
|
326
|
+
}, true);
|
|
297
327
|
}
|
|
298
328
|
catch (error) {
|
|
299
|
-
|
|
329
|
+
return sponsorService.createErrorResponse(`Failed to update folder: ${error.message}`);
|
|
300
330
|
}
|
|
301
331
|
}
|
|
302
332
|
/**
|
|
@@ -335,16 +365,12 @@ export async function handleDeleteFolder(parameters) {
|
|
|
335
365
|
const folderName = folder.name;
|
|
336
366
|
// Delete the folder
|
|
337
367
|
await folderService.deleteFolder(targetFolderId);
|
|
338
|
-
return {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
message: `Folder "${folderName}" deleted successfully`
|
|
343
|
-
}, null, 2)
|
|
344
|
-
}]
|
|
345
|
-
};
|
|
368
|
+
return sponsorService.createResponse({
|
|
369
|
+
success: true,
|
|
370
|
+
message: `Folder "${folderName || targetFolderId}" deleted successfully`
|
|
371
|
+
}, true);
|
|
346
372
|
}
|
|
347
373
|
catch (error) {
|
|
348
|
-
|
|
374
|
+
return sponsorService.createErrorResponse(`Failed to delete folder: ${error.message}`);
|
|
349
375
|
}
|
|
350
376
|
}
|