@taazkareem/clickup-mcp-server 0.4.49 → 0.4.52

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/index.js CHANGED
@@ -1,136 +1,69 @@
1
1
  #!/usr/bin/env node
2
+ /**
3
+ * ClickUp MCP Server - A Model Context Protocol server for ClickUp integration
4
+ *
5
+ * This server enables AI applications to interact with ClickUp through a standardized protocol,
6
+ * allowing AI assistants to manage tasks, lists, and folders in ClickUp workspaces.
7
+ *
8
+ * Key capabilities include:
9
+ *
10
+ * Task Management:
11
+ * - Create, update, move and duplicate tasks with rich description support
12
+ * - Find tasks by name with smart disambiguation
13
+ * - Bulk task creation for efficient workflow setup
14
+ * - Comprehensive filtering and sorting options
15
+ *
16
+ * Workspace Organization:
17
+ * - Navigate and discover workspace structure with hierarchical views
18
+ * - Create and manage lists and folders with proper nesting
19
+ * - Smart name-based lookups that eliminate the need for IDs
20
+ * - Support for priorities, statuses, and due dates
21
+ *
22
+ * AI-Enhanced Capabilities:
23
+ * - Task summarization and status grouping for project overviews
24
+ * - Priority analysis and optimization for workload balancing
25
+ * - Detailed task description generation with structured content
26
+ * - Task relationship identification for dependency management
27
+ *
28
+ * Technical Features:
29
+ * - Full markdown support for rich text content
30
+ * - Secure credential handling through configuration
31
+ * - Comprehensive error reporting and validation
32
+ * - Name-based entity resolution with fuzzy matching
33
+ *
34
+ * This implementation follows the Model Context Protocol specification and
35
+ * is designed to be used with AI assistants that support MCP.
36
+ */
2
37
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
38
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
- import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
39
+ import { CallToolRequestSchema, ListToolsRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
5
40
  import { ClickUpService } from "./services/clickup.js";
6
41
  import config from "./config.js";
7
- import { handleWorkspaceHierarchy, handleCreateTask } from "./handlers/tools.js";
8
- import { handleSummarizeTasks, handleAnalyzeTaskPriorities, handleGenerateDescription } from "./handlers/prompts.js";
9
- import { getAllTasks } from "./utils/resolvers.js";
10
- console.log('Server starting up...');
11
- console.log('Config loaded:', {
12
- clickupApiKey: config.clickupApiKey ? '***' : 'missing',
13
- teamId: config.teamId || 'missing'
14
- });
15
- // Create and configure the server
16
- const server = new Server({
17
- name: "clickup",
18
- version: "0.1.0",
19
- description: "ClickUp MCP Server"
20
- }, {
21
- capabilities: {
22
- resources: {
23
- canList: true,
24
- canRead: true,
25
- schemes: ["clickup"]
26
- },
27
- tools: {
28
- canExecute: true,
29
- tools: {
30
- workspace_hierarchy: {
31
- description: "List complete hierarchy of the ClickUp workspace"
32
- },
33
- create_task: {
34
- description: "Create a new task in ClickUp"
35
- },
36
- create_bulk_tasks: {
37
- description: "Create multiple tasks in a ClickUp list"
38
- },
39
- create_list: {
40
- description: "Create a new list in a ClickUp space"
41
- },
42
- create_folder: {
43
- description: "Create a new folder in a ClickUp space"
44
- },
45
- create_list_in_folder: {
46
- description: "Create a new list in a ClickUp folder"
47
- },
48
- move_task: {
49
- description: "Move a task to a different list"
50
- },
51
- duplicate_task: {
52
- description: "Duplicate a task to a list"
53
- },
54
- update_task: {
55
- description: "Update an existing task in ClickUp"
56
- }
57
- }
58
- },
59
- prompts: {
60
- canExecute: true,
61
- prompts: {
62
- summarize_tasks: {
63
- description: "Summarize all ClickUp tasks"
64
- },
65
- analyze_task_priorities: {
66
- description: "Analyze task priorities"
67
- },
68
- generate_description: {
69
- description: "Generate a detailed task description"
70
- }
71
- }
72
- }
73
- }
74
- });
75
42
  // Initialize ClickUp service
76
- console.log('Initializing ClickUp service...');
77
- const clickup = ClickUpService.initialize(config.clickupApiKey);
78
- console.log('Creating MCP server...');
79
- console.log('MCP server created');
43
+ const clickup = ClickUpService.initialize(config.clickupApiKey, config.clickupTeamId);
80
44
  /**
81
- * Handler for listing available ClickUp tasks as resources.
45
+ * Create an MCP server with capabilities for tools and prompts.
46
+ * Resources have been removed as they are being replaced with direct tool calls.
82
47
  */
83
- server.setRequestHandler(ListResourcesRequestSchema, async () => {
84
- console.log('Handling ListResources request');
85
- try {
86
- const { tasks, spaces } = await getAllTasks(clickup, config.teamId);
87
- return {
88
- resources: tasks.map(task => ({
89
- uri: `clickup://task/${task.id}`,
90
- mimeType: "application/json",
91
- name: task.name,
92
- description: task.description || `Task in ${task.list.name} (${task.space.name})`,
93
- tags: []
94
- }))
95
- };
96
- }
97
- catch (error) {
98
- console.error('Error in ListResources:', error);
99
- throw error;
100
- }
101
- });
102
- /**
103
- * Handler for reading the contents of a specific ClickUp task.
104
- */
105
- server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
106
- try {
107
- const url = new URL(request.params.uri);
108
- const taskId = url.pathname.replace(/^\/task\//, '');
109
- const task = await clickup.getTask(taskId);
110
- return {
111
- contents: [{
112
- uri: request.params.uri,
113
- mimeType: "application/json",
114
- text: JSON.stringify(task, null, 2),
115
- tags: []
116
- }]
117
- };
118
- }
119
- catch (error) {
120
- console.error('Error reading resource:', error);
121
- throw error;
122
- }
48
+ const server = new Server({
49
+ name: "clickup-mcp-server",
50
+ version: "0.4.50",
51
+ }, {
52
+ capabilities: {
53
+ tools: {},
54
+ prompts: {},
55
+ },
123
56
  });
124
57
  /**
125
- * Handler for listing available tools.
58
+ * Handler that lists available tools.
59
+ * Exposes tools for listing spaces, creating tasks, and updating tasks.
126
60
  */
127
61
  server.setRequestHandler(ListToolsRequestSchema, async () => {
128
- console.log('Handling ListTools request');
129
62
  return {
130
63
  tools: [
131
64
  {
132
- name: "workspace_hierarchy",
133
- description: "List complete hierarchy of the ClickUp workspace",
65
+ name: "get_workspace_hierarchy",
66
+ description: "Retrieve the complete ClickUp workspace hierarchy, including all spaces, folders, and lists with their IDs, names, and hierarchical paths. Call this tool only when you need to discover the workspace structure and don't already have this information from recent context. Avoid using for repeated lookups of the same information.",
134
67
  inputSchema: {
135
68
  type: "object",
136
69
  properties: {},
@@ -139,37 +72,41 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
139
72
  },
140
73
  {
141
74
  name: "create_task",
142
- description: "Create a new task in ClickUp",
75
+ description: "Create a single task in a ClickUp list. Use this tool for individual task creation only. For multiple tasks, use create_bulk_tasks instead. The tool finds lists by name (case-insensitive) so explicit list IDs aren't required. When creating a task, you must provide either a listId or listName.",
143
76
  inputSchema: {
144
77
  type: "object",
145
78
  properties: {
146
79
  listId: {
147
80
  type: "string",
148
- description: "ID of the list to create the task in (optional if listName is provided)"
81
+ description: "ID of the list to create the task in (optional if using listName instead)"
149
82
  },
150
83
  listName: {
151
84
  type: "string",
152
- description: "Name of the list to create the task in (optional if listId is provided)"
85
+ description: "Name of the list to create the task in - will automatically find the list by name (optional if using listId instead)"
153
86
  },
154
87
  name: {
155
88
  type: "string",
156
- description: "Name of the task"
89
+ description: "Name of the task. Put a relevant emoji followed by a blank space before the name."
157
90
  },
158
91
  description: {
159
92
  type: "string",
160
- description: "Description of the task"
93
+ description: "Plain text description for the task"
94
+ },
95
+ markdown_description: {
96
+ type: "string",
97
+ description: "Markdown formatted description for the task. If provided, this takes precedence over description"
161
98
  },
162
99
  status: {
163
100
  type: "string",
164
- description: "Status of the task"
101
+ description: "OPTIONAL: Override the default ClickUp status. In most cases, you should omit this to use ClickUp defaults"
165
102
  },
166
103
  priority: {
167
104
  type: "number",
168
- description: "Priority of the task (1-4)"
105
+ description: "Priority of the task (1-4), where 1 is urgent/highest priority and 4 is lowest priority. Only set this when the user explicitly requests a priority level."
169
106
  },
170
107
  dueDate: {
171
108
  type: "string",
172
- description: "Due date of the task (ISO string)"
109
+ description: "Due date of the task (Unix timestamp in milliseconds). Convert dates to this format before submitting."
173
110
  }
174
111
  },
175
112
  required: ["name"]
@@ -177,43 +114,47 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
177
114
  },
178
115
  {
179
116
  name: "create_bulk_tasks",
180
- description: "Create multiple tasks in a ClickUp list",
117
+ description: "Create multiple tasks in a ClickUp list simultaneously. Use this tool when you need to add several related tasks in one operation. The tool finds lists by name (case-insensitive), so explicit list IDs aren't required. More efficient than creating tasks one by one for batch operations.",
181
118
  inputSchema: {
182
119
  type: "object",
183
120
  properties: {
184
121
  listId: {
185
122
  type: "string",
186
- description: "ID of the list to create the tasks in (optional if listName is provided)"
123
+ description: "ID of the list to create the tasks in (optional if using listName instead)"
187
124
  },
188
125
  listName: {
189
126
  type: "string",
190
- description: "Name of the list to create the tasks in (optional if listId is provided)"
127
+ description: "Name of the list to create the tasks in - will automatically find the list by name (optional if using listId instead)"
191
128
  },
192
129
  tasks: {
193
130
  type: "array",
194
- description: "Array of tasks to create",
131
+ description: "Array of tasks to create (at least one task required)",
195
132
  items: {
196
133
  type: "object",
197
134
  properties: {
198
135
  name: {
199
136
  type: "string",
200
- description: "Name of the task"
137
+ description: "Name of the task. Consider adding a relevant emoji before the name."
201
138
  },
202
139
  description: {
203
140
  type: "string",
204
- description: "Description of the task"
141
+ description: "Plain text description for the task"
142
+ },
143
+ markdown_description: {
144
+ type: "string",
145
+ description: "Markdown formatted description for the task. If provided, this takes precedence over description"
205
146
  },
206
147
  status: {
207
148
  type: "string",
208
- description: "Status of the task"
149
+ description: "OPTIONAL: Override the default ClickUp status. In most cases, you should omit this to use ClickUp defaults"
209
150
  },
210
151
  priority: {
211
152
  type: "number",
212
- description: "Priority level (1-4)"
153
+ description: "Priority level (1-4), where 1 is urgent/highest priority and 4 is lowest priority. Only set when explicitly requested."
213
154
  },
214
155
  dueDate: {
215
156
  type: "string",
216
- description: "Due date (ISO string)"
157
+ description: "Due date (Unix timestamp in milliseconds). Convert dates to this format before submitting."
217
158
  },
218
159
  assignees: {
219
160
  type: "array",
@@ -232,17 +173,17 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
232
173
  },
233
174
  {
234
175
  name: "create_list",
235
- description: "Create a new list in a ClickUp space",
176
+ description: "Create a new list directly in a ClickUp space. Use this tool when you need a top-level list not nested inside a folder. The tool can find spaces by name, so explicit space IDs aren't required. For creating lists inside folders, use create_list_in_folder instead.",
236
177
  inputSchema: {
237
178
  type: "object",
238
179
  properties: {
239
180
  spaceId: {
240
181
  type: "string",
241
- description: "ID of the space to create the list in (optional if spaceName is provided)"
182
+ description: "ID of the space to create the list in (optional if using spaceName instead)"
242
183
  },
243
184
  spaceName: {
244
185
  type: "string",
245
- description: "Name of the space to create the list in (optional if spaceId is provided)"
186
+ description: "Name of the space to create the list in - will automatically find the space by name (optional if using spaceId instead)"
246
187
  },
247
188
  name: {
248
189
  type: "string",
@@ -254,11 +195,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
254
195
  },
255
196
  dueDate: {
256
197
  type: "string",
257
- description: "Due date for the list (ISO string)"
198
+ description: "Due date for the list (Unix timestamp in milliseconds). Convert dates to this format before submitting."
258
199
  },
259
200
  priority: {
260
201
  type: "number",
261
- description: "Priority of the list (1-4)"
202
+ description: "Priority of the list (1-4), where 1 is urgent/highest priority and 4 is lowest priority. Only set when explicitly requested."
262
203
  },
263
204
  assignee: {
264
205
  type: "number",
@@ -274,17 +215,17 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
274
215
  },
275
216
  {
276
217
  name: "create_folder",
277
- description: "Create a new folder in a ClickUp space",
218
+ description: "Create a new folder in a ClickUp space for organizing related lists. Use this tool when you need to group multiple lists together. The tool can find spaces by name, so explicit space IDs aren't required. After creating a folder, you can add lists to it using create_list_in_folder.",
278
219
  inputSchema: {
279
220
  type: "object",
280
221
  properties: {
281
222
  spaceId: {
282
223
  type: "string",
283
- description: "ID of the space to create the folder in (optional if spaceName is provided)"
224
+ description: "ID of the space to create the folder in (optional if using spaceName instead)"
284
225
  },
285
226
  spaceName: {
286
227
  type: "string",
287
- description: "Name of the space to create the folder in (optional if spaceId is provided)"
228
+ description: "Name of the space to create the folder in - will automatically find the space by name (optional if using spaceId instead)"
288
229
  },
289
230
  name: {
290
231
  type: "string",
@@ -292,7 +233,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
292
233
  },
293
234
  override_statuses: {
294
235
  type: "boolean",
295
- description: "Whether to override space statuses"
236
+ description: "Whether to override space statuses with folder-specific statuses"
296
237
  }
297
238
  },
298
239
  required: ["name"]
@@ -300,25 +241,25 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
300
241
  },
301
242
  {
302
243
  name: "create_list_in_folder",
303
- description: "Create a new list in a ClickUp folder",
244
+ description: "Create a new list within a ClickUp folder. Use this tool when you need to add a list to an existing folder structure. The tool can find folders and spaces by name, so explicit IDs aren't required. For top-level lists not in folders, use create_list instead.",
304
245
  inputSchema: {
305
246
  type: "object",
306
247
  properties: {
307
248
  folderId: {
308
249
  type: "string",
309
- description: "ID of the folder to create the list in (optional if folderName and spaceId/spaceName are provided)"
250
+ description: "ID of the folder to create the list in (optional if using folderName instead)"
310
251
  },
311
252
  folderName: {
312
253
  type: "string",
313
- description: "Name of the folder to create the list in"
254
+ description: "Name of the folder to create the list in - will automatically find the folder by name (optional if using folderId instead)"
314
255
  },
315
256
  spaceId: {
316
257
  type: "string",
317
- description: "ID of the space containing the folder (required if using folderName)"
258
+ description: "ID of the space containing the folder (optional if using spaceName instead)"
318
259
  },
319
260
  spaceName: {
320
261
  type: "string",
321
- description: "Name of the space containing the folder (alternative to spaceId)"
262
+ description: "Name of the space containing the folder - will automatically find the space by name (optional if using spaceId instead)"
322
263
  },
323
264
  name: {
324
265
  type: "string",
@@ -330,7 +271,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
330
271
  },
331
272
  status: {
332
273
  type: "string",
333
- description: "Status of the list"
274
+ description: "Status of the list (uses folder default if not specified)"
334
275
  }
335
276
  },
336
277
  required: ["name"]
@@ -338,77 +279,225 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
338
279
  },
339
280
  {
340
281
  name: "move_task",
341
- description: "Move a task to a different list",
282
+ description: "Move an existing task from its current list to a different list. Use this tool when you need to relocate a task within your workspace hierarchy. The tool can find tasks and lists by name, so explicit IDs aren't required. Task statuses may be reset if the destination list uses different status options.",
342
283
  inputSchema: {
343
284
  type: "object",
344
285
  properties: {
345
286
  taskId: {
346
287
  type: "string",
347
- description: "ID of the task to move"
288
+ description: "ID of the task to move (optional if using taskName instead)"
289
+ },
290
+ taskName: {
291
+ type: "string",
292
+ description: "Name of the task to move - will automatically find the task by name (optional if using taskId instead)"
293
+ },
294
+ sourceListName: {
295
+ type: "string",
296
+ description: "Optional: Name of the source list to narrow down task search when multiple tasks have the same name"
348
297
  },
349
298
  listId: {
350
299
  type: "string",
351
- description: "ID of the destination list (optional if listName is provided)"
300
+ description: "ID of the destination list (optional if using listName instead)"
352
301
  },
353
302
  listName: {
354
303
  type: "string",
355
- description: "Name of the destination list (optional if listId is provided)"
304
+ description: "Name of the destination list - will automatically find the list by name (optional if using listId instead)"
356
305
  }
357
306
  },
358
- required: ["taskId"]
307
+ required: []
359
308
  }
360
309
  },
361
310
  {
362
311
  name: "duplicate_task",
363
- description: "Duplicate a task to a list",
312
+ description: "Create a copy of an existing task in the same or different list. Use this tool when you need to replicate a task's content and properties. The tool can find tasks and lists by name, so explicit IDs aren't required. The duplicate will preserve name, description, priority, and other attributes from the original task.",
364
313
  inputSchema: {
365
314
  type: "object",
366
315
  properties: {
367
316
  taskId: {
368
317
  type: "string",
369
- description: "ID of the task to duplicate"
318
+ description: "ID of the task to duplicate (optional if using taskName instead)"
319
+ },
320
+ taskName: {
321
+ type: "string",
322
+ description: "Name of the task to duplicate - will automatically find the task by name (optional if using taskId instead)"
323
+ },
324
+ sourceListName: {
325
+ type: "string",
326
+ description: "Optional: Name of the source list to narrow down task search when multiple tasks have the same name"
370
327
  },
371
328
  listId: {
372
329
  type: "string",
373
- description: "ID of the destination list (optional if listName is provided)"
330
+ description: "ID of the list to create the duplicate in (optional if using listName instead)"
374
331
  },
375
332
  listName: {
376
333
  type: "string",
377
- description: "Name of the destination list (optional if listId is provided)"
334
+ description: "Name of the list to create the duplicate in - will automatically find the list by name (optional if using listId instead)"
378
335
  }
379
336
  },
380
- required: ["taskId"]
337
+ required: []
381
338
  }
382
339
  },
383
340
  {
384
341
  name: "update_task",
385
- description: "Update an existing task in ClickUp",
342
+ description: "Modify the properties of an existing task. Use this tool when you need to change a task's name, description, status, priority, or due date. The tool can find tasks by name, so explicit task IDs aren't required. Only the fields you specify will be updated; other fields will remain unchanged.",
386
343
  inputSchema: {
387
344
  type: "object",
388
345
  properties: {
389
346
  taskId: {
390
347
  type: "string",
391
- description: "ID of the task to update"
348
+ description: "ID of the task to update (optional if using taskName instead)"
349
+ },
350
+ taskName: {
351
+ type: "string",
352
+ description: "Name of the task to update - will automatically find the task by name (optional if using taskId instead)"
353
+ },
354
+ listName: {
355
+ type: "string",
356
+ description: "Optional: Name of the list to narrow down task search when multiple tasks have the same name"
392
357
  },
393
358
  name: {
394
359
  type: "string",
395
- description: "New name of the task"
360
+ description: "New name for the task"
396
361
  },
397
362
  description: {
398
363
  type: "string",
399
- description: "New description of the task"
364
+ description: "New plain text description for the task"
400
365
  },
401
366
  status: {
402
367
  type: "string",
403
- description: "New status of the task"
368
+ description: "New status for the task (must be a valid status in the task's list)"
404
369
  },
405
370
  priority: {
406
371
  type: "number",
407
- description: "New priority of the task (1-4)"
372
+ description: "New priority for the task (1-4), where 1 is urgent/highest priority and 4 is lowest priority"
408
373
  },
409
374
  dueDate: {
410
375
  type: "string",
411
- description: "New due date of the task (ISO string)"
376
+ description: "New due date for the task (Unix timestamp in milliseconds). Convert dates to this format before submitting."
377
+ }
378
+ },
379
+ required: []
380
+ }
381
+ },
382
+ {
383
+ name: "get_tasks",
384
+ description: "Retrieve tasks from a ClickUp list with optional filtering capabilities. Use this tool when you need to see existing tasks or analyze your current workload. The tool can find lists by name, eliminating the need for explicit list IDs. Results can be filtered by status, assignees, dates, and more.",
385
+ inputSchema: {
386
+ type: "object",
387
+ properties: {
388
+ listId: {
389
+ type: "string",
390
+ description: "ID of the list to get tasks from (optional if using listName instead)"
391
+ },
392
+ listName: {
393
+ type: "string",
394
+ description: "Name of the list to get tasks from - will automatically find the list by name (optional if using listId instead)"
395
+ },
396
+ archived: {
397
+ type: "boolean",
398
+ description: "Set to true to include archived tasks in the results"
399
+ },
400
+ page: {
401
+ type: "number",
402
+ description: "Page number for pagination when dealing with many tasks (starts at 0)"
403
+ },
404
+ order_by: {
405
+ type: "string",
406
+ description: "Field to order tasks by (e.g., 'due_date', 'created', 'updated')"
407
+ },
408
+ reverse: {
409
+ type: "boolean",
410
+ description: "Set to true to reverse the sort order (descending instead of ascending)"
411
+ },
412
+ subtasks: {
413
+ type: "boolean",
414
+ description: "Set to true to include subtasks in the results"
415
+ },
416
+ statuses: {
417
+ type: "array",
418
+ items: { type: "string" },
419
+ description: "Array of status names to filter tasks by (e.g., ['To Do', 'In Progress'])"
420
+ },
421
+ include_closed: {
422
+ type: "boolean",
423
+ description: "Set to true to include tasks with 'Closed' status"
424
+ },
425
+ assignees: {
426
+ type: "array",
427
+ items: { type: "string" },
428
+ description: "Array of user IDs to filter tasks by assignee"
429
+ },
430
+ due_date_gt: {
431
+ type: "number",
432
+ description: "Filter tasks due after this timestamp (Unix milliseconds)"
433
+ },
434
+ due_date_lt: {
435
+ type: "number",
436
+ description: "Filter tasks due before this timestamp (Unix milliseconds)"
437
+ },
438
+ date_created_gt: {
439
+ type: "number",
440
+ description: "Filter tasks created after this timestamp (Unix milliseconds)"
441
+ },
442
+ date_created_lt: {
443
+ type: "number",
444
+ description: "Filter tasks created before this timestamp (Unix milliseconds)"
445
+ },
446
+ date_updated_gt: {
447
+ type: "number",
448
+ description: "Filter tasks updated after this timestamp (Unix milliseconds)"
449
+ },
450
+ date_updated_lt: {
451
+ type: "number",
452
+ description: "Filter tasks updated before this timestamp (Unix milliseconds)"
453
+ },
454
+ custom_fields: {
455
+ type: "object",
456
+ description: "Object with custom field IDs as keys and desired values for filtering"
457
+ }
458
+ },
459
+ required: []
460
+ }
461
+ },
462
+ {
463
+ name: "get_task",
464
+ description: "Retrieve comprehensive details about a specific ClickUp task. Use this tool when you need in-depth information about a particular task, including its description, custom fields, attachments, and other metadata. The tool can find tasks by name, eliminating the need for explicit task IDs.",
465
+ inputSchema: {
466
+ type: "object",
467
+ properties: {
468
+ taskId: {
469
+ type: "string",
470
+ description: "ID of the task to retrieve (optional if using taskName instead)"
471
+ },
472
+ taskName: {
473
+ type: "string",
474
+ description: "Name of the task to retrieve - will automatically find the task by name (optional if using taskId instead)"
475
+ },
476
+ listName: {
477
+ type: "string",
478
+ description: "Optional: Name of the list to narrow down task search when multiple tasks have the same name"
479
+ }
480
+ },
481
+ required: []
482
+ }
483
+ },
484
+ {
485
+ name: "delete_task",
486
+ description: "Permanently remove a task from your ClickUp workspace. Use this tool with caution as deletion cannot be undone. The tool requires an explicit task ID for safety reasons, which you can obtain by first using get_task or get_tasks to find the appropriate task ID.",
487
+ inputSchema: {
488
+ type: "object",
489
+ properties: {
490
+ taskId: {
491
+ type: "string",
492
+ description: "ID of the task to delete - this is required for safety to prevent accidental deletions"
493
+ },
494
+ taskName: {
495
+ type: "string",
496
+ description: "Name of the task to delete - will automatically find the task by name (optional if using taskId instead)"
497
+ },
498
+ listName: {
499
+ type: "string",
500
+ description: "Optional: Name of the list to narrow down task search when multiple tasks have the same name"
412
501
  }
413
502
  },
414
503
  required: ["taskId"]
@@ -418,23 +507,68 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
418
507
  };
419
508
  });
420
509
  /**
421
- * Handler for executing tools.
510
+ * Handler for the CallToolRequestSchema.
511
+ * Handles the execution of tools like listing spaces, creating tasks, and updating tasks.
422
512
  */
423
513
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
424
514
  try {
425
515
  switch (request.params.name) {
426
- case "workspace_hierarchy": {
427
- const output = await handleWorkspaceHierarchy(clickup, config.teamId);
516
+ case "get_workspace_hierarchy": {
517
+ const hierarchy = await clickup.getWorkspaceHierarchy();
428
518
  return {
429
519
  content: [{
430
520
  type: "text",
431
- text: output
521
+ text: JSON.stringify({
522
+ workspace: {
523
+ id: hierarchy.root.id,
524
+ name: hierarchy.root.name,
525
+ spaces: hierarchy.root.children.map((space) => ({
526
+ id: space.id,
527
+ name: space.name,
528
+ lists: space.children
529
+ .filter((node) => node.type === 'list')
530
+ .map((list) => ({
531
+ id: list.id,
532
+ name: list.name,
533
+ path: `${space.name} > ${list.name}`
534
+ })),
535
+ folders: space.children
536
+ .filter((node) => node.type === 'folder')
537
+ .map((folder) => ({
538
+ id: folder.id,
539
+ name: folder.name,
540
+ path: `${space.name} > ${folder.name}`,
541
+ lists: folder.children.map((list) => ({
542
+ id: list.id,
543
+ name: list.name,
544
+ path: `${space.name} > ${folder.name} > ${list.name}`
545
+ }))
546
+ }))
547
+ }))
548
+ }
549
+ }, null, 2)
432
550
  }]
433
551
  };
434
552
  }
435
553
  case "create_task": {
436
554
  const args = request.params.arguments;
437
- const task = await handleCreateTask(clickup, config.teamId, args);
555
+ if (!args.listId && !args.listName) {
556
+ throw new Error("Either listId or listName is required");
557
+ }
558
+ if (!args.name) {
559
+ throw new Error("name is required");
560
+ }
561
+ let listId = args.listId;
562
+ if (!listId && args.listName) {
563
+ const hierarchy = await clickup.getWorkspaceHierarchy();
564
+ const result = clickup.findIDByNameInHierarchy(hierarchy, args.listName, 'list');
565
+ if (!result) {
566
+ throw new Error(`List with name "${args.listName}" not found`);
567
+ }
568
+ listId = result.id;
569
+ }
570
+ const { listId: _, listName: __, ...taskData } = args;
571
+ const task = await clickup.createTask(listId, taskData);
438
572
  return {
439
573
  content: [{
440
574
  type: "text",
@@ -444,26 +578,26 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
444
578
  }
445
579
  case "create_bulk_tasks": {
446
580
  const args = request.params.arguments;
581
+ if (!args.listId && !args.listName) {
582
+ throw new Error("Either listId or listName is required");
583
+ }
584
+ if (!args.tasks || args.tasks.length === 0) {
585
+ throw new Error("tasks array is required and must not be empty");
586
+ }
447
587
  let listId = args.listId;
448
588
  if (!listId && args.listName) {
449
- const result = await clickup.findListByNameGlobally(config.teamId, args.listName);
589
+ const result = await clickup.findListIDByName(args.listName);
450
590
  if (!result) {
451
591
  throw new Error(`List with name "${args.listName}" not found`);
452
592
  }
453
- listId = result.list.id;
454
- }
455
- if (!listId) {
456
- throw new Error("Either listId or listName is required");
457
- }
458
- if (!args.tasks || !args.tasks.length) {
459
- throw new Error("At least one task is required");
593
+ listId = result.id;
460
594
  }
461
595
  const { listId: _, listName: __, tasks } = args;
462
596
  const createdTasks = await clickup.createBulkTasks(listId, { tasks });
463
597
  return {
464
598
  content: [{
465
599
  type: "text",
466
- text: `Created ${createdTasks.length} tasks:\n${createdTasks.map(task => `- ${task.id}: ${task.name}`).join('\n')}`
600
+ text: `Created ${createdTasks.length} tasks`
467
601
  }]
468
602
  };
469
603
  }
@@ -472,41 +606,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
472
606
  if (!args.name) {
473
607
  throw new Error("name is required");
474
608
  }
475
- // If folder is specified, create list in folder
476
- if (args.folderName || args.folderId) {
477
- let folderId = args.folderId;
478
- if (!folderId && args.folderName) {
479
- const result = await clickup.findFolderByNameGlobally(config.teamId, args.folderName);
480
- if (!result) {
481
- throw new Error(`Folder with name "${args.folderName}" not found`);
482
- }
483
- folderId = result.folder.id;
484
- }
485
- if (!folderId) {
486
- throw new Error("Either folderId or folderName must be provided");
487
- }
488
- const { spaceId: _, spaceName: __, folderName: ___, folderId: ____, ...listData } = args;
489
- const list = await clickup.createListInFolder(folderId, listData);
490
- return {
491
- content: [{
492
- type: "text",
493
- text: `Created list ${list.id}: ${list.name} in folder`
494
- }]
495
- };
496
- }
497
- // Otherwise, create list in space
498
609
  let spaceId = args.spaceId;
499
610
  if (!spaceId && args.spaceName) {
500
- const space = await clickup.findSpaceByName(config.teamId, args.spaceName);
501
- if (!space) {
611
+ const foundId = await clickup.findSpaceIDByName(args.spaceName);
612
+ if (!foundId) {
502
613
  throw new Error(`Space with name "${args.spaceName}" not found`);
503
614
  }
504
- spaceId = space.id;
615
+ spaceId = foundId;
505
616
  }
506
617
  if (!spaceId) {
507
618
  throw new Error("Either spaceId or spaceName must be provided");
508
619
  }
509
- const { spaceId: _, spaceName: __, folderName: ___, folderId: ____, ...listData } = args;
620
+ const { spaceId: _, spaceName: __, ...listData } = args;
510
621
  const list = await clickup.createList(spaceId, listData);
511
622
  return {
512
623
  content: [{
@@ -522,11 +633,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
522
633
  }
523
634
  let spaceId = args.spaceId;
524
635
  if (!spaceId && args.spaceName) {
525
- const space = await clickup.findSpaceByName(config.teamId, args.spaceName);
526
- if (!space) {
636
+ const foundId = await clickup.findSpaceIDByName(args.spaceName);
637
+ if (!foundId) {
527
638
  throw new Error(`Space with name "${args.spaceName}" not found`);
528
639
  }
529
- spaceId = space.id;
640
+ spaceId = foundId;
530
641
  }
531
642
  if (!spaceId) {
532
643
  throw new Error("Either spaceId or spaceName must be provided");
@@ -547,195 +658,340 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
547
658
  }
548
659
  let folderId = args.folderId;
549
660
  if (!folderId && args.folderName) {
550
- const result = await clickup.findFolderByNameGlobally(config.teamId, args.folderName);
661
+ const result = await clickup.findFolderIDByName(args.folderName);
551
662
  if (!result) {
552
663
  throw new Error(`Folder with name "${args.folderName}" not found`);
553
664
  }
554
- folderId = result.folder.id;
665
+ folderId = result.id;
555
666
  }
556
667
  if (!folderId) {
557
- throw new Error("Either folderId or folderName is required");
668
+ throw new Error("Either folderId or folderName must be provided");
558
669
  }
559
- const listData = {
560
- name: args.name,
561
- content: args.content,
562
- status: args.status
670
+ const { folderId: _, folderName: __, spaceId: ___, spaceName: ____, ...listData } = args;
671
+ const list = await clickup.createListInFolder(folderId, listData);
672
+ return {
673
+ content: [{
674
+ type: "text",
675
+ text: `Created list ${list.id}: ${list.name} in folder`
676
+ }]
563
677
  };
564
- try {
565
- const list = await clickup.createListInFolder(folderId, listData);
566
- return {
567
- content: [{
568
- type: "text",
569
- text: `Created list ${list.id}: ${list.name} in folder`
570
- }]
571
- };
572
- }
573
- catch (error) {
574
- throw new Error(`Failed to create list: ${error.message}`);
575
- }
576
678
  }
577
679
  case "move_task": {
578
680
  const args = request.params.arguments;
579
- if (!args.taskId) {
580
- throw new Error("taskId is required");
681
+ if (!args.taskId && !args.taskName) {
682
+ throw new Error("Either taskId or taskName is required");
683
+ }
684
+ if (!args.listId && !args.listName) {
685
+ throw new Error("Either listId or listName is required");
686
+ }
687
+ let taskId = args.taskId;
688
+ if (!taskId && args.taskName) {
689
+ const result = await clickup.findTaskByName(args.taskName, undefined, args.sourceListName);
690
+ if (!result) {
691
+ throw new Error(`Task with name "${args.taskName}" not found${args.sourceListName ? ` in list "${args.sourceListName}"` : ''}`);
692
+ }
693
+ taskId = result.id;
581
694
  }
582
695
  let listId = args.listId;
583
696
  if (!listId && args.listName) {
584
- const result = await clickup.findListByNameGlobally(config.teamId, args.listName);
697
+ const result = await clickup.findListIDByName(args.listName);
585
698
  if (!result) {
586
699
  throw new Error(`List with name "${args.listName}" not found`);
587
700
  }
588
- listId = result.list.id;
701
+ listId = result.id;
589
702
  }
590
- if (!listId) {
591
- throw new Error("Either listId or listName is required");
592
- }
593
- const task = await clickup.moveTask(args.taskId, listId);
703
+ // Get the original task details for the response message
704
+ const originalTask = await clickup.getTask(taskId);
705
+ const newTask = await clickup.moveTask(taskId, listId);
594
706
  return {
595
707
  content: [{
596
708
  type: "text",
597
- text: `Moved task ${task.id} to list ${listId}`
709
+ text: `Moved task "${originalTask.name}" from "${originalTask.list.name}" to "${newTask.list.name}"`
598
710
  }]
599
711
  };
600
712
  }
601
713
  case "duplicate_task": {
602
714
  const args = request.params.arguments;
603
- if (!args.taskId) {
604
- throw new Error("taskId is required");
715
+ // Require either taskId or taskName
716
+ if (!args.taskId && !args.taskName) {
717
+ throw new Error("Either taskId or taskName is required");
718
+ }
719
+ // Require either listId or listName
720
+ if (!args.listId && !args.listName) {
721
+ throw new Error("Either listId or listName is required");
722
+ }
723
+ // Get taskId from taskName if needed
724
+ let taskId = args.taskId;
725
+ if (!taskId && args.taskName) {
726
+ const result = await clickup.findTaskByName(args.taskName, undefined, args.sourceListName);
727
+ if (!result) {
728
+ throw new Error(`Task with name "${args.taskName}" not found${args.sourceListName ? ` in list "${args.sourceListName}"` : ''}`);
729
+ }
730
+ taskId = result.id;
605
731
  }
732
+ // Get listId from listName if needed
606
733
  let listId = args.listId;
607
734
  if (!listId && args.listName) {
608
- const result = await clickup.findListByNameGlobally(config.teamId, args.listName);
735
+ const result = await clickup.findListIDByName(args.listName);
609
736
  if (!result) {
610
737
  throw new Error(`List with name "${args.listName}" not found`);
611
738
  }
612
- listId = result.list.id;
739
+ listId = result.id;
740
+ }
741
+ // Get the original task details for the response message
742
+ const originalTask = await clickup.getTask(taskId);
743
+ const newTask = await clickup.duplicateTask(taskId, listId);
744
+ return {
745
+ content: [{
746
+ type: "text",
747
+ text: `Duplicated task "${originalTask.name}" from "${originalTask.list.name}" to "${newTask.list.name}"`
748
+ }]
749
+ };
750
+ }
751
+ case "update_task": {
752
+ const args = request.params.arguments;
753
+ if (!args.taskId && !args.taskName) {
754
+ throw new Error("Either taskId or taskName is required");
755
+ }
756
+ let taskId = args.taskId;
757
+ if (!taskId && args.taskName) {
758
+ const result = await clickup.findTaskByName(args.taskName, undefined, args.listName);
759
+ if (!result) {
760
+ throw new Error(`Task with name "${args.taskName}" not found${args.listName ? ` in list "${args.listName}"` : ''}`);
761
+ }
762
+ taskId = result.id;
613
763
  }
614
- if (!listId) {
764
+ const { taskId: _, taskName: __, listName: ___, ...updateData } = args;
765
+ const task = await clickup.updateTask(taskId, updateData);
766
+ return {
767
+ content: [{
768
+ type: "text",
769
+ text: `Updated task ${task.id}: ${task.name}`
770
+ }]
771
+ };
772
+ }
773
+ case "get_tasks": {
774
+ const args = request.params.arguments;
775
+ // Enforce required listName field as specified in the schema
776
+ if (!args.listId && !args.listName) {
615
777
  throw new Error("Either listId or listName is required");
616
778
  }
617
- const task = await clickup.duplicateTask(args.taskId, listId);
779
+ let listId = args.listId;
780
+ if (!listId && args.listName) {
781
+ const result = await clickup.findListIDByName(args.listName);
782
+ if (!result) {
783
+ throw new Error(`List with name "${args.listName}" not found`);
784
+ }
785
+ listId = result.id;
786
+ }
787
+ // Remove listId and listName from filters
788
+ const { listId: _, listName: __, ...filters } = args;
789
+ const { tasks, statuses } = await clickup.getTasks(listId, filters);
618
790
  return {
619
791
  content: [{
620
792
  type: "text",
621
- text: `Duplicated task ${args.taskId} to new task ${task.id} in list ${listId}`
793
+ text: JSON.stringify({ tasks, available_statuses: statuses }, null, 2)
622
794
  }]
623
795
  };
624
796
  }
625
- case "update_task": {
797
+ case "get_task": {
798
+ const args = request.params.arguments;
799
+ // Enforce required taskName field as specified in the schema
800
+ if (!args.taskId && !args.taskName) {
801
+ throw new Error("Either taskId or taskName is required");
802
+ }
803
+ let taskId = args.taskId;
804
+ if (!taskId && args.taskName) {
805
+ const result = await clickup.findTaskByName(args.taskName, undefined, args.listName);
806
+ if (!result) {
807
+ throw new Error(`Task with name "${args.taskName}" not found${args.listName ? ` in list "${args.listName}"` : ''}`);
808
+ }
809
+ taskId = result.id;
810
+ }
811
+ const task = await clickup.getTask(taskId);
812
+ return {
813
+ content: [{
814
+ type: "text",
815
+ text: JSON.stringify(task, null, 2)
816
+ }]
817
+ };
818
+ }
819
+ case "delete_task": {
626
820
  const args = request.params.arguments;
821
+ // Validate the required taskId parameter
627
822
  if (!args.taskId) {
628
- throw new Error("taskId is required");
629
- }
630
- const dueDate = args.due_date ? new Date(args.due_date).getTime() : undefined;
631
- const task = await clickup.updateTask(args.taskId, {
632
- name: args.name,
633
- description: args.description,
634
- status: args.status,
635
- priority: args.priority,
636
- due_date: dueDate
637
- });
823
+ throw new Error("taskId is required for deletion operations");
824
+ }
825
+ // Store the task name before deletion for the response message
826
+ let taskName = args.taskName;
827
+ if (!taskName) {
828
+ try {
829
+ const task = await clickup.getTask(args.taskId);
830
+ taskName = task.name;
831
+ }
832
+ catch (error) {
833
+ // If we can't get the task details, just use the ID in the response
834
+ }
835
+ }
836
+ await clickup.deleteTask(args.taskId);
638
837
  return {
639
838
  content: [{
640
839
  type: "text",
641
- text: `Updated task ${task.id}: ${task.name}`
840
+ text: `Successfully deleted task ${taskName || args.taskId}`
642
841
  }]
643
842
  };
644
843
  }
645
844
  default:
646
- throw new Error("Unknown tool");
845
+ throw new Error(`Unknown tool: ${request.params.name}`);
647
846
  }
648
847
  }
649
848
  catch (error) {
650
- console.error('Error executing tool:', error);
651
- throw error;
849
+ console.error('Error in tool call:', error);
850
+ return {
851
+ content: [{
852
+ type: "text",
853
+ text: `Error: ${error instanceof Error ? error.message : String(error)}`
854
+ }]
855
+ };
652
856
  }
653
857
  });
654
858
  /**
655
- * Handler for listing available prompts.
859
+ * Add handlers for listing and getting prompts.
860
+ * Prompts include summarizing tasks, analyzing priorities, and generating task descriptions.
656
861
  */
657
862
  server.setRequestHandler(ListPromptsRequestSchema, async () => {
658
863
  return {
659
864
  prompts: [
660
865
  {
661
866
  name: "summarize_tasks",
662
- description: "Summarize all ClickUp tasks"
867
+ description: "Generate a comprehensive summary of tasks in a ClickUp list or workspace. The summary includes a high-level overview, groups tasks by status, highlights priority items, and identifies potential task relationships or dependencies. Useful for project status reports and team updates.",
663
868
  },
664
869
  {
665
- name: "analyze_task_priorities",
666
- description: "Analyze task priorities"
870
+ name: "analyze_priorities",
871
+ description: "Evaluate task priority distribution across your workspace and identify optimization opportunities. The analysis examines current priority assignments, identifies misaligned priorities, suggests adjustments, and recommends task sequencing based on priorities. Helpful for workload management and project planning.",
667
872
  },
668
873
  {
669
874
  name: "generate_description",
670
- description: "Generate a detailed task description with objectives, success criteria, resources, and dependencies"
875
+ description: "Create a detailed, well-structured task description with clearly defined objectives, success criteria, required resources, dependencies, and risk assessments. This prompt helps ensure tasks are comprehensively documented with all necessary information for successful execution.",
671
876
  }
672
877
  ]
673
878
  };
674
879
  });
675
- /**
676
- * Handler for getting a specific prompt.
677
- */
678
880
  server.setRequestHandler(GetPromptRequestSchema, async (request) => {
679
881
  try {
680
882
  switch (request.params.name) {
681
883
  case "summarize_tasks": {
682
- const output = await handleSummarizeTasks(clickup, config.teamId);
884
+ const spaces = await clickup.getSpaces(config.clickupTeamId);
885
+ const tasks = [];
886
+ // Gather all tasks
887
+ for (const space of spaces) {
888
+ const lists = await clickup.getLists(space.id);
889
+ for (const list of lists) {
890
+ const { tasks: listTasks } = await clickup.getTasks(list.id);
891
+ tasks.push(...listTasks.map((task) => ({
892
+ type: "resource",
893
+ resource: {
894
+ uri: `clickup://task/${task.id}`,
895
+ mimeType: "application/json",
896
+ text: JSON.stringify(task, null, 2)
897
+ }
898
+ })));
899
+ }
900
+ }
683
901
  return {
684
- content: [{
685
- type: "text",
686
- text: output
687
- }]
902
+ messages: [
903
+ {
904
+ role: "user",
905
+ content: {
906
+ type: "text",
907
+ text: "Please provide a summary of the following ClickUp tasks:"
908
+ }
909
+ },
910
+ ...tasks.map(task => ({
911
+ role: "user",
912
+ content: task
913
+ })),
914
+ {
915
+ role: "user",
916
+ content: {
917
+ type: "text",
918
+ text: "Please provide:\n1. A high-level overview of all tasks\n2. Group them by status\n3. Highlight any urgent or high-priority items\n4. Suggest any task dependencies or relationships"
919
+ }
920
+ }
921
+ ]
688
922
  };
689
923
  }
690
- case "analyze_task_priorities": {
691
- const output = await handleAnalyzeTaskPriorities(clickup, config.teamId);
924
+ case "analyze_priorities": {
925
+ const spaces = await clickup.getSpaces(config.clickupTeamId);
926
+ const tasks = [];
927
+ for (const space of spaces) {
928
+ const lists = await clickup.getLists(space.id);
929
+ for (const list of lists) {
930
+ const { tasks: listTasks } = await clickup.getTasks(list.id);
931
+ tasks.push(...listTasks.map((task) => ({
932
+ type: "resource",
933
+ resource: {
934
+ uri: `clickup://task/${task.id}`,
935
+ mimeType: "application/json",
936
+ text: JSON.stringify(task, null, 2)
937
+ }
938
+ })));
939
+ }
940
+ }
692
941
  return {
693
- content: [{
694
- type: "text",
695
- text: output
696
- }]
942
+ messages: [
943
+ {
944
+ role: "user",
945
+ content: {
946
+ type: "text",
947
+ text: "Analyze the priorities of the following ClickUp tasks:"
948
+ }
949
+ },
950
+ ...tasks.map(task => ({
951
+ role: "user",
952
+ content: task
953
+ })),
954
+ {
955
+ role: "user",
956
+ content: {
957
+ type: "text",
958
+ text: "Please provide:\n1. Analysis of current priority distribution\n2. Identify any misaligned priorities\n3. Suggest priority adjustments\n4. Recommend task sequencing based on priorities"
959
+ }
960
+ }
961
+ ]
697
962
  };
698
963
  }
699
964
  case "generate_description": {
700
- if (!request.params.arguments?.taskId) {
701
- throw new Error("taskId is required for generate_description prompt");
702
- }
703
- const output = await handleGenerateDescription(clickup, request.params.arguments.taskId);
704
965
  return {
705
- content: [{
706
- type: "text",
707
- text: output
708
- }]
966
+ messages: [
967
+ {
968
+ role: "user",
969
+ content: {
970
+ type: "text",
971
+ text: "Please help me generate a detailed description for a ClickUp task. The description should include:\n1. Clear objective\n2. Success criteria\n3. Required resources\n4. Dependencies\n5. Potential risks\n\nPlease ask me about the task details."
972
+ }
973
+ }
974
+ ]
709
975
  };
710
976
  }
711
977
  default:
712
- throw new Error("Prompt not found");
978
+ throw new Error("Unknown prompt");
713
979
  }
714
980
  }
715
981
  catch (error) {
716
- console.error('Error getting prompt:', error);
982
+ console.error('Error handling prompt:', error);
717
983
  throw error;
718
984
  }
719
985
  });
720
- // Start the server
721
- console.log('Setting up transport...');
722
- const transport = new StdioServerTransport();
723
- // Connect the server to the transport
724
- console.log('Connecting server to transport...');
725
- server.connect(transport).catch(error => {
726
- console.error('Error connecting server to transport:', error);
986
+ /**
987
+ * Start the server using stdio transport.
988
+ * This allows the server to communicate via standard input/output streams.
989
+ */
990
+ async function main() {
991
+ const transport = new StdioServerTransport();
992
+ await server.connect(transport);
993
+ }
994
+ main().catch((error) => {
995
+ console.error("Server error:", error);
727
996
  process.exit(1);
728
997
  });
729
- // Handle process signals
730
- process.on('SIGINT', () => {
731
- console.log('Received SIGINT. Shutting down...');
732
- transport.close();
733
- });
734
- process.on('SIGTERM', () => {
735
- console.log('Received SIGTERM. Shutting down...');
736
- transport.close();
737
- });
738
- // Prevent unhandled promise rejections from crashing the server
739
- process.on('unhandledRejection', (error) => {
740
- console.error('Unhandled promise rejection:', error);
741
- });