@taazkareem/clickup-mcp-server 0.4.0 → 0.4.2

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 CHANGED
@@ -45,16 +45,11 @@ A Model Context Protocol (MCP) server for integrating ClickUp tasks with AI appl
45
45
 
46
46
  ## Installation
47
47
 
48
- ### Using npx (Recommended)
48
+ ### Using npx
49
49
  ```bash
50
50
  npx @taazkareem/clickup-mcp-server
51
51
  ```
52
52
 
53
- ### Global Installation
54
- ```bash
55
- npm install -g @taazkareem/clickup-mcp-server
56
- ```
57
-
58
53
  ## Configuration
59
54
 
60
55
  1. Get your ClickUp API key from [ClickUp Settings](https://app.clickup.com/settings/apps)
@@ -64,7 +59,7 @@ CLICKUP_API_KEY=your_api_key_here
64
59
  TEAM_ID=your_team_id_here
65
60
  ```
66
61
 
67
- ## Using with Cursor AI Composer
62
+ ## Using with Cursor AI Composer Agent
68
63
 
69
64
  To add this server to Cursor AI Composer, follow these steps:
70
65
 
@@ -72,17 +67,13 @@ To add this server to Cursor AI Composer, follow these steps:
72
67
  2. Add the following command under MCP Servers:
73
68
 
74
69
  ```bash
75
- npx -y @taazkareem/clickup-mcp-server \
70
+ npx -y @taazkareem/clickup-mcp-server --stdio \
76
71
  --env CLICKUP_API_KEY=your_api_key_here \
77
72
  --env TEAM_ID=your_team_id_here
78
73
  ```
79
74
  3. Replace `your_api_key_here` and `your_team_id_here` with your actual ClickUp credentials.
80
75
  4. Click on 'Save' to add the server.
81
76
 
82
- You can get these values from:
83
- - `CLICKUP_API_KEY`: Get from [ClickUp Settings > Apps](https://app.clickup.com/settings/apps)
84
- - `TEAM_ID`: Your ClickUp Team ID (found in the URL when viewing your workspace or via API)
85
-
86
77
  > **Security Note**: Your API key will be stored securely and will not be exposed to AI models.
87
78
 
88
79
  ### Available Tools
@@ -246,4 +237,4 @@ Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md)
246
237
 
247
238
  ## License
248
239
 
249
- This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
240
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
package/build/config.js CHANGED
@@ -1,8 +1,13 @@
1
1
  import dotenv from 'dotenv';
2
2
  // Load environment variables from .env file
3
3
  dotenv.config();
4
+ console.log('Environment variables received:', {
5
+ CLICKUP_API_KEY: process.env.CLICKUP_API_KEY,
6
+ TEAM_ID: process.env.TEAM_ID
7
+ });
4
8
  // Parse command line arguments for --env flags
5
9
  const args = process.argv.slice(2);
10
+ console.log('Command line arguments:', args);
6
11
  const envArgs = {};
7
12
  for (let i = 0; i < args.length; i++) {
8
13
  if (args[i] === '--env' && i + 1 < args.length) {
@@ -14,10 +19,12 @@ for (let i = 0; i < args.length; i++) {
14
19
  i++; // Skip the next argument since we used it
15
20
  }
16
21
  }
22
+ console.log('Parsed environment arguments:', envArgs);
17
23
  const configuration = {
18
24
  clickupApiKey: envArgs.clickupApiKey || process.env.CLICKUP_API_KEY || '',
19
- teamId: envArgs.teamId || process.env.TEAM_ID || '',
25
+ teamId: envArgs.teamId || process.env.TEAM_ID || ''
20
26
  };
27
+ console.log('Final configuration:', configuration);
21
28
  // Check for missing environment variables
22
29
  const missingEnvVars = Object.entries(configuration)
23
30
  .filter(([_, value]) => !value)
@@ -0,0 +1,25 @@
1
+ import { getAllTasks } from '../utils/resolvers.js';
2
+ export async function handleSummarizeTasks(clickup, teamId) {
3
+ const { tasks } = await getAllTasks(clickup, teamId);
4
+ let output = "Summarized Tasks:\n\n";
5
+ for (const task of tasks) {
6
+ output += `- ${task.name}: ${task.description}\n`;
7
+ }
8
+ return output;
9
+ }
10
+ export async function handleAnalyzeTaskPriorities(clickup, teamId) {
11
+ const { tasks } = await getAllTasks(clickup, teamId);
12
+ const priorities = tasks.map(task => task.priority?.priority);
13
+ const uniquePriorities = [...new Set(priorities.filter(p => p !== undefined))];
14
+ const priorityCounts = uniquePriorities.map(priority => ({
15
+ priority,
16
+ count: priorities.filter(p => p === priority).length
17
+ }));
18
+ let output = "Task Priorities Analysis:\n\n";
19
+ output += "Available Priorities: " + uniquePriorities.join(', ') + "\n\n";
20
+ output += "Priority Counts:\n";
21
+ for (const priority of priorityCounts) {
22
+ output += `- Priority ${priority.priority}: ${priority.count}\n`;
23
+ }
24
+ return output;
25
+ }
@@ -0,0 +1,38 @@
1
+ import { resolveListId } from '../utils/resolvers.js';
2
+ export async function handleWorkspaceHierarchy(clickup, teamId) {
3
+ const spaces = await clickup.getSpaces(teamId);
4
+ const allLists = await clickup.getAllLists(teamId);
5
+ let output = "ClickUp Workspace Hierarchy:\n\n";
6
+ for (const space of spaces) {
7
+ output += `Space: ${space.name} (ID: ${space.id})\n`;
8
+ const folders = await clickup.getFolders(space.id);
9
+ for (const folder of folders) {
10
+ output += ` ├─ Folder: ${folder.name} (ID: ${folder.id})\n`;
11
+ const folderLists = folder.lists || [];
12
+ for (const list of folderLists) {
13
+ const { statuses } = await clickup.getTasks(list.id);
14
+ output += ` │ └─ List: ${list.name} (ID: ${list.id})\n`;
15
+ output += ` │ Available Statuses: ${statuses.join(', ')}\n`;
16
+ }
17
+ }
18
+ const spaceLists = allLists.filter(list => list.space &&
19
+ list.space.id === space.id &&
20
+ !folders.some(folder => folder.lists?.some(fl => fl.id === list.id)));
21
+ if (spaceLists.length > 0) {
22
+ output += " ├─ Lists (not in folders):\n";
23
+ for (const list of spaceLists) {
24
+ const { statuses } = await clickup.getTasks(list.id);
25
+ output += ` │ └─ List: ${list.name} (ID: ${list.id})\n`;
26
+ output += ` │ Available Statuses: ${statuses.join(', ')}\n`;
27
+ }
28
+ }
29
+ output += "\n";
30
+ }
31
+ return output;
32
+ }
33
+ export async function handleCreateTask(clickup, teamId, args) {
34
+ const listId = await resolveListId(clickup, teamId, args.listId, args.listName);
35
+ const { listId: _, listName: __, ...taskData } = args;
36
+ return await clickup.createTask(listId, taskData);
37
+ }
38
+ // Add other handler functions for each tool...
package/build/index.js CHANGED
@@ -1,36 +1,31 @@
1
1
  #!/usr/bin/env node
2
- /**
3
- * This is a template MCP server that implements a simple ClickUp task management system.
4
- * It demonstrates core MCP concepts like resources, tools, and prompts by allowing:
5
- * - Listing ClickUp tasks as resources
6
- * - Reading individual ClickUp tasks
7
- * - Creating new ClickUp tasks via a tool
8
- * - Updating existing ClickUp tasks via a tool
9
- * - Summarizing all ClickUp tasks via a prompt
10
- * - Analyzing task priorities via a prompt
11
- * - Generating detailed descriptions for tasks via a prompt
12
- */
13
2
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
14
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
15
4
  import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
16
5
  import { ClickUpService } from "./services/clickup.js";
17
6
  import config from "./config.js";
18
- /**
19
- * Simple in-memory storage for notes.
20
- * In a real implementation, this would likely be backed by a database.
21
- */
22
- const notes = {
23
- "1": { title: "First Note", content: "This is note 1" },
24
- "2": { title: "Second Note", content: "This is note 2" }
25
- };
7
+ import { handleWorkspaceHierarchy, handleCreateTask } from "./handlers/tools.js";
8
+ import { handleSummarizeTasks, handleAnalyzeTaskPriorities } 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
+ });
26
15
  // Initialize ClickUp service
27
- const clickup = ClickUpService.initialize(config.clickupApiKey);
28
- /**
29
- * Create an MCP server with capabilities for resources (to list/read ClickUp tasks),
30
- * tools (to create/update ClickUp tasks), and prompts (to summarize/analyze ClickUp tasks).
31
- */
16
+ let clickup;
17
+ try {
18
+ console.log('Initializing ClickUp service...');
19
+ clickup = ClickUpService.initialize(config.clickupApiKey);
20
+ console.log('ClickUp service initialized successfully');
21
+ }
22
+ catch (error) {
23
+ console.error("Failed to initialize ClickUp service:", error);
24
+ process.exit(1);
25
+ }
26
+ console.log('Creating MCP server...');
32
27
  const server = new Server({
33
- name: "clickup-mcp-server",
28
+ name: "clickup",
34
29
  version: "0.1.0",
35
30
  }, {
36
31
  capabilities: {
@@ -39,40 +34,31 @@ const server = new Server({
39
34
  prompts: {},
40
35
  },
41
36
  });
37
+ console.log('MCP server created');
42
38
  /**
43
39
  * Handler for listing available ClickUp tasks as resources.
44
- * Each task is exposed as a resource with:
45
- * - A clickup:// URI scheme
46
- * - JSON MIME type
47
- * - Human readable name and description (including the task name and description)
48
40
  */
49
41
  server.setRequestHandler(ListResourcesRequestSchema, async () => {
42
+ console.log('Handling ListResources request');
50
43
  try {
51
- const spaces = await clickup.getSpaces(config.teamId);
52
- const resources = [];
53
- for (const space of spaces) {
54
- const lists = await clickup.getLists(space.id);
55
- for (const list of lists) {
56
- const { tasks } = await clickup.getTasks(list.id);
57
- resources.push(...tasks.map((task) => ({
58
- uri: `clickup://task/${task.id}`,
59
- mimeType: "application/json",
60
- name: task.name,
61
- description: task.description || `Task in ${list.name} (${space.name})`,
62
- tags: []
63
- })));
64
- }
65
- }
66
- return { resources };
44
+ const { tasks, spaces } = await getAllTasks(clickup, config.teamId);
45
+ return {
46
+ resources: tasks.map(task => ({
47
+ uri: `clickup://task/${task.id}`,
48
+ mimeType: "application/json",
49
+ name: task.name,
50
+ description: task.description || `Task in ${task.list.name} (${task.space.name})`,
51
+ tags: []
52
+ }))
53
+ };
67
54
  }
68
55
  catch (error) {
69
- console.error('Error listing resources:', error);
56
+ console.error('Error in ListResources:', error);
70
57
  throw error;
71
58
  }
72
59
  });
73
60
  /**
74
61
  * Handler for reading the contents of a specific ClickUp task.
75
- * Takes a clickup:// URI and returns the task content as JSON.
76
62
  */
77
63
  server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
78
64
  try {
@@ -94,15 +80,15 @@ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
94
80
  }
95
81
  });
96
82
  /**
97
- * Handler that lists available tools.
98
- * Exposes tools for listing spaces, creating tasks, and updating tasks.
83
+ * Handler for listing available tools.
99
84
  */
100
85
  server.setRequestHandler(ListToolsRequestSchema, async () => {
86
+ console.log('Handling ListTools request');
101
87
  return {
102
88
  tools: [
103
89
  {
104
90
  name: "workspace_hierarchy",
105
- description: "List complete hierarchy of the ClickUp workspace, including spaces, folders, lists, and their IDs and available statuses",
91
+ description: "List complete hierarchy of the ClickUp workspace",
106
92
  inputSchema: {
107
93
  type: "object",
108
94
  properties: {},
@@ -390,54 +376,13 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
390
376
  };
391
377
  });
392
378
  /**
393
- * Handler for the CallToolRequestSchema.
394
- * Handles the execution of tools like listing spaces, creating tasks, and updating tasks.
379
+ * Handler for executing tools.
395
380
  */
396
381
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
397
382
  try {
398
383
  switch (request.params.name) {
399
384
  case "workspace_hierarchy": {
400
- const spaces = await clickup.getSpaces(config.teamId);
401
- const allLists = await clickup.getAllLists(config.teamId);
402
- let output = "ClickUp Workspace Hierarchy:\n\n";
403
- for (const space of spaces) {
404
- output += `Space: ${space.name} (ID: ${space.id})\n`;
405
- // Get folders in this space
406
- const folders = await clickup.getFolders(space.id);
407
- for (const folder of folders) {
408
- output += ` ├─ Folder: ${folder.name} (ID: ${folder.id})\n`;
409
- // Get lists in this folder
410
- const folderLists = folder.lists || [];
411
- for (const list of folderLists) {
412
- const { statuses } = await clickup.getTasks(list.id);
413
- output += ` │ └─ List: ${list.name} (ID: ${list.id})\n`;
414
- output += ` │ Available Statuses: ${statuses.join(', ')}\n`;
415
- }
416
- }
417
- // Get lists directly in space (not in folders)
418
- const spaceLists = allLists.filter(list => list.space &&
419
- list.space.id === space.id &&
420
- !folders.some(folder => folder.lists?.some(fl => fl.id === list.id)));
421
- if (spaceLists.length > 0) {
422
- output += " ├─ Lists (not in folders):\n";
423
- for (const list of spaceLists) {
424
- const { statuses } = await clickup.getTasks(list.id);
425
- output += ` │ └─ List: ${list.name} (ID: ${list.id})\n`;
426
- output += ` │ Available Statuses: ${statuses.join(', ')}\n`;
427
- }
428
- }
429
- output += "\n";
430
- }
431
- // Add lists without spaces at the end
432
- const listsWithoutSpace = allLists.filter(list => !list.space);
433
- if (listsWithoutSpace.length > 0) {
434
- output += "Lists without assigned spaces:\n";
435
- for (const list of listsWithoutSpace) {
436
- const { statuses } = await clickup.getTasks(list.id);
437
- output += ` └─ List: ${list.name} (ID: ${list.id})\n`;
438
- output += ` Available Statuses: ${statuses.join(', ')}\n`;
439
- }
440
- }
385
+ const output = await handleWorkspaceHierarchy(clickup, config.teamId);
441
386
  return {
442
387
  content: [{
443
388
  type: "text",
@@ -447,19 +392,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
447
392
  }
448
393
  case "create_task": {
449
394
  const args = request.params.arguments;
450
- let listId = args.listId;
451
- if (!listId && args.listName) {
452
- const result = await clickup.findListByNameGlobally(config.teamId, args.listName);
453
- if (!result) {
454
- throw new Error(`List with name "${args.listName}" not found`);
455
- }
456
- listId = result.list.id;
457
- }
458
- if (!listId) {
459
- throw new Error("Either listId or listName is required");
460
- }
461
- const { listId: _, listName: __, ...taskData } = args;
462
- const task = await clickup.createTask(listId, taskData);
395
+ const task = await handleCreateTask(clickup, config.teamId, args);
463
396
  return {
464
397
  content: [{
465
398
  type: "text",
@@ -572,19 +505,22 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
572
505
  }
573
506
  let folderId = args.folderId;
574
507
  if (!folderId && args.folderName) {
575
- let spaceId = args.spaceId;
576
- if (!spaceId && args.spaceName) {
577
- const space = await clickup.findSpaceByName(config.teamId, args.spaceName);
578
- if (!space) {
579
- throw new Error(`Space with name "${args.spaceName}" not found`);
580
- }
581
- spaceId = space.id;
582
- }
583
- if (!spaceId) {
584
- throw new Error("Either spaceId or spaceName must be provided");
508
+ const result = await clickup.findFolderByNameGlobally(config.teamId, args.folderName);
509
+ if (!result) {
510
+ throw new Error(`Folder with name "${args.folderName}" not found`);
585
511
  }
586
- const { spaceId: _, spaceName: ___, ...listData } = args;
587
- const list = await clickup.createListInFolder(spaceId, listData);
512
+ folderId = result.folder.id;
513
+ }
514
+ if (!folderId) {
515
+ throw new Error("Either folderId or folderName is required");
516
+ }
517
+ const listData = {
518
+ name: args.name,
519
+ content: args.content,
520
+ status: args.status
521
+ };
522
+ try {
523
+ const list = await clickup.createListInFolder(folderId, listData);
588
524
  return {
589
525
  content: [{
590
526
  type: "text",
@@ -592,17 +528,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
592
528
  }]
593
529
  };
594
530
  }
595
- if (!folderId) {
596
- throw new Error("Either folderId or folderName must be provided");
531
+ catch (error) {
532
+ throw new Error(`Failed to create list: ${error.message}`);
597
533
  }
598
- const { spaceId: _, spaceName: ___, ...listData } = args;
599
- const list = await clickup.createListInFolder(folderId, listData);
600
- return {
601
- content: [{
602
- type: "text",
603
- text: `Created list ${list.id}: ${list.name} in folder`
604
- }]
605
- };
606
534
  }
607
535
  case "move_task": {
608
536
  const args = request.params.arguments;
@@ -654,15 +582,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
654
582
  }
655
583
  case "update_task": {
656
584
  const args = request.params.arguments;
657
- if (!args.taskId || !args.name || !args.description || !args.status || !args.priority || !args.due_date) {
658
- throw new Error("All arguments are required");
585
+ if (!args.taskId) {
586
+ throw new Error("taskId is required");
659
587
  }
588
+ const dueDate = args.due_date ? new Date(args.due_date).getTime() : undefined;
660
589
  const task = await clickup.updateTask(args.taskId, {
661
590
  name: args.name,
662
591
  description: args.description,
663
592
  status: args.status,
664
593
  priority: args.priority,
665
- due_date: args.due_date ? new Date(args.due_date).getTime() : undefined
594
+ due_date: dueDate
666
595
  });
667
596
  return {
668
597
  content: [{
@@ -671,8 +600,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
671
600
  }]
672
601
  };
673
602
  }
603
+ default:
604
+ throw new Error("Unknown tool");
674
605
  }
675
- throw new Error("Unknown tool");
676
606
  }
677
607
  catch (error) {
678
608
  console.error('Error executing tool:', error);
@@ -681,7 +611,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
681
611
  });
682
612
  /**
683
613
  * Handler for listing available prompts.
684
- * Exposes prompts for summarizing/analyzing ClickUp tasks.
685
614
  */
686
615
  server.setRequestHandler(ListPromptsRequestSchema, async () => {
687
616
  return {
@@ -693,33 +622,18 @@ server.setRequestHandler(ListPromptsRequestSchema, async () => {
693
622
  {
694
623
  name: "analyze_task_priorities",
695
624
  description: "Analyze task priorities"
696
- },
697
- {
698
- name: "generate_task_descriptions",
699
- description: "Generate detailed descriptions for tasks"
700
625
  }
701
626
  ]
702
627
  };
703
628
  });
704
629
  /**
705
630
  * Handler for getting a specific prompt.
706
- * Takes a prompt name and returns the prompt content.
707
631
  */
708
632
  server.setRequestHandler(GetPromptRequestSchema, async (request) => {
709
633
  try {
710
634
  switch (request.params.name) {
711
635
  case "summarize_tasks": {
712
- const spaces = await clickup.getSpaces(config.teamId);
713
- let output = "Summarized Tasks:\n\n";
714
- for (const space of spaces) {
715
- const lists = await clickup.getLists(space.id);
716
- for (const list of lists) {
717
- const { tasks } = await clickup.getTasks(list.id);
718
- for (const task of tasks) {
719
- output += `- ${task.name}: ${task.description}\n`;
720
- }
721
- }
722
- }
636
+ const output = await handleSummarizeTasks(clickup, config.teamId);
723
637
  return {
724
638
  content: [{
725
639
  type: "text",
@@ -728,46 +642,7 @@ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
728
642
  };
729
643
  }
730
644
  case "analyze_task_priorities": {
731
- const spaces = await clickup.getSpaces(config.teamId);
732
- const allTasks = [];
733
- for (const space of spaces) {
734
- const lists = await clickup.getLists(space.id);
735
- for (const list of lists) {
736
- const { tasks } = await clickup.getTasks(list.id);
737
- allTasks.push(...tasks);
738
- }
739
- }
740
- const priorities = allTasks.map((task) => task.priority?.priority);
741
- const uniquePriorities = [...new Set(priorities.filter(p => p !== undefined))];
742
- const priorityCounts = uniquePriorities.map(priority => ({
743
- priority: priority,
744
- count: priorities.filter((p) => p === priority).length
745
- }));
746
- let output = "Task Priorities Analysis:\n\n";
747
- output += "Available Priorities: " + uniquePriorities.join(', ') + "\n\n";
748
- output += "Priority Counts:\n";
749
- for (const priority of priorityCounts) {
750
- output += `- Priority ${priority.priority}: ${priority.count}\n`;
751
- }
752
- return {
753
- content: [{
754
- type: "text",
755
- text: output
756
- }]
757
- };
758
- }
759
- case "generate_task_descriptions": {
760
- const spaces = await clickup.getSpaces(config.teamId);
761
- let output = "Generated Task Descriptions:\n\n";
762
- for (const space of spaces) {
763
- const lists = await clickup.getLists(space.id);
764
- for (const list of lists) {
765
- const { tasks } = await clickup.getTasks(list.id);
766
- for (const task of tasks) {
767
- output += `- ${task.name}: ${task.description}\n`;
768
- }
769
- }
770
- }
645
+ const output = await handleAnalyzeTaskPriorities(clickup, config.teamId);
771
646
  return {
772
647
  content: [{
773
648
  type: "text",
@@ -784,6 +659,38 @@ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
784
659
  throw error;
785
660
  }
786
661
  });
787
- // Start the server
788
- const transport = new StdioServerTransport();
789
- transport.start();
662
+ if (process.argv.includes('--stdio')) {
663
+ console.log('Starting server in stdio mode...');
664
+ // Set up stdio transport
665
+ const transport = new StdioServerTransport();
666
+ // Connect server with better error handling
667
+ server.connect(transport)
668
+ .then(() => {
669
+ console.log('Server connected successfully to stdio transport');
670
+ // Keep the process alive
671
+ process.stdin.resume();
672
+ // Handle process termination
673
+ process.on('SIGINT', () => {
674
+ console.log('Received SIGINT. Shutting down...');
675
+ transport.close();
676
+ process.exit(0);
677
+ });
678
+ process.on('SIGTERM', () => {
679
+ console.log('Received SIGTERM. Shutting down...');
680
+ transport.close();
681
+ process.exit(0);
682
+ });
683
+ })
684
+ .catch(error => {
685
+ console.error('Failed to connect server to transport:', error);
686
+ process.exit(1);
687
+ });
688
+ }
689
+ else {
690
+ console.log('Starting server in standard mode...');
691
+ // Add your non-stdio server initialization here if needed
692
+ }
693
+ // Prevent unhandled promise rejections from crashing the server
694
+ process.on('unhandledRejection', (error) => {
695
+ console.error('Unhandled promise rejection:', error);
696
+ });
@@ -0,0 +1,48 @@
1
+ export async function resolveListId(clickup, teamId, listId, listName) {
2
+ if (listId)
3
+ return listId;
4
+ if (!listName) {
5
+ throw new Error("Either listId or listName is required");
6
+ }
7
+ const result = await clickup.findListByNameGlobally(teamId, listName);
8
+ if (!result) {
9
+ throw new Error(`List with name "${listName}" not found`);
10
+ }
11
+ return result.list.id;
12
+ }
13
+ export async function resolveSpaceId(clickup, teamId, spaceId, spaceName) {
14
+ if (spaceId)
15
+ return spaceId;
16
+ if (!spaceName) {
17
+ throw new Error("Either spaceId or spaceName is required");
18
+ }
19
+ const space = await clickup.findSpaceByName(teamId, spaceName);
20
+ if (!space) {
21
+ throw new Error(`Space with name "${spaceName}" not found`);
22
+ }
23
+ return space.id;
24
+ }
25
+ export async function resolveFolderId(clickup, teamId, folderId, folderName) {
26
+ if (folderId)
27
+ return folderId;
28
+ if (!folderName) {
29
+ throw new Error("Either folderId or folderName is required");
30
+ }
31
+ const result = await clickup.findFolderByNameGlobally(teamId, folderName);
32
+ if (!result) {
33
+ throw new Error(`Folder with name "${folderName}" not found`);
34
+ }
35
+ return result.folder.id;
36
+ }
37
+ export async function getAllTasks(clickup, teamId) {
38
+ const spaces = await clickup.getSpaces(teamId);
39
+ const spacePromises = spaces.map(async (space) => {
40
+ const lists = await clickup.getLists(space.id);
41
+ const listPromises = lists.map(list => clickup.getTasks(list.id));
42
+ const listResults = await Promise.all(listPromises);
43
+ return listResults.flatMap(result => result.tasks);
44
+ });
45
+ const tasksPerSpace = await Promise.all(spacePromises);
46
+ const allTasks = tasksPerSpace.flat();
47
+ return { tasks: allTasks, spaces };
48
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taazkareem/clickup-mcp-server",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "ClickUp MCP Server - Integrate ClickUp tasks with AI through Model Context Protocol",
5
5
  "type": "module",
6
6
  "main": "build/index.js",
@@ -15,6 +15,7 @@
15
15
  "scripts": {
16
16
  "build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
17
17
  "start": "node build/index.js",
18
+ "serve": "node build/index.js --stdio",
18
19
  "dev": "tsc -w",
19
20
  "prepare": "npm run build",
20
21
  "prepublishOnly": "npm test",