@taazkareem/clickup-mcp-server 0.1.7 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,15 +1,41 @@
1
1
  # ClickUp MCP Server
2
2
 
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, and lists through a standardized protocol.
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
5
  ## Features
6
6
 
7
- - 🔄 List and read ClickUp tasks as resources
8
- - Create and update tasks through tools
9
- - 📊 Get spaces and lists with their IDs
10
- - 📝 Generate task descriptions with AI
11
- - 📋 Summarize tasks and analyze priorities
12
- - 🔒 Secure API key management
7
+ - 🔄 **Resource Management**
8
+ - List and read ClickUp tasks as resources
9
+ - View task details, status, and assignments
10
+ - Access task history and relationships
11
+
12
+ - 📂 **Workspace Organization**
13
+ - Create and manage spaces
14
+ - Create, update, and delete folders
15
+ - Create and manage lists (in spaces or folders)
16
+ - Flexible identification using IDs or names
17
+
18
+ - ✨ **Task Operations**
19
+ - Create and update tasks
20
+ - Move tasks between lists
21
+ - Duplicate tasks
22
+ - Set priorities and due dates
23
+ - Assign team members
24
+
25
+ - 📊 **Information Retrieval**
26
+ - Get spaces and lists with their IDs
27
+ - List available statuses
28
+ - Find items by name (case-insensitive)
29
+ - View task relationships
30
+
31
+ - 📝 **AI Integration**
32
+ - Generate task descriptions with AI
33
+ - Summarize tasks and analyze priorities
34
+ - Get AI-powered task recommendations
35
+
36
+ - 🔒 **Security**
37
+ - Secure API key management
38
+ - Environment-based configuration
13
39
 
14
40
  ## Installation
15
41
 
@@ -32,17 +58,32 @@ CLICKUP_API_KEY=your_api_key_here
32
58
  TEAM_ID=your_team_id_here
33
59
  ```
34
60
 
35
- ## Usage
61
+ ## Using with Cursor AI Composer
62
+
63
+ To add this server to Cursor AI Composer, follow these steps:
64
+
65
+ 1. Go to the Features section in the settings.
66
+ 2. Add the following command under MCP Servers:
36
67
 
37
- ### Starting the Server
38
68
  ```bash
39
- clickup-mcp-server
69
+ npx -y @taazkareem/clickup-mcp-server \
70
+ --env CLICKUP_API_KEY=your_api_key_here \
71
+ --env TEAM_ID=your_team_id_here
40
72
  ```
73
+ 3. Replace `your_api_key_here` and `your_team_id_here` with your actual ClickUp credentials.
74
+ 4. Click on 'Save' to add the server.
75
+
76
+ You can get these values from:
77
+ - `CLICKUP_API_KEY`: Get from [ClickUp Settings > Apps](https://app.clickup.com/settings/apps)
78
+ - `TEAM_ID`: Your ClickUp Team ID (found in the URL when viewing your workspace or via API)
79
+
80
+ > **Security Note**: Your API key will be stored securely and will not be exposed to AI models.
41
81
 
42
82
  ### Available Tools
43
83
 
44
84
  1. **list_spaces**
45
85
  - Lists all spaces and their lists with IDs
86
+ - Shows available statuses for each list
46
87
  - No parameters required
47
88
 
48
89
  2. **create_task**
@@ -55,51 +96,122 @@ clickup-mcp-server
55
96
  - `status`: Task status
56
97
  - `priority`: Priority level (1-4)
57
98
  - `dueDate`: Due date (ISO string)
99
+ - `assignees`: Array of user IDs
100
+
101
+ 3. **create_bulk_tasks**
102
+ - Creates multiple tasks simultaneously in a list
103
+ - Required parameters:
104
+ - `listId`: ID of the list to create the tasks in
105
+ - `tasks`: Array of task objects, each containing:
106
+ - `name`: Name of the task (required)
107
+ - `description`: Task description (optional)
108
+ - `status`: Task status (optional)
109
+ - `priority`: Priority level 1-4 (optional)
110
+ - `dueDate`: Due date ISO string (optional)
111
+ - `assignees`: Array of user IDs (optional)
112
+
113
+ 4. **create_list**
114
+ - Creates a new list in a space
115
+ - Required parameters:
116
+ - `name`: Name of the list
117
+ - Optional parameters:
118
+ - `spaceId`: ID of the space (optional if spaceName provided)
119
+ - `spaceName`: Name of the space (optional if spaceId provided)
120
+ - `content`: List description
121
+ - `status`: List status
122
+ - `priority`: Priority level (1-4)
123
+ - `dueDate`: Due date (ISO string)
124
+
125
+ 5. **create_folder**
126
+ - Creates a new folder in a space
127
+ - Required parameters:
128
+ - `name`: Name of the folder
129
+ - Optional parameters:
130
+ - `spaceId`: ID of the space (optional if spaceName provided)
131
+ - `spaceName`: Name of the space (optional if spaceId provided)
132
+ - `override_statuses`: Whether to override space statuses
133
+
134
+ 6. **create_list_in_folder**
135
+ - Creates a new list within a folder
136
+ - Required parameters:
137
+ - `name`: Name of the list
138
+ - Optional parameters:
139
+ - `folderId`: ID of the folder (optional if using folderName)
140
+ - `folderName`: Name of the folder
141
+ - `spaceId`: ID of the space (required if using folderName)
142
+ - `spaceName`: Name of the space (alternative to spaceId)
143
+ - `content`: List description
144
+ - `status`: List status
145
+
146
+ 7. **move_task**
147
+ - Moves a task to a different list
148
+ - Required parameters:
149
+ - `taskId`: ID of the task to move
150
+ - `listId`: ID of the destination list
151
+
152
+ 8. **duplicate_task**
153
+ - Creates a copy of a task in a specified list
154
+ - Required parameters:
155
+ - `taskId`: ID of the task to duplicate
156
+ - `listId`: ID of the destination list
58
157
 
59
- 3. **update_task**
158
+ 9. **update_task**
60
159
  - Updates an existing task
61
160
  - Required parameters:
62
161
  - `taskId`: ID of the task to update
63
162
  - Optional parameters:
64
- - Same as create_task
163
+ - `name`: New task name
164
+ - `description`: New description
165
+ - `status`: New status
166
+ - `priority`: New priority level (1-4)
167
+ - `dueDate`: New due date (ISO string)
65
168
 
66
169
  ### Available Prompts
67
170
 
68
171
  1. **summarize_tasks**
69
- - Provides a summary of all tasks
70
- - Groups by status and highlights priorities
172
+ - Provides a comprehensive summary of tasks
173
+ - Groups tasks by status
174
+ - Highlights priorities and deadlines
175
+ - Suggests task relationships
71
176
 
72
177
  2. **analyze_priorities**
73
- - Analyzes task priorities
74
- - Suggests optimizations and sequencing
178
+ - Analyzes task priority distribution
179
+ - Identifies misaligned priorities
180
+ - Suggests priority adjustments
181
+ - Recommends task sequencing
75
182
 
76
183
  3. **generate_description**
77
184
  - Helps generate detailed task descriptions
78
- - Includes objectives, criteria, and dependencies
185
+ - Includes:
186
+ - Clear objectives
187
+ - Success criteria
188
+ - Required resources
189
+ - Dependencies
190
+ - Potential risks
79
191
 
80
- ## Using with Cursor AI Composer
192
+ ## Name Resolution
81
193
 
82
- To add this server to Cursor AI Composer:
194
+ Most tools support finding items by either ID or name:
195
+ - Spaces can be referenced by `spaceId` or `spaceName`
196
+ - Folders can be referenced by `folderId` or `folderName` (with space information)
197
+ - Lists can be referenced by `listId` or found within spaces/folders
83
198
 
84
- ```bash
85
- npx -y @taazkareem/clickup-mcp-server \
86
- --env CLICKUP_API_KEY=your_api_key_here \
87
- --env TEAM_ID=your_team_id_here
88
- ```
199
+ Name matching is case-insensitive for convenience.
89
200
 
90
- You can get these values from:
91
- - `CLICKUP_API_KEY`: Get from [ClickUp Settings > Apps](https://app.clickup.com/settings/apps)
92
- - `TEAM_ID`: Your ClickUp Team ID (found in the URL when viewing your workspace or via API)
201
+ ## Error Handling
93
202
 
94
- > ⚠️ **Important**: Make sure to replace `your_api_key_here` and `your_team_id_here` with your actual ClickUp credentials.
95
-
96
- > **Security Note**: Your API key will be stored securely and will not be exposed to AI models.
203
+ The server provides clear error messages for common scenarios:
204
+ - Missing required parameters
205
+ - Invalid IDs or names
206
+ - Items not found
207
+ - Permission issues
208
+ - API errors
97
209
 
98
210
  ## Development
99
211
 
100
212
  1. Clone the repository:
101
213
  ```bash
102
- git clone https://github.com/yourusername/clickup-mcp-server.git
214
+ git clone https://github.com/taazkareem/clickup-mcp-server.git
103
215
  cd clickup-mcp-server
104
216
  ```
105
217
 
@@ -119,4 +231,4 @@ Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md)
119
231
 
120
232
  ## License
121
233
 
122
- This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
234
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
package/build/index.js CHANGED
@@ -143,6 +143,199 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
143
143
  required: ["listId", "name"]
144
144
  }
145
145
  },
146
+ {
147
+ name: "create_bulk_tasks",
148
+ description: "Create multiple tasks in a ClickUp list",
149
+ inputSchema: {
150
+ type: "object",
151
+ properties: {
152
+ listId: {
153
+ type: "string",
154
+ description: "ID of the list to create the tasks in"
155
+ },
156
+ tasks: {
157
+ type: "array",
158
+ description: "Array of tasks to create",
159
+ items: {
160
+ type: "object",
161
+ properties: {
162
+ name: {
163
+ type: "string",
164
+ description: "Name of the task"
165
+ },
166
+ description: {
167
+ type: "string",
168
+ description: "Description of the task"
169
+ },
170
+ status: {
171
+ type: "string",
172
+ description: "Status of the task"
173
+ },
174
+ priority: {
175
+ type: "number",
176
+ description: "Priority level (1-4)"
177
+ },
178
+ dueDate: {
179
+ type: "string",
180
+ description: "Due date of the task (ISO string)"
181
+ },
182
+ assignees: {
183
+ type: "array",
184
+ items: {
185
+ type: "number"
186
+ },
187
+ description: "Array of user IDs to assign to the task"
188
+ }
189
+ },
190
+ required: ["name"]
191
+ }
192
+ }
193
+ },
194
+ required: ["listId", "tasks"]
195
+ }
196
+ },
197
+ {
198
+ name: "create_list",
199
+ description: "Create a new list in a ClickUp space",
200
+ inputSchema: {
201
+ type: "object",
202
+ properties: {
203
+ spaceId: {
204
+ type: "string",
205
+ description: "ID of the space to create the list in (optional if spaceName is provided)"
206
+ },
207
+ spaceName: {
208
+ type: "string",
209
+ description: "Name of the space to create the list in (optional if spaceId is provided)"
210
+ },
211
+ name: {
212
+ type: "string",
213
+ description: "Name of the list"
214
+ },
215
+ content: {
216
+ type: "string",
217
+ description: "Description or content of the list"
218
+ },
219
+ dueDate: {
220
+ type: "string",
221
+ description: "Due date for the list (ISO string)"
222
+ },
223
+ priority: {
224
+ type: "number",
225
+ description: "Priority of the list (1-4)"
226
+ },
227
+ assignee: {
228
+ type: "number",
229
+ description: "User ID to assign the list to"
230
+ },
231
+ status: {
232
+ type: "string",
233
+ description: "Status of the list"
234
+ }
235
+ },
236
+ required: ["name"]
237
+ }
238
+ },
239
+ {
240
+ name: "create_folder",
241
+ description: "Create a new folder in a ClickUp space",
242
+ inputSchema: {
243
+ type: "object",
244
+ properties: {
245
+ spaceId: {
246
+ type: "string",
247
+ description: "ID of the space to create the folder in (optional if spaceName is provided)"
248
+ },
249
+ spaceName: {
250
+ type: "string",
251
+ description: "Name of the space to create the folder in (optional if spaceId is provided)"
252
+ },
253
+ name: {
254
+ type: "string",
255
+ description: "Name of the folder"
256
+ },
257
+ override_statuses: {
258
+ type: "boolean",
259
+ description: "Whether to override space statuses"
260
+ }
261
+ },
262
+ required: ["name"]
263
+ }
264
+ },
265
+ {
266
+ name: "create_list_in_folder",
267
+ description: "Create a new list in a ClickUp folder",
268
+ inputSchema: {
269
+ type: "object",
270
+ properties: {
271
+ folderId: {
272
+ type: "string",
273
+ description: "ID of the folder to create the list in (optional if folderName and spaceId/spaceName are provided)"
274
+ },
275
+ folderName: {
276
+ type: "string",
277
+ description: "Name of the folder to create the list in"
278
+ },
279
+ spaceId: {
280
+ type: "string",
281
+ description: "ID of the space containing the folder (required if using folderName)"
282
+ },
283
+ spaceName: {
284
+ type: "string",
285
+ description: "Name of the space containing the folder (alternative to spaceId)"
286
+ },
287
+ name: {
288
+ type: "string",
289
+ description: "Name of the list"
290
+ },
291
+ content: {
292
+ type: "string",
293
+ description: "Description or content of the list"
294
+ },
295
+ status: {
296
+ type: "string",
297
+ description: "Status of the list"
298
+ }
299
+ },
300
+ required: ["name"]
301
+ }
302
+ },
303
+ {
304
+ name: "move_task",
305
+ description: "Move a task to a different list",
306
+ inputSchema: {
307
+ type: "object",
308
+ properties: {
309
+ taskId: {
310
+ type: "string",
311
+ description: "ID of the task to move"
312
+ },
313
+ listId: {
314
+ type: "string",
315
+ description: "ID of the destination list"
316
+ }
317
+ },
318
+ required: ["taskId", "listId"]
319
+ }
320
+ },
321
+ {
322
+ name: "duplicate_task",
323
+ description: "Duplicate a task to a list",
324
+ inputSchema: {
325
+ type: "object",
326
+ properties: {
327
+ taskId: {
328
+ type: "string",
329
+ description: "ID of the task to duplicate"
330
+ },
331
+ listId: {
332
+ type: "string",
333
+ description: "ID of the list to create the duplicate in"
334
+ }
335
+ },
336
+ required: ["taskId", "listId"]
337
+ }
338
+ },
146
339
  {
147
340
  name: "update_task",
148
341
  description: "Update an existing task in ClickUp",
@@ -232,6 +425,132 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
232
425
  }]
233
426
  };
234
427
  }
428
+ case "create_bulk_tasks": {
429
+ const args = request.params.arguments;
430
+ if (!args.listId || !args.tasks || !args.tasks.length) {
431
+ throw new Error("listId and at least one task are required");
432
+ }
433
+ const { listId, tasks } = args;
434
+ const createdTasks = await clickup.createBulkTasks(listId, { tasks });
435
+ return {
436
+ content: [{
437
+ type: "text",
438
+ text: `Created ${createdTasks.length} tasks:\n${createdTasks.map(task => `- ${task.id}: ${task.name}`).join('\n')}`
439
+ }]
440
+ };
441
+ }
442
+ case "create_list": {
443
+ const args = request.params.arguments;
444
+ if (!args.name) {
445
+ throw new Error("name is required");
446
+ }
447
+ let spaceId = args.spaceId;
448
+ if (!spaceId && args.spaceName) {
449
+ const space = await clickup.findSpaceByName(config.teamId, args.spaceName);
450
+ if (!space) {
451
+ throw new Error(`Space with name "${args.spaceName}" not found`);
452
+ }
453
+ spaceId = space.id;
454
+ }
455
+ if (!spaceId) {
456
+ throw new Error("Either spaceId or spaceName must be provided");
457
+ }
458
+ const { spaceId: _, spaceName: __, ...listData } = args;
459
+ const list = await clickup.createList(spaceId, listData);
460
+ return {
461
+ content: [{
462
+ type: "text",
463
+ text: `Created list ${list.id}: ${list.name}`
464
+ }]
465
+ };
466
+ }
467
+ case "create_folder": {
468
+ const args = request.params.arguments;
469
+ if (!args.name) {
470
+ throw new Error("name is required");
471
+ }
472
+ let spaceId = args.spaceId;
473
+ if (!spaceId && args.spaceName) {
474
+ const space = await clickup.findSpaceByName(config.teamId, args.spaceName);
475
+ if (!space) {
476
+ throw new Error(`Space with name "${args.spaceName}" not found`);
477
+ }
478
+ spaceId = space.id;
479
+ }
480
+ if (!spaceId) {
481
+ throw new Error("Either spaceId or spaceName must be provided");
482
+ }
483
+ const { spaceId: _, spaceName: __, ...folderData } = args;
484
+ const folder = await clickup.createFolder(spaceId, folderData);
485
+ return {
486
+ content: [{
487
+ type: "text",
488
+ text: `Created folder ${folder.id}: ${folder.name}`
489
+ }]
490
+ };
491
+ }
492
+ case "create_list_in_folder": {
493
+ const args = request.params.arguments;
494
+ if (!args.name) {
495
+ throw new Error("name is required");
496
+ }
497
+ let folderId = args.folderId;
498
+ if (!folderId && args.folderName) {
499
+ let spaceId = args.spaceId;
500
+ if (!spaceId && args.spaceName) {
501
+ const space = await clickup.findSpaceByName(config.teamId, args.spaceName);
502
+ if (!space) {
503
+ throw new Error(`Space with name "${args.spaceName}" not found`);
504
+ }
505
+ spaceId = space.id;
506
+ }
507
+ if (!spaceId) {
508
+ throw new Error("Either spaceId or spaceName must be provided when using folderName");
509
+ }
510
+ const folder = await clickup.findFolderByName(spaceId, args.folderName);
511
+ if (!folder) {
512
+ throw new Error(`Folder with name "${args.folderName}" not found`);
513
+ }
514
+ folderId = folder.id;
515
+ }
516
+ if (!folderId) {
517
+ throw new Error("Either folderId or folderName (with space information) must be provided");
518
+ }
519
+ const { folderId: _, folderName: __, spaceId: ___, spaceName: ____, ...listData } = args;
520
+ const list = await clickup.createListInFolder(folderId, listData);
521
+ return {
522
+ content: [{
523
+ type: "text",
524
+ text: `Created list ${list.id}: ${list.name} in folder`
525
+ }]
526
+ };
527
+ }
528
+ case "move_task": {
529
+ const args = request.params.arguments;
530
+ if (!args.taskId || !args.listId) {
531
+ throw new Error("taskId and listId are required");
532
+ }
533
+ const task = await clickup.moveTask(args.taskId, args.listId);
534
+ return {
535
+ content: [{
536
+ type: "text",
537
+ text: `Moved task ${task.id} to list ${args.listId}`
538
+ }]
539
+ };
540
+ }
541
+ case "duplicate_task": {
542
+ const args = request.params.arguments;
543
+ if (!args.taskId || !args.listId) {
544
+ throw new Error("taskId and listId are required");
545
+ }
546
+ const task = await clickup.duplicateTask(args.taskId, args.listId);
547
+ return {
548
+ content: [{
549
+ type: "text",
550
+ text: `Duplicated task ${args.taskId} to new task ${task.id} in list ${args.listId}`
551
+ }]
552
+ };
553
+ }
235
554
  case "update_task": {
236
555
  const args = request.params.arguments;
237
556
  if (!args.taskId) {
@@ -38,6 +38,10 @@ export class ClickUpService {
38
38
  const response = await this.client.post(`/list/${listId}/task`, data);
39
39
  return response.data;
40
40
  }
41
+ async createBulkTasks(listId, data) {
42
+ const tasks = await Promise.all(data.tasks.map(taskData => this.createTask(listId, taskData)));
43
+ return tasks;
44
+ }
41
45
  async updateTask(taskId, data) {
42
46
  const response = await this.client.put(`/task/${taskId}`, data);
43
47
  return response.data;
@@ -67,4 +71,60 @@ export class ClickUpService {
67
71
  const response = await this.client.get(`/space/${spaceId}`);
68
72
  return response.data;
69
73
  }
74
+ async findSpaceByName(teamId, spaceName) {
75
+ const spaces = await this.getSpaces(teamId);
76
+ return spaces.find(space => space.name.toLowerCase() === spaceName.toLowerCase()) || null;
77
+ }
78
+ async createList(spaceId, data) {
79
+ const response = await this.client.post(`/space/${spaceId}/list`, data);
80
+ return response.data;
81
+ }
82
+ // Folders
83
+ async getFolders(spaceId) {
84
+ const response = await this.client.get(`/space/${spaceId}/folder`);
85
+ return response.data.folders;
86
+ }
87
+ async getFolder(folderId) {
88
+ const response = await this.client.get(`/folder/${folderId}`);
89
+ return response.data;
90
+ }
91
+ async createFolder(spaceId, data) {
92
+ const response = await this.client.post(`/space/${spaceId}/folder`, data);
93
+ return response.data;
94
+ }
95
+ async deleteFolder(folderId) {
96
+ await this.client.delete(`/folder/${folderId}`);
97
+ }
98
+ async createListInFolder(folderId, data) {
99
+ const response = await this.client.post(`/folder/${folderId}/list`, data);
100
+ return response.data;
101
+ }
102
+ async findFolderByName(spaceId, folderName) {
103
+ const folders = await this.getFolders(spaceId);
104
+ return folders.find(folder => folder.name.toLowerCase() === folderName.toLowerCase()) || null;
105
+ }
106
+ // Additional helper methods
107
+ async moveTask(taskId, listId) {
108
+ const response = await this.client.post(`/task/${taskId}`, {
109
+ list: listId
110
+ });
111
+ return response.data;
112
+ }
113
+ async duplicateTask(taskId, listId) {
114
+ const response = await this.client.post(`/task/${taskId}/duplicate`, {
115
+ list: listId
116
+ });
117
+ return response.data;
118
+ }
119
+ async deleteList(listId) {
120
+ await this.client.delete(`/list/${listId}`);
121
+ }
122
+ async updateList(listId, data) {
123
+ const response = await this.client.put(`/list/${listId}`, data);
124
+ return response.data;
125
+ }
126
+ async findListByName(spaceId, listName) {
127
+ const lists = await this.getLists(spaceId);
128
+ return lists.find(list => list.name.toLowerCase() === listName.toLowerCase()) || null;
129
+ }
70
130
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taazkareem/clickup-mcp-server",
3
- "version": "0.1.7",
3
+ "version": "0.3.0",
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",