@sebastienheyd/clickup-mcp 1.7.1 → 1.7.3
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/dist/shared/utils.d.ts +3 -2
- package/dist/shared/utils.d.ts.map +1 -1
- package/dist/shared/utils.js +17 -8
- package/dist/tools/search-tools.d.ts.map +1 -1
- package/dist/tools/search-tools.js +10 -2
- package/dist/tools/task-tools.js +2 -2
- package/dist/tools/task-write-tools.js +2 -2
- package/dist/tools/time-tools.d.ts.map +1 -1
- package/dist/tools/time-tools.js +10 -4
- package/package.json +2 -2
package/dist/shared/utils.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import Fuse from 'fuse.js';
|
|
2
2
|
/**
|
|
3
3
|
* Checks if a string looks like a valid ClickUp task ID
|
|
4
|
-
* Valid task IDs are 6
|
|
4
|
+
* Valid task IDs are at least 6 characters long and contain only alphanumeric characters.
|
|
5
|
+
* No upper bound: ClickUp now issues IDs of 10+ characters (e.g. "wdrv93ebwx").
|
|
5
6
|
*/
|
|
6
7
|
export declare function isTaskId(str: string): boolean;
|
|
7
8
|
/**
|
|
@@ -42,7 +43,7 @@ export declare function getSpaceDetails(spaceId: string): Promise<any>;
|
|
|
42
43
|
* Get or create a task search index with specified filters
|
|
43
44
|
* Caches promises to prevent race conditions on concurrent calls
|
|
44
45
|
*/
|
|
45
|
-
export declare function getTaskSearchIndex(space_ids?: string[], list_ids?: string[], assignees?: string[]): Promise<Fuse<any> | null>;
|
|
46
|
+
export declare function getTaskSearchIndex(space_ids?: string[], list_ids?: string[], assignees?: string[], include_closed?: boolean, archived?: boolean): Promise<Fuse<any> | null>;
|
|
46
47
|
/**
|
|
47
48
|
* Generate a ClickUp task URL from a task ID
|
|
48
49
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/shared/utils.ts"],"names":[],"mappings":"AACA,OAAO,IAAI,MAAM,SAAS,CAAC;AAI3B
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/shared/utils.ts"],"names":[],"mappings":"AACA,OAAO,IAAI,MAAM,SAAS,CAAC;AAI3B;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAG7C;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAiB3E;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQ/D;AAKD;;;GAGG;AACH,wBAAsB,cAAc,iBA6BnC;AAGD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAIpD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CA8C/D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,GAAG,GAAG,MAAM,CA+CpD;AAID;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CA0B7D;AAKD;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,CAAC,EAAE,MAAM,EAAE,EACpB,QAAQ,CAAC,EAAE,MAAM,EAAE,EACnB,SAAS,CAAC,EAAE,MAAM,EAAE,EACpB,cAAc,CAAC,EAAE,OAAO,EACxB,QAAQ,CAAC,EAAE,OAAO,GACjB,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAiC3B;AA2ED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAK1E;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,GAAG,MAAM,CA4ElG;AAKD;;;GAGG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CA+CrE;AAKD;;GAEG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,GAAG,EAAE,CAAC;IAAC,OAAO,EAAE,GAAG,EAAE,CAAC;IAAC,SAAS,EAAE,GAAG,EAAE,CAAA;CAAE,CAAC,CAgFlH;AAKD;;GAEG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CA+C3D;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAAC,CAAC,EAC5C,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC,EACpB,KAAK,EAAE,MAAM,EAAE,GACd,OAAO,CAAC,CAAC,EAAE,CAAC,CA8Dd"}
|
package/dist/shared/utils.js
CHANGED
|
@@ -28,11 +28,12 @@ const fuse_js_1 = __importDefault(require("fuse.js"));
|
|
|
28
28
|
const GLOBAL_REFRESH_INTERVAL = 60000; // 60 seconds - that is the rate limit time frame
|
|
29
29
|
/**
|
|
30
30
|
* Checks if a string looks like a valid ClickUp task ID
|
|
31
|
-
* Valid task IDs are 6
|
|
31
|
+
* Valid task IDs are at least 6 characters long and contain only alphanumeric characters.
|
|
32
|
+
* No upper bound: ClickUp now issues IDs of 10+ characters (e.g. "wdrv93ebwx").
|
|
32
33
|
*/
|
|
33
34
|
function isTaskId(str) {
|
|
34
|
-
// Task IDs are 6
|
|
35
|
-
return /^[a-z0-9]{6,
|
|
35
|
+
// Task IDs are 6+ characters long and contain only alphanumeric characters
|
|
36
|
+
return /^[a-z0-9]{6,}$/i.test(str);
|
|
36
37
|
}
|
|
37
38
|
/**
|
|
38
39
|
* Checks if a string looks like a ClickUp custom task ID (e.g. "SOI-4422", "PQP-123")
|
|
@@ -67,7 +68,7 @@ async function resolveTaskId(id) {
|
|
|
67
68
|
if (isCustomTaskId(id)) {
|
|
68
69
|
return resolveCustomTaskId(id);
|
|
69
70
|
}
|
|
70
|
-
throw new Error(`Invalid task ID format: "${id}". Expected an internal ID (6
|
|
71
|
+
throw new Error(`Invalid task ID format: "${id}". Expected an internal ID (6+ alphanumeric characters) or a custom ID (e.g. "SOI-4422").`);
|
|
71
72
|
}
|
|
72
73
|
// Cache for current user info to avoid repeated API calls and race conditions
|
|
73
74
|
let cachedUserPromise = null;
|
|
@@ -216,12 +217,14 @@ const taskIndices = new Map();
|
|
|
216
217
|
* Get or create a task search index with specified filters
|
|
217
218
|
* Caches promises to prevent race conditions on concurrent calls
|
|
218
219
|
*/
|
|
219
|
-
async function getTaskSearchIndex(space_ids, list_ids, assignees) {
|
|
220
|
+
async function getTaskSearchIndex(space_ids, list_ids, assignees, include_closed, archived) {
|
|
220
221
|
// Create cache key from sorted filter arrays
|
|
221
222
|
const key = JSON.stringify({
|
|
222
223
|
space_ids: space_ids?.sort(),
|
|
223
224
|
list_ids: list_ids?.sort(),
|
|
224
|
-
assignees: assignees?.sort()
|
|
225
|
+
assignees: assignees?.sort(),
|
|
226
|
+
include_closed,
|
|
227
|
+
archived
|
|
225
228
|
});
|
|
226
229
|
// Check for existing valid index promise
|
|
227
230
|
const cachedPromise = taskIndices.get(key);
|
|
@@ -231,7 +234,7 @@ async function getTaskSearchIndex(space_ids, list_ids, assignees) {
|
|
|
231
234
|
// Create the fetch promise
|
|
232
235
|
const fetchPromise = (async () => {
|
|
233
236
|
console.error(`Refreshing task index for filters: ${key}`);
|
|
234
|
-
const tasks = await fetchTasks(space_ids, list_ids, assignees);
|
|
237
|
+
const tasks = await fetchTasks(space_ids, list_ids, assignees, include_closed, archived);
|
|
235
238
|
const index = createFuseIndex(tasks);
|
|
236
239
|
console.error(`Task index created with ${tasks.length} tasks`);
|
|
237
240
|
return index;
|
|
@@ -247,8 +250,14 @@ async function getTaskSearchIndex(space_ids, list_ids, assignees) {
|
|
|
247
250
|
/**
|
|
248
251
|
* Fetch tasks using team endpoint with dynamic filters
|
|
249
252
|
*/
|
|
250
|
-
async function fetchTasks(space_ids, list_ids, assignees) {
|
|
253
|
+
async function fetchTasks(space_ids, list_ids, assignees, include_closed, archived) {
|
|
251
254
|
const queryParams = ['order_by=updated', 'subtasks=true'];
|
|
255
|
+
if (include_closed) {
|
|
256
|
+
queryParams.push('include_closed=true');
|
|
257
|
+
}
|
|
258
|
+
if (archived) {
|
|
259
|
+
queryParams.push('archived=true');
|
|
260
|
+
}
|
|
252
261
|
// Add filter parameters
|
|
253
262
|
if (space_ids?.length) {
|
|
254
263
|
space_ids.forEach(id => queryParams.push(`space_ids[]=${id}`));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search-tools.d.ts","sourceRoot":"","sources":["../../src/tools/search-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAC,MAAM,yCAAyC,CAAC;AAQlE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,
|
|
1
|
+
{"version":3,"file":"search-tools.d.ts","sourceRoot":"","sources":["../../src/tools/search-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAC,MAAM,yCAAyC,CAAC;AAQlE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,QA4NnE"}
|
|
@@ -46,12 +46,20 @@ function registerSearchTools(server, userData) {
|
|
|
46
46
|
.boolean()
|
|
47
47
|
.optional()
|
|
48
48
|
.describe(`Filter for tasks assigned to the current user (${userData.user.username} (${userData.user.id}))`),
|
|
49
|
+
include_closed: zod_1.z
|
|
50
|
+
.boolean()
|
|
51
|
+
.optional()
|
|
52
|
+
.describe("Include closed tasks in results (default: false)"),
|
|
53
|
+
archived: zod_1.z
|
|
54
|
+
.boolean()
|
|
55
|
+
.optional()
|
|
56
|
+
.describe("Include archived tasks in results (default: false). Note: only returns individually archived tasks, not tasks in archived spaces/folders/lists."),
|
|
49
57
|
}, {
|
|
50
58
|
readOnlyHint: true
|
|
51
|
-
}, async ({ terms, list_ids, space_ids, only_todo, status, assigned_to_me }) => {
|
|
59
|
+
}, async ({ terms, list_ids, space_ids, only_todo, status, assigned_to_me, include_closed, archived }) => {
|
|
52
60
|
// Get current user ID if filtering by assigned_to_me
|
|
53
61
|
const assignees = assigned_to_me ? [userData.user.id] : [];
|
|
54
|
-
const searchIndex = await (0, utils_1.getTaskSearchIndex)(space_ids, list_ids, assignees);
|
|
62
|
+
const searchIndex = await (0, utils_1.getTaskSearchIndex)(space_ids, list_ids, assignees, include_closed, archived);
|
|
55
63
|
if (!searchIndex) {
|
|
56
64
|
return {
|
|
57
65
|
content: [
|
package/dist/tools/task-tools.js
CHANGED
|
@@ -20,9 +20,9 @@ function registerTaskToolsRead(server, userData) {
|
|
|
20
20
|
.string()
|
|
21
21
|
.min(1)
|
|
22
22
|
.refine(val => (0, utils_1.isTaskId)(val) || (0, utils_1.isCustomTaskId)(val), {
|
|
23
|
-
message: "Must be an internal task ID (6
|
|
23
|
+
message: "Must be an internal task ID (6+ alphanumeric characters) or a custom task ID (e.g. SOI-4422)"
|
|
24
24
|
})
|
|
25
|
-
.describe(`The task ID: either an internal ID (6
|
|
25
|
+
.describe(`The task ID: either an internal ID (6+ alphanumeric characters like "869c4za0g") or a custom task ID (e.g. "SOI-4422"). Do not include prefixes like "#", "CU-" or URLs.`),
|
|
26
26
|
}, {
|
|
27
27
|
readOnlyHint: true
|
|
28
28
|
}, async ({ id }) => {
|
|
@@ -32,7 +32,7 @@ function registerTaskToolsWrite(server, userData) {
|
|
|
32
32
|
return descriptionBase.join("\n");
|
|
33
33
|
})(), {
|
|
34
34
|
task_id: zod_1.z.string().min(1).refine(val => (0, utils_1.isTaskId)(val) || (0, utils_1.isCustomTaskId)(val), {
|
|
35
|
-
message: "Must be an internal task ID (6
|
|
35
|
+
message: "Must be an internal task ID (6+ alphanumeric characters) or a custom task ID (e.g. SOI-4422)"
|
|
36
36
|
}).describe("The task ID to comment on: internal ID (e.g. \"869c4za0g\") or custom ID (e.g. \"SOI-4422\")"),
|
|
37
37
|
comment: zod_1.z.string().min(1).describe("The comment text to add to the task"),
|
|
38
38
|
}, {
|
|
@@ -108,7 +108,7 @@ function registerTaskToolsWrite(server, userData) {
|
|
|
108
108
|
return descriptionBase.join("\n");
|
|
109
109
|
})(), {
|
|
110
110
|
task_id: zod_1.z.string().min(1).refine(val => (0, utils_1.isTaskId)(val) || (0, utils_1.isCustomTaskId)(val), {
|
|
111
|
-
message: "Must be an internal task ID (6
|
|
111
|
+
message: "Must be an internal task ID (6+ alphanumeric characters) or a custom task ID (e.g. SOI-4422)"
|
|
112
112
|
}).describe("The task ID to update: internal ID (e.g. \"869c4za0g\") or custom ID (e.g. \"SOI-4422\")"),
|
|
113
113
|
name: taskNameSchema.optional(),
|
|
114
114
|
append_description: zod_1.z.string().optional().describe("Optional markdown content to APPEND to existing task description (preserves existing content for safety)"),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"time-tools.d.ts","sourceRoot":"","sources":["../../src/tools/time-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAyDpE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"time-tools.d.ts","sourceRoot":"","sources":["../../src/tools/time-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAyDpE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,QAqFtD;AAyLD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,QAgGvD"}
|
package/dist/tools/time-tools.js
CHANGED
|
@@ -54,7 +54,9 @@ function registerTimeToolsRead(server) {
|
|
|
54
54
|
// Workaround: SDK 1.27+ dual Zod v3/v4 type causes TS2589 on server.tool() generics
|
|
55
55
|
const tool = server.tool.bind(server);
|
|
56
56
|
tool("getTimeEntries", "Gets time entries for a specific task or all user's time entries. Returns last 30 days by default if no dates specified.", {
|
|
57
|
-
task_id: zod_1.z.string().min(
|
|
57
|
+
task_id: zod_1.z.string().min(1).refine(val => (0, utils_1.isTaskId)(val) || (0, utils_1.isCustomTaskId)(val), {
|
|
58
|
+
message: "Must be an internal task ID (6+ alphanumeric characters) or a custom task ID (e.g. SOI-4422)"
|
|
59
|
+
}).optional().describe("Optional task ID to filter entries: internal ID (e.g. \"869c4za0g\") or custom ID (e.g. \"SOI-4422\"). If not provided, returns all user's time entries."),
|
|
58
60
|
start_date: zod_1.z.string().optional().describe("Optional start date filter as ISO date string (e.g., '2024-10-06T00:00:00+02:00'). Defaults to 30 days ago."),
|
|
59
61
|
end_date: zod_1.z.string().optional().describe("Optional end date filter as ISO date string (e.g., '2024-10-06T23:59:59+02:00'). Defaults to current date."),
|
|
60
62
|
list_id: zod_1.z.string().optional().describe("Optional single list ID to filter time entries by a specific list"),
|
|
@@ -67,7 +69,7 @@ function registerTimeToolsRead(server) {
|
|
|
67
69
|
// Build query parameters
|
|
68
70
|
const params = new URLSearchParams();
|
|
69
71
|
if (task_id) {
|
|
70
|
-
params.append('task_id', task_id);
|
|
72
|
+
params.append('task_id', await (0, utils_1.resolveTaskId)(task_id));
|
|
71
73
|
}
|
|
72
74
|
if (start_date) {
|
|
73
75
|
params.append('start_date', isoToTimestamp(start_date).toString());
|
|
@@ -271,7 +273,9 @@ function registerTimeToolsWrite(server) {
|
|
|
271
273
|
"IMPORTANT: Before booking time, check the task's status - booking time on tasks in 'backlog', 'closed', or similar inactive states usually doesn't make sense.",
|
|
272
274
|
"Suggest moving the task to an active status like 'in progress' first."
|
|
273
275
|
].join("\n"), {
|
|
274
|
-
task_id: zod_1.z.string().min(
|
|
276
|
+
task_id: zod_1.z.string().min(1).refine(val => (0, utils_1.isTaskId)(val) || (0, utils_1.isCustomTaskId)(val), {
|
|
277
|
+
message: "Must be an internal task ID (6+ alphanumeric characters) or a custom task ID (e.g. SOI-4422)"
|
|
278
|
+
}).describe("The task ID to book time against: internal ID (e.g. \"869c4za0g\") or custom ID (e.g. \"SOI-4422\")"),
|
|
275
279
|
hours: zod_1.z.number().min(0.01).max(24).describe("Hours to book (decimal format, e.g., 0.25 = 15min, 1.5 = 1h 30min)"),
|
|
276
280
|
description: zod_1.z.string().optional().describe("Optional description for the time entry"),
|
|
277
281
|
start_time: zod_1.z.string().optional().describe("Optional start time as ISO date string (e.g., '2024-10-06T09:00:00+02:00', defaults to current time)")
|
|
@@ -285,8 +289,10 @@ function registerTimeToolsWrite(server) {
|
|
|
285
289
|
const durationMs = Math.round(hours * 60 * 60 * 1000);
|
|
286
290
|
// Convert ISO date to timestamp if provided, otherwise use current time
|
|
287
291
|
const startTimeMs = start_time ? isoToTimestamp(start_time) : Date.now();
|
|
292
|
+
// Resolve custom task IDs (e.g. "SOI-4422") to internal IDs before booking
|
|
293
|
+
const resolved_task_id = await (0, utils_1.resolveTaskId)(task_id);
|
|
288
294
|
const requestBody = {
|
|
289
|
-
tid:
|
|
295
|
+
tid: resolved_task_id,
|
|
290
296
|
start: startTimeMs,
|
|
291
297
|
duration: durationMs,
|
|
292
298
|
...(description && { description })
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sebastienheyd/clickup-mcp",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.3",
|
|
4
4
|
"description": "Search, create, and retrieve tasks, add comments, and track time through natural language commands.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"undici": "^7.16.0"
|
|
59
59
|
},
|
|
60
60
|
"engines": {
|
|
61
|
-
"node": ">=
|
|
61
|
+
"node": ">=20.0.0"
|
|
62
62
|
},
|
|
63
63
|
"repository": {
|
|
64
64
|
"type": "git",
|