@taazkareem/clickup-mcp-server 0.4.41 → 0.4.42

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
@@ -2,14 +2,6 @@
2
2
 
3
3
  A Model Context Protocol (MCP) server for integrating ClickUp tasks with AI applications. This server allows AI agents to interact with ClickUp tasks, spaces, lists, and folders through a standardized protocol.
4
4
 
5
- ## Quick Start
6
- ```bash
7
- npx clickup-mcp-server --stdio --env CLICKUP_API_KEY=your_api_key_here --env TEAM_ID=your_team_id_here
8
- ```
9
- That's it! No installation needed. Just replace the credentials with your own:
10
- - Get your API key from [ClickUp Settings](https://app.clickup.com/settings/apps)
11
- - Find your Team ID in your ClickUp workspace URL or settings
12
-
13
5
  ## Features
14
6
 
15
7
  - 🔄 **Resource Management**
@@ -51,44 +43,36 @@ That's it! No installation needed. Just replace the credentials with your own:
51
43
  - Secure API key management
52
44
  - Environment-based configuration
53
45
 
46
+ ## Installation
47
+
48
+ ### Using npx
49
+ ```bash
50
+ npx @taazkareem/clickup-mcp-server
51
+ ```
52
+
53
+ ## Configuration
54
+
55
+ 1. Get your ClickUp API key from [ClickUp Settings](https://app.clickup.com/settings/apps)
56
+ 2. Create a `.env` file:
57
+ ```env
58
+ CLICKUP_API_KEY=your_api_key_here
59
+ TEAM_ID=your_team_id_here
60
+ ```
61
+
54
62
  ## Using with Cursor AI Composer Agent
55
63
 
56
- To add this server to Cursor AI Composer:
57
-
58
- 1. Go to Features → MCP Servers in settings
59
- 2. Paste this command:
60
- ```bash
61
- npx clickup-mcp-server --stdio --env CLICKUP_API_KEY=your_api_key_here --env TEAM_ID=your_team_id_here
62
- ```
63
- 3. Replace the credentials with your own
64
- 4. Click 'Save'
65
-
66
- ## Using with Roo Code (formerly Cline)
67
-
68
- To add this server to Roo Code:
69
-
70
- 1. Open Roo Code (formerly Cline).
71
- 2. Ask Roo Code to add the ClickUp MCP Server to your project. It should add this to your `cline_mcp_settings.json` file:
72
- ```json
73
- "clickup-mcp-server": {
74
- "command": "npx",
75
- "args": [
76
- "clickup-mcp-server",
77
- "--stdio"
78
- ],
79
- "env": {
80
- "CLICKUP_API_KEY": "your_api_key_here",
81
- "TEAM_ID": "your_team_id_here"
82
- },
83
- "disabled": false,
84
- "alwaysAllow": []
85
- }
86
- ```
87
- 4. Replace `your_api_key_here` and `your_team_id_here` with your actual ClickUp API key and Team ID.
88
- **Note:** Do not use the example settings and credentials provided here; use your own.
89
- 5. Save the `cline_mcp_settings.json` file.
64
+ To add this server to Cursor AI Composer, follow these steps:
90
65
 
66
+ 1. Go to the Features section in the settings.
67
+ 2. Add the following command under MCP Servers:
91
68
 
69
+ ```bash
70
+ npx -y @taazkareem/clickup-mcp-server --stdio \
71
+ --env CLICKUP_API_KEY=your_api_key_here \
72
+ --env TEAM_ID=your_team_id_here
73
+ ```
74
+ 3. Replace `your_api_key_here` and `your_team_id_here` with your actual ClickUp credentials.
75
+ 4. Click on 'Save' to add the server.
92
76
 
93
77
  > **Security Note**: Your API key will be stored securely and will not be exposed to AI models.
94
78
 
@@ -254,6 +238,3 @@ Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md)
254
238
  ## License
255
239
 
256
240
  This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
257
-
258
- ## Support My Work! 👍
259
- Solana Wallet: GjtRksihd7SWQw7hJSCDMcTxPHbgpNs7xPW3nFubNjVM
package/build/config.js CHANGED
@@ -1,23 +1,13 @@
1
1
  import dotenv from 'dotenv';
2
- // Function to format messages as JSON-RPC 2.0
3
- function logMessage(method, params) {
4
- const jsonRpcMessage = {
5
- jsonrpc: "2.0",
6
- method,
7
- params,
8
- id: Date.now()
9
- };
10
- console.log(JSON.stringify(jsonRpcMessage));
11
- }
12
2
  // Load environment variables from .env file
13
3
  dotenv.config();
14
- logMessage('config.env.received', {
4
+ console.log('Environment variables received:', {
15
5
  CLICKUP_API_KEY: process.env.CLICKUP_API_KEY,
16
6
  TEAM_ID: process.env.TEAM_ID
17
7
  });
18
8
  // Parse command line arguments for --env flags
19
9
  const args = process.argv.slice(2);
20
- logMessage('config.args.received', args);
10
+ console.log('Command line arguments:', args);
21
11
  const envArgs = {};
22
12
  for (let i = 0; i < args.length; i++) {
23
13
  if (args[i] === '--env' && i + 1 < args.length) {
@@ -29,12 +19,12 @@ for (let i = 0; i < args.length; i++) {
29
19
  i++; // Skip the next argument since we used it
30
20
  }
31
21
  }
32
- logMessage('config.args.parsed', envArgs);
22
+ console.log('Parsed environment arguments:', envArgs);
33
23
  const configuration = {
34
24
  clickupApiKey: envArgs.clickupApiKey || process.env.CLICKUP_API_KEY || '',
35
25
  teamId: envArgs.teamId || process.env.TEAM_ID || ''
36
26
  };
37
- logMessage('config.final', configuration);
27
+ console.log('Final configuration:', configuration);
38
28
  // Check for missing environment variables
39
29
  const missingEnvVars = Object.entries(configuration)
40
30
  .filter(([_, value]) => !value)
@@ -1,28 +1,4 @@
1
- /**
2
- * Prompt handlers for ClickUp MCP server.
3
- * Implements handlers for AI-powered analysis and summarization of ClickUp data.
4
- * Each handler processes ClickUp data and returns formatted text output.
5
- * @module handlers/prompts
6
- */
7
1
  import { getAllTasks } from '../utils/resolvers.js';
8
- /**
9
- * Generates a summary of all tasks in the workspace
10
- * Lists each task with its name and description, providing a quick overview
11
- * of all work items across spaces and lists.
12
- *
13
- * @param clickup - ClickUp service instance for API operations
14
- * @param teamId - Team ID to fetch tasks for
15
- * @returns Promise resolving to formatted text summary of all tasks
16
- *
17
- * @example
18
- * Output format:
19
- * ```
20
- * Summarized Tasks:
21
- *
22
- * - Task Name 1: Description of first task
23
- * - Task Name 2: Description of second task
24
- * ```
25
- */
26
2
  export async function handleSummarizeTasks(clickup, teamId) {
27
3
  const { tasks } = await getAllTasks(clickup, teamId);
28
4
  let output = "Summarized Tasks:\n\n";
@@ -31,35 +7,6 @@ export async function handleSummarizeTasks(clickup, teamId) {
31
7
  }
32
8
  return output;
33
9
  }
34
- /**
35
- * Analyzes task priorities across the workspace
36
- * Provides a statistical breakdown of task priorities, showing the distribution
37
- * of priority levels and identifying potential workload patterns.
38
- *
39
- * @param clickup - ClickUp service instance for API operations
40
- * @param teamId - Team ID to analyze tasks for
41
- * @returns Promise resolving to formatted text analysis of task priorities
42
- *
43
- * @example
44
- * Output format:
45
- * ```
46
- * Task Priorities Analysis:
47
- *
48
- * Available Priorities: 1, 2, 3, 4
49
- *
50
- * Priority Counts:
51
- * - Priority 1: 5
52
- * - Priority 2: 3
53
- * - Priority 3: 8
54
- * - Priority 4: 2
55
- * ```
56
- *
57
- * Priority levels:
58
- * 1 - Urgent
59
- * 2 - High
60
- * 3 - Normal
61
- * 4 - Low
62
- */
63
10
  export async function handleAnalyzeTaskPriorities(clickup, teamId) {
64
11
  const { tasks } = await getAllTasks(clickup, teamId);
65
12
  const priorities = tasks.map(task => task.priority?.priority);
@@ -1,311 +1,38 @@
1
- /**
2
- * Tool handlers for ClickUp MCP server.
3
- * Implements the business logic for each tool operation.
4
- * @module handlers/tools
5
- */
6
- import { resolveListId, resolveSpaceId, resolveFolderId } from '../utils/resolvers.js';
7
- import { logError } from '../utils/logger.js';
8
- /**
9
- * Handles the workspace_hierarchy tool request
10
- * Returns a formatted tree view of the ClickUp workspace structure
11
- * @param clickup - ClickUp service instance
12
- * @param teamId - Team ID to fetch hierarchy for
13
- * @returns Promise resolving to MCP-formatted response
14
- */
1
+ import { resolveListId } from '../utils/resolvers.js';
15
2
  export async function handleWorkspaceHierarchy(clickup, teamId) {
16
- try {
17
- const spaces = await clickup.getSpaces(teamId);
18
- const allLists = await clickup.getAllLists(teamId);
19
- let output = "ClickUp Workspace Hierarchy:\n\n";
20
- for (const space of spaces) {
21
- output += `Space: ${space.name} (ID: ${space.id})\n`;
22
- const folders = await clickup.getFolders(space.id);
23
- for (const folder of folders) {
24
- output += ` ├─ Folder: ${folder.name} (ID: ${folder.id})\n`;
25
- const folderLists = folder.lists || [];
26
- for (const list of folderLists) {
27
- const { statuses } = await clickup.getTasks(list.id);
28
- output += ` │ └─ List: ${list.name} (ID: ${list.id})\n`;
29
- output += ` │ Available Statuses: ${statuses.join(', ')}\n`;
30
- }
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`;
31
16
  }
32
- const spaceLists = allLists.filter(list => list.space &&
33
- list.space.id === space.id &&
34
- !folders.some(folder => folder.lists?.some(fl => fl.id === list.id)));
35
- if (spaceLists.length > 0) {
36
- output += " ├─ Lists (not in folders):\n";
37
- for (const list of spaceLists) {
38
- const { statuses } = await clickup.getTasks(list.id);
39
- output += ` │ └─ List: ${list.name} (ID: ${list.id})\n`;
40
- output += ` │ Available Statuses: ${statuses.join(', ')}\n`;
41
- }
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`;
42
27
  }
43
- output += "\n";
44
28
  }
45
- return {
46
- content: [{
47
- type: 'text',
48
- text: output
49
- }]
50
- };
51
- }
52
- catch (error) {
53
- logError('tool.workspace_hierarchy', error);
54
- const errorMessage = error instanceof Error ? error.message : String(error);
55
- return {
56
- content: [{
57
- type: 'error',
58
- text: `Failed to fetch workspace hierarchy: ${errorMessage}`
59
- }]
60
- };
29
+ output += "\n";
61
30
  }
31
+ return output;
62
32
  }
63
- /**
64
- * Handles the create_task tool request
65
- * Creates a new task in the specified list
66
- * @param clickup - ClickUp service instance
67
- * @param teamId - Team ID to create task in
68
- * @param args - Task creation arguments
69
- * @returns Promise resolving to MCP-formatted response
70
- */
71
33
  export async function handleCreateTask(clickup, teamId, args) {
72
- try {
73
- const listId = await resolveListId(clickup, teamId, args.listId, args.listName);
74
- const { listId: _, listName: __, ...taskData } = args;
75
- const task = await clickup.createTask(listId, taskData);
76
- return {
77
- content: [{
78
- type: 'text',
79
- text: `Successfully created task "${task.name}" (ID: ${task.id})`
80
- }]
81
- };
82
- }
83
- catch (error) {
84
- logError('tool.create_task', error);
85
- const errorMessage = error instanceof Error ? error.message : String(error);
86
- return {
87
- content: [{
88
- type: 'error',
89
- text: `Failed to create task: ${errorMessage}`
90
- }]
91
- };
92
- }
93
- }
94
- /**
95
- * Handles the create_bulk_tasks tool request
96
- * Creates multiple tasks in the specified list
97
- * @param clickup - ClickUp service instance
98
- * @param teamId - Team ID to create tasks in
99
- * @param args - Bulk task creation arguments
100
- * @returns Promise resolving to MCP-formatted response
101
- */
102
- export async function handleCreateBulkTasks(clickup, teamId, args) {
103
- try {
104
- const listId = await resolveListId(clickup, teamId, args.listId, args.listName);
105
- const { listId: _, listName: __, tasks } = args;
106
- const createdTasks = await Promise.all(tasks.map(taskData => clickup.createTask(listId, taskData)));
107
- return {
108
- content: [{
109
- type: 'text',
110
- text: `Successfully created ${createdTasks.length} tasks in list ${listId}`
111
- }]
112
- };
113
- }
114
- catch (error) {
115
- logError('tool.create_bulk_tasks', error);
116
- const errorMessage = error instanceof Error ? error.message : String(error);
117
- return {
118
- content: [{
119
- type: 'error',
120
- text: `Failed to create bulk tasks: ${errorMessage}`
121
- }]
122
- };
123
- }
124
- }
125
- /**
126
- * Handles the create_list tool request
127
- * Creates a new list in the specified space
128
- * @param clickup - ClickUp service instance
129
- * @param teamId - Team ID to create list in
130
- * @param args - List creation arguments
131
- * @returns Promise resolving to MCP-formatted response
132
- */
133
- export async function handleCreateList(clickup, teamId, args) {
134
- try {
135
- const spaceId = await resolveSpaceId(clickup, teamId, args.spaceId, args.spaceName);
136
- const { spaceId: _, spaceName: __, ...listData } = args;
137
- const list = await clickup.createList(spaceId, listData);
138
- return {
139
- content: [{
140
- type: 'text',
141
- text: `Successfully created list "${list.name}" (ID: ${list.id})`
142
- }]
143
- };
144
- }
145
- catch (error) {
146
- logError('tool.create_list', error);
147
- const errorMessage = error instanceof Error ? error.message : String(error);
148
- return {
149
- content: [{
150
- type: 'error',
151
- text: `Failed to create list: ${errorMessage}`
152
- }]
153
- };
154
- }
155
- }
156
- /**
157
- * Handles the create_folder tool request
158
- * Creates a new folder in the specified space
159
- * @param clickup - ClickUp service instance
160
- * @param teamId - Team ID to create folder in
161
- * @param args - Folder creation arguments
162
- * @returns Promise resolving to MCP-formatted response
163
- */
164
- export async function handleCreateFolder(clickup, teamId, args) {
165
- try {
166
- const spaceId = await resolveSpaceId(clickup, teamId, args.spaceId, args.spaceName);
167
- const { spaceId: _, spaceName: __, ...folderData } = args;
168
- const folder = await clickup.createFolder(spaceId, folderData);
169
- return {
170
- content: [{
171
- type: 'text',
172
- text: `Successfully created folder "${folder.name}" (ID: ${folder.id})`
173
- }]
174
- };
175
- }
176
- catch (error) {
177
- logError('tool.create_folder', error);
178
- const errorMessage = error instanceof Error ? error.message : String(error);
179
- return {
180
- content: [{
181
- type: 'error',
182
- text: `Failed to create folder: ${errorMessage}`
183
- }]
184
- };
185
- }
186
- }
187
- /**
188
- * Handles the create_list_in_folder tool request
189
- * Creates a new list within a specified folder
190
- * @param clickup - ClickUp service instance
191
- * @param teamId - Team ID to create list in
192
- * @param args - List creation arguments
193
- * @returns Promise resolving to MCP-formatted response
194
- */
195
- export async function handleCreateListInFolder(clickup, teamId, args) {
196
- try {
197
- let folderId = args.folderId;
198
- if (!folderId) {
199
- const spaceId = await resolveSpaceId(clickup, teamId, args.spaceId, args.spaceName);
200
- folderId = await resolveFolderId(clickup, teamId, undefined, args.folderName);
201
- }
202
- const { folderId: _, folderName: __, spaceId: ___, spaceName: ____, ...listData } = args;
203
- const list = await clickup.createListInFolder(folderId, listData);
204
- return {
205
- content: [{
206
- type: 'text',
207
- text: `Successfully created list "${list.name}" (ID: ${list.id}) in folder`
208
- }]
209
- };
210
- }
211
- catch (error) {
212
- logError('tool.create_list_in_folder', error);
213
- const errorMessage = error instanceof Error ? error.message : String(error);
214
- return {
215
- content: [{
216
- type: 'error',
217
- text: `Failed to create list in folder: ${errorMessage}`
218
- }]
219
- };
220
- }
221
- }
222
- /**
223
- * Handles the move_task tool request
224
- * Moves a task to a different list
225
- * @param clickup - ClickUp service instance
226
- * @param teamId - Team ID
227
- * @param args - Task move arguments
228
- * @returns Promise resolving to MCP-formatted response
229
- */
230
- export async function handleMoveTask(clickup, teamId, args) {
231
- try {
232
- const listId = await resolveListId(clickup, teamId, args.listId, args.listName);
233
- await clickup.moveTask(args.taskId, listId);
234
- return {
235
- content: [{
236
- type: 'text',
237
- text: `Successfully moved task ${args.taskId} to list ${listId}`
238
- }]
239
- };
240
- }
241
- catch (error) {
242
- logError('tool.move_task', error);
243
- const errorMessage = error instanceof Error ? error.message : String(error);
244
- return {
245
- content: [{
246
- type: 'error',
247
- text: `Failed to move task: ${errorMessage}`
248
- }]
249
- };
250
- }
251
- }
252
- /**
253
- * Handles the duplicate_task tool request
254
- * Creates a copy of a task in a specified list
255
- * @param clickup - ClickUp service instance
256
- * @param teamId - Team ID
257
- * @param args - Task duplication arguments
258
- * @returns Promise resolving to MCP-formatted response
259
- */
260
- export async function handleDuplicateTask(clickup, teamId, args) {
261
- try {
262
- const listId = await resolveListId(clickup, teamId, args.listId, args.listName);
263
- const task = await clickup.duplicateTask(args.taskId, listId);
264
- return {
265
- content: [{
266
- type: 'text',
267
- text: `Successfully duplicated task "${task.name}" (ID: ${task.id})`
268
- }]
269
- };
270
- }
271
- catch (error) {
272
- logError('tool.duplicate_task', error);
273
- const errorMessage = error instanceof Error ? error.message : String(error);
274
- return {
275
- content: [{
276
- type: 'error',
277
- text: `Failed to duplicate task: ${errorMessage}`
278
- }]
279
- };
280
- }
281
- }
282
- /**
283
- * Handles the update_task tool request
284
- * Updates an existing task with new data
285
- * @param clickup - ClickUp service instance
286
- * @param teamId - Team ID
287
- * @param args - Task update arguments
288
- * @returns Promise resolving to MCP-formatted response
289
- */
290
- export async function handleUpdateTask(clickup, teamId, args) {
291
- try {
292
- const { taskId, ...updateData } = args;
293
- const task = await clickup.updateTask(taskId, updateData);
294
- return {
295
- content: [{
296
- type: 'text',
297
- text: `Successfully updated task "${task.name}" (ID: ${task.id})`
298
- }]
299
- };
300
- }
301
- catch (error) {
302
- logError('tool.update_task', error);
303
- const errorMessage = error instanceof Error ? error.message : String(error);
304
- return {
305
- content: [{
306
- type: 'error',
307
- text: `Failed to update task: ${errorMessage}`
308
- }]
309
- };
310
- }
34
+ const listId = await resolveListId(clickup, teamId, args.listId, args.listName);
35
+ const { listId: _, listName: __, ...taskData } = args;
36
+ return await clickup.createTask(listId, taskData);
311
37
  }
38
+ // Add other handler functions for each tool...