@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.
@@ -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-9 characters long and contain only alphanumeric characters
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;;;GAGG;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,GACnB,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CA+B3B;AAkED;;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"}
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"}
@@ -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-9 characters long and contain only alphanumeric characters
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-9 characters long and contain only alphanumeric characters
35
- return /^[a-z0-9]{6,9}$/i.test(str);
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-9 alphanumeric characters) or a custom ID (e.g. "SOI-4422").`);
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,QAoNnE"}
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: [
@@ -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-9 alphanumeric characters) or a custom task ID (e.g. SOI-4422)"
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-9 alphanumeric characters like "869c4za0g") or a custom task ID (e.g. "SOI-4422"). Do not include prefixes like "#", "CU-" or URLs.`),
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-9 alphanumeric characters) or a custom task ID (e.g. SOI-4422)"
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-9 alphanumeric characters) or a custom task ID (e.g. SOI-4422)"
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,QAmFtD;AAyLD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,QA2FvD"}
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"}
@@ -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(6).max(9).optional().describe("Optional 6-9 character task ID to filter entries. If not provided, returns all user's time entries."),
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(6).max(9).describe("The 6-9 character task ID to book time against"),
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: task_id,
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.1",
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": ">=16.0.0"
61
+ "node": ">=20.0.0"
62
62
  },
63
63
  "repository": {
64
64
  "type": "git",