@taazkareem/clickup-mcp-server 0.4.74 → 0.5.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/README.md +11 -4
- package/build/index.js +0 -6
- package/build/server.js +9 -3
- package/build/services/clickup/task.js +167 -0
- package/build/tools/task/attachments.js +354 -0
- package/build/tools/task/bulk-operations.js +5 -1
- package/build/tools/task/handlers.js +37 -6
- package/build/tools/task/index.js +4 -2
- package/build/tools/task/main.js +11 -2
- package/build/tools/task/single-operations.js +66 -8
- package/build/tools/task/utilities.js +72 -11
- package/build/utils/sponsor-service.js +24 -5
- package/package.json +1 -1
- package/build/mcp-tools.js +0 -64
- package/build/server-state.js +0 -93
- package/build/server.log +0 -748
- package/build/tools/bulk-tasks.js +0 -36
- package/build/tools/debug.js +0 -76
- package/build/tools/logs.js +0 -55
- package/build/tools/task.js +0 -1554
- package/build/utils/params-utils.js +0 -39
- package/build/utils/sponsor-analytics.js +0 -100
- package/build/utils/sponsor-utils.js +0 -57
|
@@ -38,9 +38,9 @@ function buildUpdateData(params) {
|
|
|
38
38
|
/**
|
|
39
39
|
* Process a task identification validation, returning the task ID
|
|
40
40
|
*/
|
|
41
|
-
async function getTaskId(taskId, taskName, listName) {
|
|
42
|
-
validateTaskIdentification(taskId, taskName, listName);
|
|
43
|
-
return await resolveTaskIdWithValidation(taskId, taskName, listName);
|
|
41
|
+
async function getTaskId(taskId, taskName, listName, customTaskId) {
|
|
42
|
+
validateTaskIdentification(taskId, taskName, listName, customTaskId);
|
|
43
|
+
return await resolveTaskIdWithValidation(taskId, taskName, listName, customTaskId);
|
|
44
44
|
}
|
|
45
45
|
/**
|
|
46
46
|
* Process a list identification validation, returning the list ID
|
|
@@ -72,8 +72,8 @@ function buildTaskFilters(params) {
|
|
|
72
72
|
*/
|
|
73
73
|
async function mapTaskIds(tasks) {
|
|
74
74
|
return Promise.all(tasks.map(async (task) => {
|
|
75
|
-
validateTaskIdentification(task.taskId, task.taskName, task.listName);
|
|
76
|
-
return await resolveTaskIdWithValidation(task.taskId, task.taskName, task.listName);
|
|
75
|
+
validateTaskIdentification(task.taskId, task.taskName, task.listName, task.customTaskId);
|
|
76
|
+
return await resolveTaskIdWithValidation(task.taskId, task.taskName, task.listName, task.customTaskId);
|
|
77
77
|
}));
|
|
78
78
|
}
|
|
79
79
|
//=============================================================================
|
|
@@ -129,7 +129,8 @@ export async function duplicateTaskHandler(params) {
|
|
|
129
129
|
* Handler for getting a task
|
|
130
130
|
*/
|
|
131
131
|
export async function getTaskHandler(params) {
|
|
132
|
-
|
|
132
|
+
// resolveTaskIdWithValidation now auto-detects whether taskId is a regular ID or custom ID
|
|
133
|
+
const taskId = await getTaskId(params.taskId, params.taskName, params.listName, params.customTaskId);
|
|
133
134
|
return await taskService.getTask(taskId);
|
|
134
135
|
}
|
|
135
136
|
/**
|
|
@@ -155,6 +156,36 @@ export async function getTaskCommentsHandler(params) {
|
|
|
155
156
|
const { start, startId } = params;
|
|
156
157
|
return await taskService.getTaskComments(taskId, start, startId);
|
|
157
158
|
}
|
|
159
|
+
/**
|
|
160
|
+
* Handler for creating a task comment
|
|
161
|
+
*/
|
|
162
|
+
export async function createTaskCommentHandler(params) {
|
|
163
|
+
// Validate required parameters
|
|
164
|
+
if (!params.commentText) {
|
|
165
|
+
throw new Error('Comment text is required');
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
// Resolve the task ID
|
|
169
|
+
const taskId = await getTaskId(params.taskId, params.taskName, params.listName);
|
|
170
|
+
// Extract other parameters with defaults
|
|
171
|
+
const { commentText, notifyAll = false, assignee = null } = params;
|
|
172
|
+
// Create the comment
|
|
173
|
+
return await taskService.createTaskComment(taskId, commentText, notifyAll, assignee);
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
// If this is a task lookup error, provide more helpful message
|
|
177
|
+
if (error.message?.includes('not found') || error.message?.includes('identify task')) {
|
|
178
|
+
if (params.taskName) {
|
|
179
|
+
throw new Error(`Could not find task "${params.taskName}" in list "${params.listName}"`);
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
throw new Error(`Task with ID "${params.taskId}" not found`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// Otherwise, rethrow the original error
|
|
186
|
+
throw error;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
158
189
|
//=============================================================================
|
|
159
190
|
// BULK TASK OPERATIONS
|
|
160
191
|
//=============================================================================
|
|
@@ -6,13 +6,15 @@
|
|
|
6
6
|
// Re-export from main module
|
|
7
7
|
export * from './main.js';
|
|
8
8
|
// Re-export single task operation tools
|
|
9
|
-
export { createTaskTool, getTaskTool, getTasksTool, updateTaskTool, moveTaskTool, duplicateTaskTool, deleteTaskTool, getTaskCommentsTool } from './single-operations.js';
|
|
9
|
+
export { createTaskTool, getTaskTool, getTasksTool, updateTaskTool, moveTaskTool, duplicateTaskTool, deleteTaskTool, getTaskCommentsTool, createTaskCommentTool } from './single-operations.js';
|
|
10
10
|
// Re-export bulk task operation tools
|
|
11
11
|
export { createBulkTasksTool, updateBulkTasksTool, moveBulkTasksTool, deleteBulkTasksTool } from './bulk-operations.js';
|
|
12
|
+
// Re-export attachment tool
|
|
13
|
+
export { attachTaskFileTool, handleAttachTaskFile } from './attachments.js';
|
|
12
14
|
// Re-export handlers
|
|
13
15
|
export {
|
|
14
16
|
// Single task operation handlers
|
|
15
|
-
createTaskHandler, getTaskHandler, getTasksHandler, updateTaskHandler, moveTaskHandler, duplicateTaskHandler, deleteTaskHandler, getTaskCommentsHandler,
|
|
17
|
+
createTaskHandler, getTaskHandler, getTasksHandler, updateTaskHandler, moveTaskHandler, duplicateTaskHandler, deleteTaskHandler, getTaskCommentsHandler, createTaskCommentHandler,
|
|
16
18
|
// Bulk task operation handlers
|
|
17
19
|
createBulkTasksHandler, updateBulkTasksHandler, moveBulkTasksHandler, deleteBulkTasksHandler } from './handlers.js';
|
|
18
20
|
// Re-export utilities
|
package/build/tools/task/main.js
CHANGED
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { sponsorService } from '../../utils/sponsor-service.js';
|
|
8
8
|
// Import tool definitions
|
|
9
|
-
import { createTaskTool, getTaskTool, getTasksTool, updateTaskTool, moveTaskTool, duplicateTaskTool, deleteTaskTool, getTaskCommentsTool } from './single-operations.js';
|
|
9
|
+
import { createTaskTool, getTaskTool, getTasksTool, updateTaskTool, moveTaskTool, duplicateTaskTool, deleteTaskTool, getTaskCommentsTool, createTaskCommentTool } from './single-operations.js';
|
|
10
10
|
import { createBulkTasksTool, updateBulkTasksTool, moveBulkTasksTool, deleteBulkTasksTool } from './bulk-operations.js';
|
|
11
11
|
// Import handlers
|
|
12
|
-
import { createTaskHandler, getTaskHandler, getTasksHandler, updateTaskHandler, moveTaskHandler, duplicateTaskHandler, deleteTaskHandler, getTaskCommentsHandler, createBulkTasksHandler, updateBulkTasksHandler, moveBulkTasksHandler, deleteBulkTasksHandler } from './handlers.js';
|
|
12
|
+
import { createTaskHandler, getTaskHandler, getTasksHandler, updateTaskHandler, moveTaskHandler, duplicateTaskHandler, deleteTaskHandler, getTaskCommentsHandler, createTaskCommentHandler, createBulkTasksHandler, updateBulkTasksHandler, moveBulkTasksHandler, deleteBulkTasksHandler } from './handlers.js';
|
|
13
13
|
//=============================================================================
|
|
14
14
|
// HANDLER WRAPPER UTILITY
|
|
15
15
|
//=============================================================================
|
|
@@ -47,6 +47,14 @@ export const handleGetTaskComments = createHandlerWrapper(getTaskCommentsHandler
|
|
|
47
47
|
comments,
|
|
48
48
|
count: comments.length
|
|
49
49
|
}));
|
|
50
|
+
export const handleCreateTaskComment = createHandlerWrapper(createTaskCommentHandler, (comment) => ({
|
|
51
|
+
success: true,
|
|
52
|
+
message: "Comment added successfully",
|
|
53
|
+
comment: comment && typeof comment === 'object' ? comment : {
|
|
54
|
+
id: `generated-${Date.now()}`,
|
|
55
|
+
comment_text: typeof comment === 'string' ? comment : "Comment text unavailable"
|
|
56
|
+
}
|
|
57
|
+
}));
|
|
50
58
|
//=============================================================================
|
|
51
59
|
// BULK TASK OPERATIONS - HANDLER IMPLEMENTATIONS
|
|
52
60
|
//=============================================================================
|
|
@@ -81,6 +89,7 @@ export const taskTools = [
|
|
|
81
89
|
{ definition: duplicateTaskTool, handler: handleDuplicateTask },
|
|
82
90
|
{ definition: deleteTaskTool, handler: handleDeleteTask },
|
|
83
91
|
{ definition: getTaskCommentsTool, handler: handleGetTaskComments },
|
|
92
|
+
{ definition: createTaskCommentTool, handler: handleCreateTaskComment },
|
|
84
93
|
// Bulk task operations
|
|
85
94
|
{ definition: createBulkTasksTool, handler: handleCreateBulkTasks },
|
|
86
95
|
{ definition: updateBulkTasksTool, handler: handleUpdateBulkTasks },
|
|
@@ -119,7 +119,7 @@ Notes:
|
|
|
119
119
|
properties: {
|
|
120
120
|
taskId: {
|
|
121
121
|
type: "string",
|
|
122
|
-
description: "ID of the task to update (preferred).
|
|
122
|
+
description: "ID of the task to update (preferred). Works with both regular task IDs (9 characters) and custom IDs with uppercase prefixes (like 'DEV-1234')."
|
|
123
123
|
},
|
|
124
124
|
taskName: {
|
|
125
125
|
type: "string",
|
|
@@ -181,7 +181,7 @@ Warning:
|
|
|
181
181
|
properties: {
|
|
182
182
|
taskId: {
|
|
183
183
|
type: "string",
|
|
184
|
-
description: "ID of the task to move (preferred).
|
|
184
|
+
description: "ID of the task to move (preferred). Works with both regular task IDs (9 characters) and custom IDs with uppercase prefixes (like 'DEV-1234')."
|
|
185
185
|
},
|
|
186
186
|
taskName: {
|
|
187
187
|
type: "string",
|
|
@@ -228,7 +228,7 @@ Warning:
|
|
|
228
228
|
properties: {
|
|
229
229
|
taskId: {
|
|
230
230
|
type: "string",
|
|
231
|
-
description: "ID of task to duplicate (preferred).
|
|
231
|
+
description: "ID of task to duplicate (preferred). Works with both regular task IDs (9 characters) and custom IDs with uppercase prefixes (like 'DEV-1234')."
|
|
232
232
|
},
|
|
233
233
|
taskName: {
|
|
234
234
|
type: "string",
|
|
@@ -258,20 +258,24 @@ export const getTaskTool = {
|
|
|
258
258
|
description: `Purpose: Retrieve detailed information about a specific task.
|
|
259
259
|
|
|
260
260
|
Valid Usage:
|
|
261
|
-
1. Use taskId alone (preferred)
|
|
261
|
+
1. Use taskId alone (preferred) - works with both regular and custom IDs (like "DEV-1234")
|
|
262
262
|
2. Use taskName + listName
|
|
263
|
+
3. Use customTaskId for explicit custom ID lookup
|
|
263
264
|
|
|
264
265
|
Requirements:
|
|
265
266
|
- When using taskName, listName is REQUIRED
|
|
267
|
+
- When using customTaskId, listName is recommended for faster lookup
|
|
266
268
|
|
|
267
269
|
Note:
|
|
268
|
-
- Task names are only unique within a list, so the system needs to know which list to search in
|
|
270
|
+
- Task names are only unique within a list, so the system needs to know which list to search in
|
|
271
|
+
- Regular task IDs are always 9 characters long (e.g., "86b394eqa")
|
|
272
|
+
- Custom IDs have an uppercase prefix followed by a hyphen and number (e.g., "DEV-1234")`,
|
|
269
273
|
inputSchema: {
|
|
270
274
|
type: "object",
|
|
271
275
|
properties: {
|
|
272
276
|
taskId: {
|
|
273
277
|
type: "string",
|
|
274
|
-
description: "ID of task to retrieve (preferred).
|
|
278
|
+
description: "ID of task to retrieve (preferred). Works with both regular task IDs (9 characters) and custom IDs with uppercase prefixes (like 'DEV-1234'). The system automatically detects the ID format."
|
|
275
279
|
},
|
|
276
280
|
taskName: {
|
|
277
281
|
type: "string",
|
|
@@ -280,6 +284,10 @@ Note:
|
|
|
280
284
|
listName: {
|
|
281
285
|
type: "string",
|
|
282
286
|
description: "Name of list containing the task. REQUIRED when using taskName."
|
|
287
|
+
},
|
|
288
|
+
customTaskId: {
|
|
289
|
+
type: "string",
|
|
290
|
+
description: "Custom task ID (e.g., 'DEV-1234'). Only use this if you want to explicitly force custom ID lookup. In most cases, you can just use taskId which auto-detects ID format."
|
|
283
291
|
}
|
|
284
292
|
},
|
|
285
293
|
required: []
|
|
@@ -365,7 +373,7 @@ Warning:
|
|
|
365
373
|
properties: {
|
|
366
374
|
taskId: {
|
|
367
375
|
type: "string",
|
|
368
|
-
description: "ID of task to delete (preferred).
|
|
376
|
+
description: "ID of task to delete (preferred). Works with both regular task IDs (9 characters) and custom IDs with uppercase prefixes (like 'DEV-1234')."
|
|
369
377
|
},
|
|
370
378
|
taskName: {
|
|
371
379
|
type: "string",
|
|
@@ -398,7 +406,7 @@ Notes:
|
|
|
398
406
|
properties: {
|
|
399
407
|
taskId: {
|
|
400
408
|
type: "string",
|
|
401
|
-
description: "ID of task to retrieve comments for (preferred).
|
|
409
|
+
description: "ID of task to retrieve comments for (preferred). Works with both regular task IDs (9 characters) and custom IDs with uppercase prefixes (like 'DEV-1234')."
|
|
402
410
|
},
|
|
403
411
|
taskName: {
|
|
404
412
|
type: "string",
|
|
@@ -419,3 +427,53 @@ Notes:
|
|
|
419
427
|
}
|
|
420
428
|
}
|
|
421
429
|
};
|
|
430
|
+
/**
|
|
431
|
+
* Tool definition for creating a comment on a task
|
|
432
|
+
*/
|
|
433
|
+
export const createTaskCommentTool = {
|
|
434
|
+
name: "create_task_comment",
|
|
435
|
+
description: `Purpose: Create a comment on a ClickUp task.
|
|
436
|
+
|
|
437
|
+
Valid Usage:
|
|
438
|
+
1. Use taskId (preferred)
|
|
439
|
+
2. Use taskName + listName
|
|
440
|
+
|
|
441
|
+
Requirements:
|
|
442
|
+
- EITHER taskId OR (taskName + listName) is REQUIRED
|
|
443
|
+
- commentText is REQUIRED
|
|
444
|
+
|
|
445
|
+
Notes:
|
|
446
|
+
- When using taskName, providing listName helps locate the correct task
|
|
447
|
+
- Set notifyAll to true to send notifications to all task assignees
|
|
448
|
+
- Use assignee to assign the comment to a specific user (optional)`,
|
|
449
|
+
inputSchema: {
|
|
450
|
+
type: "object",
|
|
451
|
+
properties: {
|
|
452
|
+
taskId: {
|
|
453
|
+
type: "string",
|
|
454
|
+
description: "ID of task to comment on (preferred). Works with both regular task IDs (9 characters) and custom IDs with uppercase prefixes (like 'DEV-1234')."
|
|
455
|
+
},
|
|
456
|
+
taskName: {
|
|
457
|
+
type: "string",
|
|
458
|
+
description: "Name of task to comment on. When using this parameter, you MUST also provide listName."
|
|
459
|
+
},
|
|
460
|
+
listName: {
|
|
461
|
+
type: "string",
|
|
462
|
+
description: "Name of list containing the task. REQUIRED when using taskName."
|
|
463
|
+
},
|
|
464
|
+
commentText: {
|
|
465
|
+
type: "string",
|
|
466
|
+
description: "REQUIRED: Text content of the comment to create."
|
|
467
|
+
},
|
|
468
|
+
notifyAll: {
|
|
469
|
+
type: "boolean",
|
|
470
|
+
description: "Whether to notify all assignees. Default is false."
|
|
471
|
+
},
|
|
472
|
+
assignee: {
|
|
473
|
+
type: "number",
|
|
474
|
+
description: "Optional user ID to assign the comment to."
|
|
475
|
+
}
|
|
476
|
+
},
|
|
477
|
+
required: ["commentText"]
|
|
478
|
+
}
|
|
479
|
+
};
|
|
@@ -32,14 +32,14 @@ export function formatTaskData(task, additional = {}) {
|
|
|
32
32
|
//=============================================================================
|
|
33
33
|
/**
|
|
34
34
|
* Validates task identification parameters
|
|
35
|
-
* Ensures either taskId
|
|
35
|
+
* Ensures either taskId, customTaskId, or both taskName and listName are provided
|
|
36
36
|
*/
|
|
37
|
-
export function validateTaskIdentification(taskId, taskName, listName) {
|
|
38
|
-
if (!taskId && !taskName) {
|
|
39
|
-
throw new Error("Either taskId or taskName must be provided");
|
|
37
|
+
export function validateTaskIdentification(taskId, taskName, listName, customTaskId) {
|
|
38
|
+
if (!taskId && !taskName && !customTaskId) {
|
|
39
|
+
throw new Error("Either taskId, customTaskId, or taskName must be provided");
|
|
40
40
|
}
|
|
41
|
-
if (!taskId && taskName && !listName) {
|
|
42
|
-
throw new Error("
|
|
41
|
+
if (!taskId && !customTaskId && taskName && !listName) {
|
|
42
|
+
throw new Error("listName is required when using taskName");
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
/**
|
|
@@ -84,18 +84,79 @@ export function parseBulkOptions(rawOptions) {
|
|
|
84
84
|
return rawOptions;
|
|
85
85
|
}
|
|
86
86
|
//=============================================================================
|
|
87
|
+
// ID DETECTION UTILITIES
|
|
88
|
+
//=============================================================================
|
|
89
|
+
/**
|
|
90
|
+
* Determines if an ID is a custom ID based on its format
|
|
91
|
+
* Custom IDs typically have an uppercase prefix followed by a hyphen and number (e.g., DEV-1234)
|
|
92
|
+
* Regular task IDs are always 9 characters long
|
|
93
|
+
*
|
|
94
|
+
* @param id The task ID to check
|
|
95
|
+
* @returns True if the ID appears to be a custom ID
|
|
96
|
+
*/
|
|
97
|
+
export function isCustomTaskId(id) {
|
|
98
|
+
if (!id)
|
|
99
|
+
return false;
|
|
100
|
+
// Regular task IDs are exactly 9 characters
|
|
101
|
+
if (id.length === 9) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
// Custom IDs have an uppercase prefix followed by a hyphen and numbers
|
|
105
|
+
const customIdPattern = /^[A-Z]+-\d+$/;
|
|
106
|
+
return customIdPattern.test(id);
|
|
107
|
+
}
|
|
108
|
+
//=============================================================================
|
|
87
109
|
// ID RESOLUTION UTILITIES
|
|
88
110
|
//=============================================================================
|
|
89
111
|
/**
|
|
90
|
-
* Resolves a task ID from
|
|
112
|
+
* Resolves a task ID from direct ID, custom ID, or name
|
|
91
113
|
* Handles validation and throws appropriate errors
|
|
92
114
|
*/
|
|
93
|
-
export async function resolveTaskIdWithValidation(taskId, taskName, listName) {
|
|
115
|
+
export async function resolveTaskIdWithValidation(taskId, taskName, listName, customTaskId) {
|
|
94
116
|
// Validate parameters
|
|
95
|
-
validateTaskIdentification(taskId, taskName, listName);
|
|
96
|
-
// If
|
|
97
|
-
if (
|
|
117
|
+
validateTaskIdentification(taskId, taskName, listName, customTaskId);
|
|
118
|
+
// If customTaskId is explicitly provided, use it
|
|
119
|
+
if (customTaskId) {
|
|
120
|
+
const { task: taskService } = clickUpServices;
|
|
121
|
+
try {
|
|
122
|
+
// First try to get the task by custom ID
|
|
123
|
+
// If listName is provided, we can also look up in a specific list for better performance
|
|
124
|
+
let listId;
|
|
125
|
+
if (listName) {
|
|
126
|
+
listId = await resolveListIdWithValidation(undefined, listName);
|
|
127
|
+
}
|
|
128
|
+
// Look up by custom ID
|
|
129
|
+
const foundTask = await taskService.getTaskByCustomId(customTaskId, listId);
|
|
130
|
+
return foundTask.id;
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
throw new Error(`Task with custom ID "${customTaskId}" not found`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// If taskId is provided, check if it looks like a custom ID
|
|
137
|
+
if (taskId) {
|
|
138
|
+
if (isCustomTaskId(taskId)) {
|
|
139
|
+
console.log(`Detected task ID "${taskId}" as a custom ID, using custom ID lookup`);
|
|
140
|
+
// If it looks like a custom ID, try to get it as a custom ID first
|
|
141
|
+
const { task: taskService } = clickUpServices;
|
|
142
|
+
try {
|
|
143
|
+
// Look up by custom ID
|
|
144
|
+
let listId;
|
|
145
|
+
if (listName) {
|
|
146
|
+
listId = await resolveListIdWithValidation(undefined, listName);
|
|
147
|
+
}
|
|
148
|
+
const foundTask = await taskService.getTaskByCustomId(taskId, listId);
|
|
149
|
+
return foundTask.id;
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
// If it fails as a custom ID, try as a regular ID
|
|
153
|
+
console.log(`Failed to find task with custom ID "${taskId}", falling back to regular ID`);
|
|
154
|
+
return taskId;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// Regular task ID
|
|
98
158
|
return taskId;
|
|
159
|
+
}
|
|
99
160
|
// At this point we know we have taskName and listName (validation ensures this)
|
|
100
161
|
// Find the list ID from its name
|
|
101
162
|
const listId = await resolveListIdWithValidation(undefined, listName);
|
|
@@ -30,16 +30,35 @@ export class SponsorService {
|
|
|
30
30
|
*/
|
|
31
31
|
createResponse(data, includeSponsorMessage = false) {
|
|
32
32
|
const content = [];
|
|
33
|
+
// Special handling for workspace hierarchy which contains a preformatted tree
|
|
34
|
+
if (data && typeof data === 'object' && 'hierarchy' in data && typeof data.hierarchy === 'string') {
|
|
35
|
+
// Handle workspace hierarchy specially - it contains a preformatted tree
|
|
36
|
+
content.push({
|
|
37
|
+
type: "text",
|
|
38
|
+
text: data.hierarchy
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
else if (typeof data === 'string') {
|
|
42
|
+
// If it's already a string, use it directly
|
|
43
|
+
content.push({
|
|
44
|
+
type: "text",
|
|
45
|
+
text: data
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
// Otherwise, stringify the JSON object
|
|
50
|
+
content.push({
|
|
51
|
+
type: "text",
|
|
52
|
+
text: JSON.stringify(data, null, 2)
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
// Then add sponsorship message if enabled
|
|
33
56
|
if (this.isEnabled && includeSponsorMessage) {
|
|
34
57
|
content.push({
|
|
35
58
|
type: "text",
|
|
36
|
-
text:
|
|
59
|
+
text: `\n\n❤️ Support this project by sponsoring the developer at ${this.sponsorUrl}`
|
|
37
60
|
});
|
|
38
61
|
}
|
|
39
|
-
content.push({
|
|
40
|
-
type: "text",
|
|
41
|
-
text: JSON.stringify(data, null, 2)
|
|
42
|
-
});
|
|
43
62
|
return { content };
|
|
44
63
|
}
|
|
45
64
|
/**
|
package/package.json
CHANGED
package/build/mcp-tools.js
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { Logger } from "./logger.js";
|
|
2
|
-
// Create a logger instance
|
|
3
|
-
const logger = new Logger('MCPTools');
|
|
4
|
-
/**
|
|
5
|
-
* Register a handler for a tool that may receive JSON string parameters
|
|
6
|
-
* This wrapper ensures that array and object parameters are properly parsed
|
|
7
|
-
*
|
|
8
|
-
* @param server MCP Server instance
|
|
9
|
-
* @param name Tool name
|
|
10
|
-
* @param handler Handler function
|
|
11
|
-
*/
|
|
12
|
-
export function registerToolHandler(server, name, handler) {
|
|
13
|
-
// Create a wrapper handler that pre-processes parameters
|
|
14
|
-
const wrappedHandler = async (params) => {
|
|
15
|
-
logger.debug(`Processing parameters for tool ${name}`, { params });
|
|
16
|
-
try {
|
|
17
|
-
// Process the parameters before passing them to the actual handler
|
|
18
|
-
const processedParams = {};
|
|
19
|
-
// Process each parameter - try to parse strings that might be JSON
|
|
20
|
-
for (const [key, value] of Object.entries(params)) {
|
|
21
|
-
if (typeof value === 'string') {
|
|
22
|
-
try {
|
|
23
|
-
// Check if this might be a JSON array or object
|
|
24
|
-
if ((value.startsWith('[') && value.endsWith(']')) ||
|
|
25
|
-
(value.startsWith('{') && value.endsWith('}'))) {
|
|
26
|
-
try {
|
|
27
|
-
processedParams[key] = JSON.parse(value);
|
|
28
|
-
logger.debug(`Parsed JSON parameter: ${key}`, { original: value, parsed: processedParams[key] });
|
|
29
|
-
}
|
|
30
|
-
catch (parseError) {
|
|
31
|
-
// If parsing fails, use the original string
|
|
32
|
-
processedParams[key] = value;
|
|
33
|
-
logger.debug(`Failed to parse JSON for parameter: ${key}, using original`, { error: parseError.message });
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
processedParams[key] = value;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
catch (error) {
|
|
41
|
-
// If there's any error, use the original value
|
|
42
|
-
processedParams[key] = value;
|
|
43
|
-
logger.debug(`Error processing parameter: ${key}`, { error: error.message });
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
// Non-string values are used as-is
|
|
48
|
-
processedParams[key] = value;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
logger.debug(`Processed parameters for tool ${name}`, { processedParams });
|
|
52
|
-
// Call the original handler with processed parameters
|
|
53
|
-
return handler(processedParams);
|
|
54
|
-
}
|
|
55
|
-
catch (error) {
|
|
56
|
-
logger.error(`Error in wrapped handler for tool ${name}:`, { error: error.stack || error.message });
|
|
57
|
-
throw error;
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
// Use setRequestHandler to register the wrapped handler
|
|
61
|
-
logger.info(`Registering wrapped handler for tool: ${name}`);
|
|
62
|
-
// Override the tool's handler in the CallTool switch statement
|
|
63
|
-
// The server.ts file will use the switch case to call this handler
|
|
64
|
-
}
|
package/build/server-state.js
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Server State Management Module
|
|
3
|
-
*
|
|
4
|
-
* This module provides shared state management for the MCP server,
|
|
5
|
-
* particularly for controlling shutdown behavior and tracking busy states.
|
|
6
|
-
*/
|
|
7
|
-
// State variables
|
|
8
|
-
let serverBusyState = false; // Tracks if server is doing critical work
|
|
9
|
-
let gracePeriodActive = false; // Tracks if we're in post-initialization grace period
|
|
10
|
-
let gracePeriodTimer = null;
|
|
11
|
-
export const GRACE_PERIOD_MS = 10000; // 10 second grace period after startup
|
|
12
|
-
/**
|
|
13
|
-
* Logging helper that avoids circular dependency
|
|
14
|
-
*/
|
|
15
|
-
function safeLog(level, message, data) {
|
|
16
|
-
// Use console as a fallback to avoid circular dependency with logger
|
|
17
|
-
const timestamp = new Date().toISOString();
|
|
18
|
-
if (level === 'error') {
|
|
19
|
-
console.error(`[${timestamp}] ${level.toUpperCase()}: ${message}`, data || '');
|
|
20
|
-
}
|
|
21
|
-
else if (level === 'debug' && process.env.DEBUG) {
|
|
22
|
-
console.debug(`[${timestamp}] ${level.toUpperCase()}: ${message}`, data || '');
|
|
23
|
-
}
|
|
24
|
-
else if (level !== 'debug') {
|
|
25
|
-
console.log(`[${timestamp}] ${level.toUpperCase()}: ${message}`, data || '');
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Set the server as busy doing critical work that shouldn't be interrupted
|
|
30
|
-
* @param busy Whether the server is currently busy with critical operations
|
|
31
|
-
*/
|
|
32
|
-
export function setServerBusy(busy) {
|
|
33
|
-
serverBusyState = busy;
|
|
34
|
-
safeLog('debug', `Server busy state set to: ${busy}`);
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Start grace period after initialization to prevent immediate shutdown
|
|
38
|
-
*/
|
|
39
|
-
export function startGracePeriod() {
|
|
40
|
-
gracePeriodActive = true;
|
|
41
|
-
safeLog('debug', `Starting ${GRACE_PERIOD_MS}ms grace period to prevent premature shutdown`);
|
|
42
|
-
if (gracePeriodTimer) {
|
|
43
|
-
clearTimeout(gracePeriodTimer);
|
|
44
|
-
}
|
|
45
|
-
gracePeriodTimer = setTimeout(() => {
|
|
46
|
-
gracePeriodActive = false;
|
|
47
|
-
safeLog('debug', 'Grace period ended, server will now respond to shutdown signals');
|
|
48
|
-
gracePeriodTimer = null;
|
|
49
|
-
}, GRACE_PERIOD_MS);
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Cancel the grace period if needed
|
|
53
|
-
*/
|
|
54
|
-
export function cancelGracePeriod() {
|
|
55
|
-
if (gracePeriodTimer) {
|
|
56
|
-
clearTimeout(gracePeriodTimer);
|
|
57
|
-
gracePeriodTimer = null;
|
|
58
|
-
}
|
|
59
|
-
gracePeriodActive = false;
|
|
60
|
-
safeLog('debug', 'Grace period canceled, server will now respond to shutdown signals');
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Check if the server should ignore shutdown signals
|
|
64
|
-
* @returns true if shutdown signals should be ignored
|
|
65
|
-
*/
|
|
66
|
-
export function shouldIgnoreShutdown() {
|
|
67
|
-
// Ignore shutdown if explicitly configured via environment variable
|
|
68
|
-
if (process.env.FORCE_KEEP_ALIVE === 'true') {
|
|
69
|
-
return true;
|
|
70
|
-
}
|
|
71
|
-
// Ignore shutdown during the grace period after startup
|
|
72
|
-
if (gracePeriodActive) {
|
|
73
|
-
return true;
|
|
74
|
-
}
|
|
75
|
-
// Ignore shutdown if the server is doing critical work
|
|
76
|
-
if (serverBusyState) {
|
|
77
|
-
return true;
|
|
78
|
-
}
|
|
79
|
-
// Otherwise, allow normal shutdown
|
|
80
|
-
return false;
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Check if grace period is currently active
|
|
84
|
-
*/
|
|
85
|
-
export function isGracePeriodActive() {
|
|
86
|
-
return gracePeriodActive;
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Check if server is currently in busy state
|
|
90
|
-
*/
|
|
91
|
-
export function isServerBusy() {
|
|
92
|
-
return serverBusyState;
|
|
93
|
-
}
|