@taazkareem/clickup-mcp-server 0.4.56 → 0.4.57
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 +52 -23
- package/build/services/clickup.js +30 -22
- package/package.json +2 -4
package/build/index.js
CHANGED
|
@@ -109,7 +109,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
109
109
|
description: "Due date of the task (Unix timestamp in milliseconds). Convert dates to this format before submitting."
|
|
110
110
|
}
|
|
111
111
|
},
|
|
112
|
-
required: [
|
|
112
|
+
required: []
|
|
113
113
|
}
|
|
114
114
|
},
|
|
115
115
|
{
|
|
@@ -164,11 +164,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
164
164
|
description: "Array of user IDs to assign to the task"
|
|
165
165
|
}
|
|
166
166
|
},
|
|
167
|
-
required: [
|
|
167
|
+
required: []
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
170
|
},
|
|
171
|
-
required: [
|
|
171
|
+
required: []
|
|
172
172
|
}
|
|
173
173
|
},
|
|
174
174
|
{
|
|
@@ -210,7 +210,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
210
210
|
description: "Status of the list"
|
|
211
211
|
}
|
|
212
212
|
},
|
|
213
|
-
required: [
|
|
213
|
+
required: []
|
|
214
214
|
}
|
|
215
215
|
},
|
|
216
216
|
{
|
|
@@ -236,7 +236,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
236
236
|
description: "Whether to override space statuses with folder-specific statuses"
|
|
237
237
|
}
|
|
238
238
|
},
|
|
239
|
-
required: [
|
|
239
|
+
required: []
|
|
240
240
|
}
|
|
241
241
|
},
|
|
242
242
|
{
|
|
@@ -274,7 +274,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
274
274
|
description: "Status of the list (uses folder default if not specified)"
|
|
275
275
|
}
|
|
276
276
|
},
|
|
277
|
-
required: [
|
|
277
|
+
required: []
|
|
278
278
|
}
|
|
279
279
|
},
|
|
280
280
|
{
|
|
@@ -304,7 +304,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
304
304
|
description: "Name of the destination list - will automatically find the list by name (optional if using listId instead). Only use this if you don't already have the list ID from previous responses."
|
|
305
305
|
}
|
|
306
306
|
},
|
|
307
|
-
required: [
|
|
307
|
+
required: []
|
|
308
308
|
}
|
|
309
309
|
},
|
|
310
310
|
{
|
|
@@ -334,7 +334,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
334
334
|
description: "Name of the list to create the duplicate in - will automatically find the list by name (optional if using listId instead). Only use this if you don't already have the list ID from previous responses."
|
|
335
335
|
}
|
|
336
336
|
},
|
|
337
|
-
required: [
|
|
337
|
+
required: []
|
|
338
338
|
}
|
|
339
339
|
},
|
|
340
340
|
{
|
|
@@ -363,20 +363,22 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
363
363
|
type: "string",
|
|
364
364
|
description: "New plain text description for the task"
|
|
365
365
|
},
|
|
366
|
+
markdown_description: {
|
|
367
|
+
type: "string",
|
|
368
|
+
description: "New markdown formatted description for the task. If provided, this takes precedence over description"
|
|
369
|
+
},
|
|
366
370
|
status: {
|
|
367
371
|
type: "string",
|
|
368
372
|
description: "New status for the task (must be a valid status in the task's list)"
|
|
369
373
|
},
|
|
370
374
|
priority: {
|
|
371
|
-
type: "number",
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
type: "string",
|
|
376
|
-
description: "New due date for the task (Unix timestamp in milliseconds). Convert dates to this format before submitting."
|
|
375
|
+
type: ["number", "null"],
|
|
376
|
+
enum: [1, 2, 3, 4, null],
|
|
377
|
+
description: "New priority for the task (1-4 or null), where 1 is urgent/highest priority and 4 is lowest priority. Set to null to clear priority.",
|
|
378
|
+
optional: true
|
|
377
379
|
}
|
|
378
380
|
},
|
|
379
|
-
required: [
|
|
381
|
+
required: []
|
|
380
382
|
}
|
|
381
383
|
},
|
|
382
384
|
{
|
|
@@ -456,7 +458,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
456
458
|
description: "Object with custom field IDs as keys and desired values for filtering"
|
|
457
459
|
}
|
|
458
460
|
},
|
|
459
|
-
required: [
|
|
461
|
+
required: []
|
|
460
462
|
}
|
|
461
463
|
},
|
|
462
464
|
{
|
|
@@ -478,7 +480,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
478
480
|
description: "Optional: Name of the list to narrow down task search when multiple tasks have the same name"
|
|
479
481
|
}
|
|
480
482
|
},
|
|
481
|
-
required: [
|
|
483
|
+
required: []
|
|
482
484
|
}
|
|
483
485
|
},
|
|
484
486
|
{
|
|
@@ -500,7 +502,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
500
502
|
description: "Optional: Name of the list to narrow down task search when multiple tasks have the same name"
|
|
501
503
|
}
|
|
502
504
|
},
|
|
503
|
-
required: [
|
|
505
|
+
required: []
|
|
504
506
|
}
|
|
505
507
|
},
|
|
506
508
|
{
|
|
@@ -730,12 +732,17 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
730
732
|
}
|
|
731
733
|
case "create_bulk_tasks": {
|
|
732
734
|
const args = request.params.arguments;
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
}
|
|
736
|
-
if (!args.tasks || args.tasks.length === 0) {
|
|
735
|
+
// First validate tasks array
|
|
736
|
+
if (!args.tasks || !Array.isArray(args.tasks) || args.tasks.length === 0) {
|
|
737
737
|
throw new Error("tasks array is required and must not be empty");
|
|
738
738
|
}
|
|
739
|
+
// Validate each task has required fields
|
|
740
|
+
args.tasks.forEach((task, index) => {
|
|
741
|
+
if (!task.name) {
|
|
742
|
+
throw new Error(`Task at index ${index} is missing required field 'name'`);
|
|
743
|
+
}
|
|
744
|
+
});
|
|
745
|
+
// Get listId from name if needed
|
|
739
746
|
let listId = args.listId;
|
|
740
747
|
if (!listId && args.listName) {
|
|
741
748
|
const result = await clickup.findListIDByName(args.listName);
|
|
@@ -744,12 +751,23 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
744
751
|
}
|
|
745
752
|
listId = result.id;
|
|
746
753
|
}
|
|
754
|
+
// Now validate we have a listId
|
|
755
|
+
if (!listId) {
|
|
756
|
+
throw new Error("Either listId or listName must be provided");
|
|
757
|
+
}
|
|
747
758
|
const { listId: _, listName: __, tasks } = args;
|
|
748
759
|
const createdTasks = await clickup.createBulkTasks(listId, { tasks });
|
|
749
760
|
return {
|
|
750
761
|
content: [{
|
|
751
762
|
type: "text",
|
|
752
|
-
text:
|
|
763
|
+
text: JSON.stringify({
|
|
764
|
+
message: `Created ${createdTasks.length} tasks`,
|
|
765
|
+
tasks: createdTasks.map(task => ({
|
|
766
|
+
id: task.id,
|
|
767
|
+
name: task.name,
|
|
768
|
+
url: task.url
|
|
769
|
+
}))
|
|
770
|
+
}, null, 2)
|
|
753
771
|
}]
|
|
754
772
|
};
|
|
755
773
|
}
|
|
@@ -902,9 +920,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
902
920
|
}
|
|
903
921
|
case "update_task": {
|
|
904
922
|
const args = request.params.arguments;
|
|
923
|
+
// Require either taskId or taskName
|
|
905
924
|
if (!args.taskId && !args.taskName) {
|
|
906
925
|
throw new Error("Either taskId or taskName is required");
|
|
907
926
|
}
|
|
927
|
+
// Get taskId from taskName if needed
|
|
908
928
|
let taskId = args.taskId;
|
|
909
929
|
if (!taskId && args.taskName) {
|
|
910
930
|
const result = await clickup.findTaskByName(args.taskName, undefined, args.listName);
|
|
@@ -913,7 +933,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
913
933
|
}
|
|
914
934
|
taskId = result.id;
|
|
915
935
|
}
|
|
936
|
+
// Remove helper fields before updating
|
|
916
937
|
const { taskId: _, taskName: __, listName: ___, ...updateData } = args;
|
|
938
|
+
// Ensure priority is properly handled
|
|
939
|
+
if (updateData.priority !== undefined && updateData.priority !== null) {
|
|
940
|
+
const priority = Number(updateData.priority);
|
|
941
|
+
if (isNaN(priority) || ![1, 2, 3, 4].includes(priority)) {
|
|
942
|
+
throw new Error("Priority must be a number between 1 and 4, or null to clear priority");
|
|
943
|
+
}
|
|
944
|
+
updateData.priority = priority;
|
|
945
|
+
}
|
|
917
946
|
const task = await clickup.updateTask(taskId, updateData);
|
|
918
947
|
return {
|
|
919
948
|
content: [{
|
|
@@ -59,16 +59,7 @@ export class ClickUpService {
|
|
|
59
59
|
*/
|
|
60
60
|
async makeRequest(requestFn) {
|
|
61
61
|
await this.checkRateLimit();
|
|
62
|
-
|
|
63
|
-
return await requestFn();
|
|
64
|
-
}
|
|
65
|
-
catch (error) {
|
|
66
|
-
if (error.response?.status === 429) {
|
|
67
|
-
// Let the interceptor handle it
|
|
68
|
-
throw error;
|
|
69
|
-
}
|
|
70
|
-
throw error;
|
|
71
|
-
}
|
|
62
|
+
return await requestFn();
|
|
72
63
|
}
|
|
73
64
|
/**
|
|
74
65
|
* Initializes the ClickUpService singleton instance.
|
|
@@ -183,10 +174,15 @@ export class ClickUpService {
|
|
|
183
174
|
async createTask(listId, data) {
|
|
184
175
|
return this.makeRequest(async () => {
|
|
185
176
|
const taskData = { ...data };
|
|
186
|
-
|
|
187
|
-
|
|
177
|
+
// If markdown_description is provided, it takes precedence
|
|
178
|
+
if (taskData.markdown_description) {
|
|
179
|
+
// Ensure we don't send both to avoid confusion
|
|
188
180
|
delete taskData.description;
|
|
189
181
|
}
|
|
182
|
+
else if (taskData.description) {
|
|
183
|
+
// Only use description as-is, don't auto-convert to markdown
|
|
184
|
+
taskData.description = taskData.description.trim();
|
|
185
|
+
}
|
|
190
186
|
const response = await this.client.post(`/list/${listId}/task`, taskData);
|
|
191
187
|
return response.data;
|
|
192
188
|
});
|
|
@@ -200,10 +196,15 @@ export class ClickUpService {
|
|
|
200
196
|
for (const taskData of data.tasks) {
|
|
201
197
|
await this.makeRequest(async () => {
|
|
202
198
|
const processedTask = { ...taskData };
|
|
203
|
-
|
|
204
|
-
|
|
199
|
+
// If markdown_description is provided, it takes precedence
|
|
200
|
+
if (processedTask.markdown_description) {
|
|
201
|
+
// Ensure we don't send both to avoid confusion
|
|
205
202
|
delete processedTask.description;
|
|
206
203
|
}
|
|
204
|
+
else if (processedTask.description) {
|
|
205
|
+
// Only use description as-is, don't auto-convert to markdown
|
|
206
|
+
processedTask.description = processedTask.description.trim();
|
|
207
|
+
}
|
|
207
208
|
const response = await this.client.post(`/list/${listId}/task`, processedTask);
|
|
208
209
|
createdTasks.push(response.data);
|
|
209
210
|
});
|
|
@@ -216,7 +217,21 @@ export class ClickUpService {
|
|
|
216
217
|
*/
|
|
217
218
|
async updateTask(taskId, data) {
|
|
218
219
|
return this.makeRequest(async () => {
|
|
219
|
-
const
|
|
220
|
+
const updateData = { ...data };
|
|
221
|
+
// If markdown_description is provided, it takes precedence
|
|
222
|
+
if (updateData.markdown_description) {
|
|
223
|
+
// Ensure we don't send both to avoid confusion
|
|
224
|
+
delete updateData.description;
|
|
225
|
+
}
|
|
226
|
+
else if (updateData.description) {
|
|
227
|
+
// Only use description as-is, don't auto-convert to markdown
|
|
228
|
+
updateData.description = updateData.description.trim();
|
|
229
|
+
}
|
|
230
|
+
// Handle null priority explicitly
|
|
231
|
+
if (updateData.priority === null) {
|
|
232
|
+
updateData.priority = null;
|
|
233
|
+
}
|
|
234
|
+
const response = await this.client.put(`/task/${taskId}`, updateData);
|
|
220
235
|
return response.data;
|
|
221
236
|
});
|
|
222
237
|
}
|
|
@@ -730,11 +745,4 @@ export class ClickUpService {
|
|
|
730
745
|
: `${task.space.name} > ${task.list.name} > ${task.name}`;
|
|
731
746
|
return { id: task.id, path };
|
|
732
747
|
}
|
|
733
|
-
async getTaskStatuses(listId) {
|
|
734
|
-
const response = await this.getTasks(listId);
|
|
735
|
-
const statuses = [...new Set(response.tasks
|
|
736
|
-
.filter((task) => task.status !== undefined)
|
|
737
|
-
.map((task) => task.status.status))];
|
|
738
|
-
return statuses;
|
|
739
|
-
}
|
|
740
748
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taazkareem/clickup-mcp-server",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.57",
|
|
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",
|
|
@@ -44,10 +44,8 @@
|
|
|
44
44
|
"homepage": "https://github.com/taazkareem/clickup-mcp-server#readme",
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@modelcontextprotocol/sdk": "0.6.0",
|
|
47
|
-
"@types/express": "^5.0.0",
|
|
48
47
|
"axios": "^1.6.7",
|
|
49
|
-
"dotenv": "^16.4.1"
|
|
50
|
-
"express": "^4.21.2"
|
|
48
|
+
"dotenv": "^16.4.1"
|
|
51
49
|
},
|
|
52
50
|
"devDependencies": {
|
|
53
51
|
"@types/node": "^20.11.16",
|