@hauptsache.net/clickup-mcp 1.4.3 → 1.5.1

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,9 +2,6 @@
2
2
 
3
3
  Model Context Protocol (MCP) server enabling AI assistants to interact with ClickUp workspaces. Get complete task context with comments and images, search across projects, create and update tasks, collaborate through comments, and track time - all through natural language.
4
4
 
5
- > **⚠️ Experimental Software Notice**
6
- > This MCP is experimental and under active development. While implemented with great care to prevent data loss through append-only description updates and other safety measures, it cannot be guaranteed to be completely safe. Please use with caution, especially in production environments. Always test in a non-critical workspace first. 🧪
7
-
8
5
  ## What You Can Do
9
6
 
10
7
  Turn natural language into powerful ClickUp actions:
@@ -124,7 +121,7 @@ Replace `your_api_key` and `your_team_id` with your actual ClickUp credentials.
124
121
 
125
122
  **Claude Code (CLI):**
126
123
  ```bash
127
- claude mcp add clickup \
124
+ claude mcp add --scope user clickup \
128
125
  --env CLICKUP_API_KEY=YOUR_KEY \
129
126
  --env CLICKUP_TEAM_ID=YOUR_ID \
130
127
  --env CLICKUP_MCP_MODE=read-minimal \
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAqIpE,QAAA,MAAM,aAAa,oBAAqB,CAAC;AAIzC,OAAO,EAAE,aAAa,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAoIpE,QAAA,MAAM,aAAa,oBAAqB,CAAC;AAIzC,OAAO,EAAE,aAAa,EAAE,CAAC"}
package/dist/index.js CHANGED
@@ -53,8 +53,7 @@ async function initializeServer() {
53
53
  title: lang === 'de' ? "Meine TODOs" : "My TODOs",
54
54
  description: lang === 'de'
55
55
  ? "Meine aktuellen TODO-Aufgaben aus ClickUp abrufen und nach Priorität kategorisiert analysieren"
56
- : "Get and analyze my current TODO tasks from ClickUp, categorized by priority",
57
- argsSchema: {}
56
+ : "Get and analyze my current TODO tasks from ClickUp, categorized by priority"
58
57
  }, () => {
59
58
  const messages = [{
60
59
  role: "user",
@@ -34,8 +34,8 @@ function registerSpaceResources(server) {
34
34
  return {
35
35
  resources: activeSpaces.map((space) => ({
36
36
  uri: `clickup://space/${space.id}`,
37
- name: space.name,
38
- title: space.name,
37
+ name: `${space.name} ClickUp Space.txt`,
38
+ title: `${space.name} ClickUp Space`,
39
39
  mimeType: "text/plain"
40
40
  }))
41
41
  };
@@ -61,11 +61,6 @@ export declare function getSpaceContent(spaceId: string): Promise<{
61
61
  * Gets all team members from ClickUp API with caching
62
62
  */
63
63
  export declare function getAllTeamMembers(): Promise<string[]>;
64
- /**
65
- * Get or create a document search index with space name resolution
66
- * Caches promises to prevent race conditions on concurrent calls
67
- */
68
- export declare function getDocumentSearchIndex(space_ids?: string[]): Promise<Fuse<any> | null>;
69
64
  /**
70
65
  * Performs multi-term search with aggressive boosting for items matching multiple terms
71
66
  * @param searchIndex Fuse search index to search within
@@ -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;AAKD;;;GAGG;AACH,wBAAsB,cAAc,iBA6BnC;AAGD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAIpD;;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;AAKD;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,SAAS,CAAC,EAAE,MAAM,EAAE,GACnB,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CA6B3B;AAuID;;;;;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;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAG7C;AAKD;;;GAGG;AACH,wBAAsB,cAAc,iBA6BnC;AAGD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAIpD;;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"}
@@ -17,7 +17,6 @@ exports.formatSpaceTree = formatSpaceTree;
17
17
  exports.getSpaceSearchIndex = getSpaceSearchIndex;
18
18
  exports.getSpaceContent = getSpaceContent;
19
19
  exports.getAllTeamMembers = getAllTeamMembers;
20
- exports.getDocumentSearchIndex = getDocumentSearchIndex;
21
20
  exports.performMultiTermSearch = performMultiTermSearch;
22
21
  const config_1 = require("./config");
23
22
  const fuse_js_1 = __importDefault(require("fuse.js"));
@@ -449,156 +448,6 @@ async function getAllTeamMembers() {
449
448
  }, GLOBAL_REFRESH_INTERVAL);
450
449
  return fetchPromise;
451
450
  }
452
- // Document search index management - cache promises to prevent race conditions
453
- const documentIndices = new Map();
454
- /**
455
- * Get or create a document search index with space name resolution
456
- * Caches promises to prevent race conditions on concurrent calls
457
- */
458
- async function getDocumentSearchIndex(space_ids) {
459
- // Create cache key from sorted filter arrays
460
- const key = JSON.stringify({
461
- space_ids: space_ids?.sort()
462
- });
463
- // Check for existing valid index promise
464
- const cachedPromise = documentIndices.get(key);
465
- if (cachedPromise) {
466
- return cachedPromise;
467
- }
468
- // Create the fetch promise
469
- const fetchPromise = (async () => {
470
- console.error(`Refreshing document index for filters: ${key}`);
471
- const documents = await fetchDocuments(space_ids);
472
- const index = createDocumentFuseIndex(documents);
473
- console.error(`Document index created with ${documents.length} documents`);
474
- return index;
475
- })();
476
- // Store promise with auto-cleanup
477
- documentIndices.set(key, fetchPromise);
478
- setTimeout(() => {
479
- documentIndices.delete(key);
480
- console.error(`Auto-cleaned document index for filters: ${key}`);
481
- }, GLOBAL_REFRESH_INTERVAL);
482
- return fetchPromise;
483
- }
484
- /**
485
- * Fetch documents and resolve space names
486
- */
487
- async function fetchDocuments(space_ids) {
488
- try {
489
- // Fetch spaces first
490
- const spacesResponse = await fetch(`https://api.clickup.com/api/v2/team/${config_1.CONFIG.teamId}/space`, {
491
- headers: { Authorization: config_1.CONFIG.apiKey }
492
- });
493
- if (!spacesResponse.ok) {
494
- console.error('Error fetching spaces:', spacesResponse.status);
495
- return [];
496
- }
497
- const spacesData = await spacesResponse.json();
498
- // Fetch documents with pagination
499
- const allDocuments = [];
500
- let nextCursor = null;
501
- let pageCount = 0;
502
- const maxPages = 10; // Limit to 10 pages to avoid excessive API calls
503
- do {
504
- const url = new URL(`https://api.clickup.com/api/v3/workspaces/${config_1.CONFIG.teamId}/docs`);
505
- url.searchParams.set('limit', '100');
506
- if (nextCursor) {
507
- url.searchParams.set('next_cursor', nextCursor);
508
- }
509
- const documentsResponse = await fetch(url.toString(), {
510
- headers: { Authorization: config_1.CONFIG.apiKey }
511
- });
512
- if (!documentsResponse.ok) {
513
- console.error('Error fetching documents:', documentsResponse.status);
514
- break;
515
- }
516
- const documentsData = await documentsResponse.json();
517
- const pageDocs = documentsData.docs || [];
518
- allDocuments.push(...pageDocs);
519
- nextCursor = documentsData.next_cursor || null;
520
- pageCount++;
521
- console.error(`Fetched page ${pageCount} with ${pageDocs.length} documents (total: ${allDocuments.length})`);
522
- } while (nextCursor && pageCount < maxPages);
523
- const documents = allDocuments;
524
- const spaces = spacesData.spaces || [];
525
- // Create space lookup map
526
- const spaceMap = new Map(spaces.map((space) => [space.id, space]));
527
- // Enhance documents with parent information
528
- const enhancedDocuments = documents.map((doc) => {
529
- // Parent types: 4=Space, 5=Folder, 6=List, 7=Workspace
530
- if (doc.parent?.type === 4) {
531
- // Space parent - we can resolve the name
532
- const space = spaceMap.get(doc.parent.id);
533
- return {
534
- ...doc,
535
- space_name: space?.name || 'Unknown',
536
- space_id: doc.parent.id,
537
- parent_info: space?.name ? `Space: ${space.name} (${doc.parent.id})` : `Space: ${doc.parent.id}`
538
- };
539
- }
540
- else if (doc.parent?.type === 6) {
541
- // List parent - just show ID
542
- return {
543
- ...doc,
544
- space_name: 'N/A',
545
- space_id: null,
546
- parent_info: `List: ${doc.parent.id}`
547
- };
548
- }
549
- else if (doc.parent?.type === 5) {
550
- // Folder parent - just show ID
551
- return {
552
- ...doc,
553
- space_name: 'N/A',
554
- space_id: null,
555
- parent_info: `Folder: ${doc.parent.id}`
556
- };
557
- }
558
- else if (doc.parent?.type === 7) {
559
- // Workspace parent
560
- return {
561
- ...doc,
562
- space_name: 'N/A',
563
- space_id: null,
564
- parent_info: `Workspace`
565
- };
566
- }
567
- // Unknown parent type
568
- return {
569
- ...doc,
570
- space_name: 'Unknown',
571
- space_id: doc.parent?.id,
572
- parent_info: doc.parent ? `Unknown (type ${doc.parent.type})` : 'Unknown'
573
- };
574
- });
575
- // Filter by space_ids if provided
576
- if (space_ids?.length) {
577
- return enhancedDocuments.filter((doc) => space_ids.includes(doc.space_id));
578
- }
579
- return enhancedDocuments;
580
- }
581
- catch (error) {
582
- console.error('Error fetching documents:', error);
583
- return [];
584
- }
585
- }
586
- /**
587
- * Create a Fuse index from documents array
588
- */
589
- function createDocumentFuseIndex(documents) {
590
- return new fuse_js_1.default(documents, {
591
- keys: [
592
- { name: 'name', weight: 0.8 },
593
- { name: 'space_name', weight: 0.6 },
594
- { name: 'id', weight: 0.4 }
595
- ],
596
- findAllMatches: true,
597
- includeScore: true,
598
- minMatchCharLength: 2,
599
- threshold: 0.4,
600
- });
601
- }
602
451
  /**
603
452
  * Performs multi-term search with aggressive boosting for items matching multiple terms
604
453
  * @param searchIndex Fuse search index to search within
@@ -1 +1 @@
1
- {"version":3,"file":"doc-tools.d.ts","sourceRoot":"","sources":["../../src/tools/doc-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAoDpE,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,QAuR1D;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,QAiP3D"}
1
+ {"version":3,"file":"doc-tools.d.ts","sourceRoot":"","sources":["../../src/tools/doc-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAoDpE,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,QA0J1D;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,QAoS3D"}
@@ -47,6 +47,7 @@ function displayPageHierarchy(pageGroup, currentPageId, depth = 0) {
47
47
  function registerDocumentToolsRead(server) {
48
48
  server.tool("readDocument", [
49
49
  "Get a ClickUp document with page structure and content.",
50
+ "Documents can be discovered via searchSpaces (which includes documents in space tree) or by direct URL from the user or within tasks.",
50
51
  "Always use the document URL when referencing documents in conversations or sharing with others.",
51
52
  "The response provides complete document metadata, page structure, and requested page content.",
52
53
  `Document URLs look like this: ${(0, utils_1.generateDocumentUrl)('doc_id', 'page_id')}`,
@@ -98,8 +99,8 @@ function registerDocumentToolsRead(server) {
98
99
  ⚠️ This document exists but has no pages yet.
99
100
 
100
101
  **Next steps:**
101
- - Use \`writeDocument\` with parent_type="doc" and parent_id="${doc_id}" to add the first page
102
- - Example: writeDocument(parent_type="doc", parent_id="${doc_id}", page_name="Introduction", content="Your content here")`
102
+ - Use \`createDocumentOrPage\` with doc_id="${doc_id}" to add the first page
103
+ - Example: createDocumentOrPage(doc_id="${doc_id}", name="Introduction", content="Your content here")`
103
104
  }],
104
105
  };
105
106
  }
@@ -158,8 +159,8 @@ function registerDocumentToolsRead(server) {
158
159
  result.push('*This page is empty.*');
159
160
  result.push('');
160
161
  result.push('**💡 To add content to this page:**');
161
- result.push(`Use \`writeDocument\` with page_id="${targetPage.id}" and your content.`);
162
- result.push(`Example: writeDocument(page_id="${targetPage.id}", content="Your content here")`);
162
+ result.push(`Use \`updateDocumentPage\` with doc_id="${doc_id}", page_id="${targetPage.id}" and your content.`);
163
+ result.push(`Example: updateDocumentPage(doc_id="${doc_id}", page_id="${targetPage.id}", content="Your content here")`);
163
164
  return {
164
165
  content: [
165
166
  { type: "text", text: result.join('\n') },
@@ -179,203 +180,169 @@ function registerDocumentToolsRead(server) {
179
180
  };
180
181
  }
181
182
  });
182
- // Dynamically construct the searchDocuments description
183
- const searchDocumentsDescriptionBase = [
184
- "Search documents by name and space with fuzzy matching and support for multiple search terms (OR logic).",
185
- "This can be a great knowledge based for general project information. Consider searching for documents and for tasks at the same time.",
186
- "Can filter by specific space_ids. If no search terms provided, returns most recently updated documents."
187
- ];
188
- if (config_1.CONFIG.primaryLanguageHint && config_1.CONFIG.primaryLanguageHint.toLowerCase() !== 'en') {
189
- searchDocumentsDescriptionBase.push(`For optimal results, as your ClickUp documents may be primarily in '${config_1.CONFIG.primaryLanguageHint}', consider providing search terms in English and '${config_1.CONFIG.primaryLanguageHint}'.`);
190
- }
191
- searchDocumentsDescriptionBase.push("Always reference documents by their URLs when discussing search results or suggesting actions.");
192
- searchDocumentsDescriptionBase.push("You'll get document overview with space context - use readDocument to get full content.");
193
- server.tool("searchDocuments", searchDocumentsDescriptionBase.join("\n"), {
194
- terms: zod_1.z
195
- .array(zod_1.z.string())
183
+ }
184
+ function registerDocumentToolsWrite(server) {
185
+ server.tool("updateDocumentPage", [
186
+ "Updates an existing document page's content and/or name.",
187
+ "Use this when you have both doc_id and page_id from readDocument.",
188
+ "Content is in markdown format and can be replaced or appended.",
189
+ "Always reference documents by their URLs when sharing with users."
190
+ ].join("\n"), {
191
+ doc_id: zod_1.z
192
+ .string()
193
+ .min(1)
194
+ .describe("The document ID containing the page (from readDocument)"),
195
+ page_id: zod_1.z
196
+ .string()
197
+ .min(1)
198
+ .describe("The page ID to update (from readDocument)"),
199
+ name: zod_1.z
200
+ .string()
196
201
  .optional()
197
- .describe("Array of search terms to match against document names and spaces. If not provided, returns most recent documents."),
198
- space_ids: zod_1.z
199
- .array(zod_1.z.string())
202
+ .describe("Optional: new name for the page"),
203
+ content: zod_1.z
204
+ .string()
205
+ .optional()
206
+ .describe("Optional: page content in markdown format"),
207
+ append: zod_1.z
208
+ .boolean()
200
209
  .optional()
201
- .describe("Filter documents to specific space IDs")
210
+ .describe("Whether to append content to existing page content (default: false - replaces content)")
202
211
  }, {
203
- readOnlyHint: true
204
- }, async ({ terms, space_ids }) => {
212
+ readOnlyHint: false,
213
+ destructiveHint: true,
214
+ idempotentHint: false,
215
+ }, async ({ doc_id, page_id, name, content, append = false }) => {
205
216
  try {
206
- // Get the document search index
207
- const searchIndex = await (0, utils_1.getDocumentSearchIndex)(space_ids);
208
- if (!searchIndex) {
209
- return {
210
- content: [
211
- {
212
- type: "text",
213
- text: "Unable to create document search index.",
214
- },
215
- ],
216
- };
217
- }
218
- let results;
219
- if (terms && terms.length > 0) {
220
- // Perform multi-term search
221
- results = await (0, utils_1.performMultiTermSearch)(searchIndex, terms);
217
+ const requestBody = {};
218
+ if (name) {
219
+ requestBody.name = name;
222
220
  }
223
- else {
224
- // Return all documents sorted by creation date (most recent first)
225
- const allResults = searchIndex._docs || [];
226
- results = allResults
227
- .sort((a, b) => new Date(b.date_created || 0).getTime() - new Date(a.date_created || 0).getTime());
221
+ if (content !== undefined) {
222
+ requestBody.content = content;
223
+ requestBody.content_edit_mode = append ? 'append' : 'replace';
228
224
  }
229
- // Limit results to prevent overwhelming the LLM
230
- const limitedResults = results.slice(0, 50);
231
- if (limitedResults.length === 0) {
232
- const searchTermsText = terms && terms.length > 0 ? ` for terms: ${terms.join(", ")}` : "";
233
- const spaceFilterText = space_ids && space_ids.length > 0 ? ` in spaces: ${space_ids.join(", ")}` : "";
225
+ if (Object.keys(requestBody).length === 0) {
234
226
  return {
235
- content: [
236
- {
227
+ content: [{
237
228
  type: "text",
238
- text: [
239
- `No documents found${searchTermsText}${spaceFilterText}.`,
240
- `The content of documents is not searched, so ask the user for more details if needed.`,
241
- ].join("\n"),
242
- },
243
- ],
229
+ text: "Error: At least one of 'name' or 'content' must be provided for update."
230
+ }],
244
231
  };
245
232
  }
246
- // Format results
247
- const responseLines = [
248
- `Found ${limitedResults.length} document${limitedResults.length === 1 ? "" : "s"}:`
249
- ];
250
- limitedResults.forEach((doc, index) => {
251
- const docUrl = (0, utils_1.generateDocumentUrl)(doc.id);
252
- const createdDate = doc.date_created ? new Date(+doc.date_created).toLocaleDateString() : 'Unknown';
253
- const meta = [`doc_id ${doc.id}`];
254
- // Show parent information
255
- if (doc.parent_info) {
256
- meta.push(doc.parent_info);
257
- }
258
- meta.push(`Created: ${createdDate}`);
259
- responseLines.push(`- ${doc.name} (${meta.join(', ')}) ${docUrl}`);
233
+ const response = await fetch(`https://api.clickup.com/api/v3/workspaces/${config_1.CONFIG.teamId}/docs/${doc_id}/pages/${page_id}`, {
234
+ method: 'PUT',
235
+ headers: {
236
+ Authorization: config_1.CONFIG.apiKey,
237
+ 'Content-Type': 'application/json',
238
+ },
239
+ body: JSON.stringify(requestBody),
260
240
  });
261
- responseLines.push("", "Use `readDocument` with a document ID to view full content and page structure.");
241
+ if (!response.ok) {
242
+ const errorText = await response.text();
243
+ throw new Error(`Error updating page: ${response.status} ${response.statusText}. ${errorText}`);
244
+ }
245
+ // Handle potentially empty response
246
+ const responseText = await response.text();
247
+ let updatedPage = {};
248
+ if (responseText && responseText.trim()) {
249
+ try {
250
+ const data = JSON.parse(responseText);
251
+ updatedPage = data.page || data;
252
+ }
253
+ catch (e) {
254
+ // If JSON parsing fails, continue with empty updatedPage object
255
+ console.error('Warning: Could not parse response JSON, but update was successful');
256
+ }
257
+ }
258
+ const pageName = updatedPage.name || name || "page";
262
259
  return {
263
- content: [
264
- {
260
+ content: [{
265
261
  type: "text",
266
- text: responseLines.join("\n"),
267
- },
268
- ],
262
+ text: `✅ Successfully updated page "${pageName}" (page_id: ${page_id})\n\nPage URL: ${(0, utils_1.generateDocumentUrl)(doc_id, page_id)}`
263
+ }],
269
264
  };
270
265
  }
271
266
  catch (error) {
272
- console.error('Error searching documents:', error);
267
+ console.error('Error updating document page:', error);
273
268
  return {
274
269
  content: [
275
270
  {
276
271
  type: "text",
277
- text: `Error searching documents: ${error instanceof Error ? error.message : 'Unknown error'}`,
272
+ text: `Error updating page: ${error instanceof Error ? error.message : 'Unknown error'}`,
278
273
  },
279
274
  ],
280
275
  };
281
276
  }
282
277
  });
283
- }
284
- function registerDocumentToolsWrite(server) {
285
- server.tool("writeDocument", [
286
- "Universal tool for document and page operations with smart document creation.",
287
- "Create new documents by using parent_type 'space' or 'list'.",
288
- "Create pages in existing documents by using parent_type 'doc'.",
289
- "Create sub-pages by using parent_type 'page'.",
290
- "Update existing pages by providing page_id.",
291
- "Always reference documents by their URLs when creating or updating."
278
+ server.tool("createDocumentOrPage", [
279
+ "Creates a new document with its first page, OR adds a page to an existing document.",
280
+ "To create a NEW document: provide space_id OR list_id + name + content",
281
+ "To add a page to EXISTING document: provide doc_id + name + content",
282
+ "To create a sub-page: provide doc_id + parent_page_id + name + content",
283
+ "Content is in markdown format and supports ClickUp's markdown features.",
284
+ "Always reference documents by their URLs when sharing with users."
292
285
  ].join("\n"), {
293
- page_id: zod_1.z
286
+ space_id: zod_1.z
294
287
  .string()
295
288
  .optional()
296
- .describe("Optional page ID to update (if provided, updates existing page; if not, creates new page/document)"),
297
- parent_type: zod_1.z
298
- .enum(["space", "list", "doc", "page"])
289
+ .describe("Create NEW document in this space (mutually exclusive with list_id and doc_id)"),
290
+ list_id: zod_1.z
291
+ .string()
299
292
  .optional()
300
- .describe("Type of parent when creating new content (required if page_id not provided)"),
301
- parent_id: zod_1.z
293
+ .describe("Create NEW document in this list (mutually exclusive with space_id and doc_id)"),
294
+ doc_id: zod_1.z
302
295
  .string()
303
296
  .optional()
304
- .describe("ID of parent (space, list, document, or page) when creating new content (required if page_id not provided)"),
305
- page_name: zod_1.z
297
+ .describe("Add page to EXISTING document with this ID (mutually exclusive with space_id and list_id)"),
298
+ parent_page_id: zod_1.z
306
299
  .string()
307
300
  .optional()
308
- .describe("Name for the page (required for new pages, optional for updates to rename)"),
301
+ .describe("Optional: when provided with doc_id, creates a sub-page under this parent page"),
302
+ name: zod_1.z
303
+ .string()
304
+ .min(1)
305
+ .describe("Name for the document/page being created"),
309
306
  content: zod_1.z
310
307
  .string()
311
308
  .optional()
312
- .describe("Page content in markdown format (optional for updates)"),
313
- append: zod_1.z
314
- .boolean()
315
- .optional()
316
- .describe("Whether to append content to existing page content (default: false - replaces content)")
309
+ .describe("Optional: page content in markdown format")
317
310
  }, {
318
- readOnlyHint: false
319
- }, async ({ page_id, parent_type, parent_id, page_name, content, append = false }) => {
311
+ readOnlyHint: false,
312
+ destructiveHint: false,
313
+ idempotentHint: false,
314
+ openWorldHint: true
315
+ }, async ({ space_id, list_id, doc_id, parent_page_id, name, content }) => {
320
316
  try {
321
- // Validate input parameters
322
- if (!page_id && (!parent_type || !parent_id)) {
317
+ // Validate mutually exclusive parameters
318
+ const locationParams = [space_id, list_id, doc_id].filter(Boolean).length;
319
+ if (locationParams !== 1) {
323
320
  return {
324
321
  content: [{
325
322
  type: "text",
326
- text: "Error: Either page_id (for updates) or both parent_type and parent_id (for creation) must be provided."
323
+ text: "Error: Provide exactly ONE of: space_id (new doc in space), list_id (new doc in list), or doc_id (page in existing doc)."
327
324
  }],
328
325
  };
329
326
  }
330
- if (!page_id && !page_name) {
327
+ if (parent_page_id && !doc_id) {
331
328
  return {
332
329
  content: [{
333
330
  type: "text",
334
- text: "Error: page_name is required when creating new pages."
331
+ text: "Error: parent_page_id requires doc_id to be provided."
335
332
  }],
336
333
  };
337
334
  }
338
- // Case 1: Update existing page
339
- if (page_id) {
340
- const requestBody = {};
341
- if (page_name) {
342
- requestBody.name = page_name;
343
- }
344
- if (content !== undefined) {
345
- requestBody.content = content;
346
- // Use native append mode from ClickUp API
347
- requestBody.content_edit_mode = append ? 'append' : 'replace';
348
- }
349
- const response = await fetch(`https://api.clickup.com/api/v3/docs/pages/${page_id}`, {
350
- method: 'PUT',
351
- headers: {
352
- Authorization: config_1.CONFIG.apiKey,
353
- 'Content-Type': 'application/json',
354
- },
355
- body: JSON.stringify(requestBody),
356
- });
357
- if (!response.ok) {
358
- throw new Error(`Error updating page: ${response.status} ${response.statusText}`);
359
- }
360
- const data = await response.json();
361
- const updatedPage = data.page;
362
- return {
363
- content: [{
364
- type: "text",
365
- text: `✅ Successfully updated page "${updatedPage.name}" (${page_id})\n\nPage URL: ${(0, utils_1.generateDocumentUrl)(updatedPage.doc_id, page_id)}`
366
- }],
367
- };
368
- }
369
- // Case 2: Create new content
370
- let docId;
371
- if (parent_type === "space" || parent_type === "list") {
372
- // Create new document first, then create first page manually
335
+ // Case 1: Create new document in space or list
336
+ if (space_id || list_id) {
337
+ const parentId = space_id || list_id;
338
+ const parentType = space_id ? 4 : 6; // 4=Space, 6=List
339
+ // Create new document
373
340
  const docRequestBody = {
374
- name: page_name, // Document name matches first page name
375
- create_page: false, // Don't auto-create page, we'll create it manually for better control
341
+ name: name,
342
+ create_page: false,
376
343
  parent: {
377
- id: parent_id,
378
- type: parent_type === "space" ? 4 : parent_type === "list" ? 6 : 7 // 4=Space, 6=List, 7=Workspace
344
+ id: parentId,
345
+ type: parentType
379
346
  }
380
347
  };
381
348
  const docResponse = await fetch(`https://api.clickup.com/api/v3/workspaces/${config_1.CONFIG.teamId}/docs`, {
@@ -387,16 +354,17 @@ function registerDocumentToolsWrite(server) {
387
354
  body: JSON.stringify(docRequestBody),
388
355
  });
389
356
  if (!docResponse.ok) {
390
- throw new Error(`Error creating document: ${docResponse.status} ${docResponse.statusText}`);
357
+ const errorText = await docResponse.text();
358
+ throw new Error(`Error creating document: ${docResponse.status} ${docResponse.statusText}. ${errorText}`);
391
359
  }
392
360
  const docData = await docResponse.json();
393
- docId = docData.id;
394
- // Create the first page manually for better control
361
+ const newDocId = docData.id;
362
+ // Create the first page
395
363
  const pageRequestBody = {
396
- name: page_name,
364
+ name: name,
397
365
  content: content || '',
398
366
  };
399
- const pageResponse = await fetch(`https://api.clickup.com/api/v3/workspaces/${config_1.CONFIG.teamId}/docs/${docId}/pages`, {
367
+ const pageResponse = await fetch(`https://api.clickup.com/api/v3/workspaces/${config_1.CONFIG.teamId}/docs/${newDocId}/pages`, {
400
368
  method: 'POST',
401
369
  headers: {
402
370
  Authorization: config_1.CONFIG.apiKey,
@@ -405,54 +373,28 @@ function registerDocumentToolsWrite(server) {
405
373
  body: JSON.stringify(pageRequestBody),
406
374
  });
407
375
  if (!pageResponse.ok) {
408
- throw new Error(`Error creating first page: ${pageResponse.status} ${pageResponse.statusText}`);
376
+ const errorText = await pageResponse.text();
377
+ throw new Error(`Error creating first page: ${pageResponse.status} ${pageResponse.statusText}. ${errorText}`);
409
378
  }
410
379
  const pageData = await pageResponse.json();
411
380
  const firstPage = pageData.page || pageData;
412
381
  return {
413
382
  content: [{
414
383
  type: "text",
415
- text: `✅ Successfully created new document "${page_name}" with first page\n\nDocument URL: ${(0, utils_1.generateDocumentUrl)(docId)}\nFirst Page URL: ${(0, utils_1.generateDocumentUrl)(docId, firstPage.id)}`
384
+ text: `✅ Successfully created new document "${name}" (doc_id: ${newDocId})\n\nDocument URL: ${(0, utils_1.generateDocumentUrl)(newDocId)}\nFirst Page URL: ${(0, utils_1.generateDocumentUrl)(newDocId, firstPage.id)}`
416
385
  }],
417
386
  };
418
387
  }
419
- else if (parent_type === "doc") {
420
- // Create new page in existing document
421
- docId = parent_id;
388
+ // Case 2: Add page to existing document or create sub-page
389
+ if (doc_id) {
422
390
  const pageRequestBody = {
423
- name: page_name,
391
+ name: name,
424
392
  content: content || '',
425
393
  };
426
- const response = await fetch(`https://api.clickup.com/api/v3/workspaces/${config_1.CONFIG.teamId}/docs/${docId}/pages`, {
427
- method: 'POST',
428
- headers: {
429
- Authorization: config_1.CONFIG.apiKey,
430
- 'Content-Type': 'application/json',
431
- },
432
- body: JSON.stringify(pageRequestBody),
433
- });
434
- if (!response.ok) {
435
- throw new Error(`Error creating page: ${response.status} ${response.statusText}`);
394
+ if (parent_page_id) {
395
+ pageRequestBody.parent_page_id = parent_page_id;
436
396
  }
437
- const data = await response.json();
438
- const newPage = data.page || data; // Handle both nested and flat response formats
439
- return {
440
- content: [{
441
- type: "text",
442
- text: `✅ Successfully created page "${newPage.name}" (${newPage.id}) in document\n\nPage URL: ${(0, utils_1.generateDocumentUrl)(docId, newPage.id)}`
443
- }],
444
- };
445
- }
446
- else if (parent_type === "page") {
447
- // Create sub-page under existing page
448
- const pageRequestBody = {
449
- name: page_name,
450
- content: content || '',
451
- parent_page_id: parent_id
452
- };
453
- // For sub-pages, we need to get the doc_id from the parent page first
454
- // This is a limitation we'll need to handle - for now, use a generic endpoint
455
- const response = await fetch(`https://api.clickup.com/api/v3/docs/pages`, {
397
+ const response = await fetch(`https://api.clickup.com/api/v3/workspaces/${config_1.CONFIG.teamId}/docs/${doc_id}/pages`, {
456
398
  method: 'POST',
457
399
  headers: {
458
400
  Authorization: config_1.CONFIG.apiKey,
@@ -461,28 +403,31 @@ function registerDocumentToolsWrite(server) {
461
403
  body: JSON.stringify(pageRequestBody),
462
404
  });
463
405
  if (!response.ok) {
464
- throw new Error(`Error creating sub-page: ${response.status} ${response.statusText}`);
406
+ const errorText = await response.text();
407
+ throw new Error(`Error creating page: ${response.status} ${response.statusText}. ${errorText}`);
465
408
  }
466
409
  const data = await response.json();
467
- const newPage = data.page || data; // Handle both nested and flat response formats
410
+ const newPage = data.page || data;
411
+ const pageType = parent_page_id ? "sub-page" : "page";
412
+ const parentInfo = parent_page_id ? ` under parent page ${parent_page_id}` : "";
468
413
  return {
469
414
  content: [{
470
415
  type: "text",
471
- text: `✅ Successfully created sub-page "${newPage.name}" (${newPage.id}) under parent page ${parent_id}\n\nPage URL: ${(0, utils_1.generateDocumentUrl)(newPage.doc_id, newPage.id)}`
416
+ text: `✅ Successfully created ${pageType} "${newPage.name}" (page_id: ${newPage.id})${parentInfo}\n\nPage URL: ${(0, utils_1.generateDocumentUrl)(doc_id, newPage.id)}`
472
417
  }],
473
418
  };
474
419
  }
475
420
  return {
476
- content: [{ type: "text", text: "Error: Invalid parent_type specified." }],
421
+ content: [{ type: "text", text: "Error: Unexpected state in document/page creation." }],
477
422
  };
478
423
  }
479
424
  catch (error) {
480
- console.error('Error in writeDocument:', error);
425
+ console.error('Error creating document or page:', error);
481
426
  return {
482
427
  content: [
483
428
  {
484
429
  type: "text",
485
- text: `Error in document/page operation: ${error instanceof Error ? error.message : 'Unknown error'}`,
430
+ text: `Error creating document or page: ${error instanceof Error ? error.message : 'Unknown error'}`,
486
431
  },
487
432
  ],
488
433
  };
@@ -1 +1 @@
1
- {"version":3,"file":"list-tools.d.ts","sourceRoot":"","sources":["../../src/tools/list-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAKpE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,QAkHtD;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,QA4EvD"}
1
+ {"version":3,"file":"list-tools.d.ts","sourceRoot":"","sources":["../../src/tools/list-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAKpE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,QAkHtD;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,QA8EvD"}
@@ -118,7 +118,9 @@ function registerListToolsWrite(server) {
118
118
  list_id: zod_1.z.string().min(1).describe("The list ID to update"),
119
119
  append_description: zod_1.z.string().min(1).describe("Markdown content to APPEND to existing list description (preserves existing content for safety)")
120
120
  }, {
121
- readOnlyHint: false
121
+ readOnlyHint: false,
122
+ destructiveHint: false,
123
+ idempotentHint: false,
122
124
  }, async ({ list_id, append_description }) => {
123
125
  try {
124
126
  // Get current list info including description (try to get markdown content)
@@ -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;AASlE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,QA2LnE"}
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,QAmLnE"}
@@ -89,11 +89,8 @@ function registerSearchTools(server, userData) {
89
89
  ],
90
90
  };
91
91
  }
92
- // Fetch time entries for all unique list IDs
93
- const uniqueListIds = [...new Set(resultTasks.map((task) => task.list?.id).filter(Boolean))];
94
- const timeEntries = await fetchBulkTimeEntries(uniqueListIds);
95
92
  return {
96
- content: await Promise.all(resultTasks.map((task) => (0, task_tools_1.generateTaskMetadata)(task, timeEntries))),
93
+ content: await Promise.all(resultTasks.map((task) => (0, task_tools_1.generateTaskMetadata)(task))),
97
94
  };
98
95
  }
99
96
  // Create a results map to track unique tasks with scores
@@ -157,52 +154,8 @@ function registerSearchTools(server, userData) {
157
154
  ],
158
155
  };
159
156
  }
160
- // Fetch time entries for all unique list IDs
161
- const uniqueListIds = [...new Set(resultTasks.map((task) => task.list?.id).filter(Boolean))];
162
- const timeEntries = await fetchBulkTimeEntries(uniqueListIds);
163
157
  return {
164
- content: await Promise.all(resultTasks.map((task) => (0, task_tools_1.generateTaskMetadata)(task, timeEntries))),
158
+ content: await Promise.all(resultTasks.map((task) => (0, task_tools_1.generateTaskMetadata)(task))),
165
159
  };
166
160
  });
167
161
  }
168
- /**
169
- * Fetch time entries for a list of unique list IDs
170
- */
171
- async function fetchBulkTimeEntries(listIds) {
172
- try {
173
- // Get all team members for assignee filter
174
- const teamMembers = await (0, utils_1.getAllTeamMembers)();
175
- const assigneeParam = teamMembers.length > 0 ? teamMembers.join(',') : '';
176
- // Calculate 30 days ago timestamp
177
- const thirtyDaysAgo = new Date();
178
- thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
179
- const startDate = thirtyDaysAgo.getTime();
180
- // Fetch time entries for each unique list ID
181
- const timeEntryPromises = listIds.map(async (listId) => {
182
- const params = new URLSearchParams({
183
- list_id: listId,
184
- start_date: startDate.toString(),
185
- include_location_names: 'true'
186
- });
187
- if (assigneeParam) {
188
- params.append('assignee', assigneeParam);
189
- }
190
- const response = await fetch(`https://api.clickup.com/api/v2/team/${config_1.CONFIG.teamId}/time_entries?${params}`, {
191
- headers: { Authorization: config_1.CONFIG.apiKey },
192
- });
193
- if (!response.ok) {
194
- console.error(`Error fetching time entries for list ${listId}: ${response.status} ${response.statusText}`);
195
- return [];
196
- }
197
- const data = await response.json();
198
- return data.data || [];
199
- });
200
- // Wait for all requests to complete and flatten results
201
- const results = await Promise.all(timeEntryPromises);
202
- return results.flat();
203
- }
204
- catch (error) {
205
- console.error('Error fetching bulk time entries:', error);
206
- return [];
207
- }
208
- }
@@ -1 +1 @@
1
- {"version":3,"file":"task-write-tools.d.ts","sourceRoot":"","sources":["../../src/tools/task-write-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAapE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,QA2YtE"}
1
+ {"version":3,"file":"task-write-tools.d.ts","sourceRoot":"","sources":["../../src/tools/task-write-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAapE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,QAmZtE"}
@@ -31,7 +31,9 @@ function registerTaskToolsWrite(server, userData) {
31
31
  task_id: zod_1.z.string().min(6).max(9).describe("The 6-9 character task ID to comment on"),
32
32
  comment: zod_1.z.string().min(1).describe("The comment text to add to the task"),
33
33
  }, {
34
- readOnlyHint: false
34
+ readOnlyHint: false,
35
+ destructiveHint: false,
36
+ idempotentHint: false,
35
37
  }, async ({ task_id, comment }) => {
36
38
  try {
37
39
  const requestBody = {
@@ -111,7 +113,10 @@ function registerTaskToolsWrite(server, userData) {
111
113
  blocking: zod_1.z.array(zod_1.z.string()).optional().describe("Optional array of task IDs that this task should block. Note: This creates dependencies FROM those tasks TO this task (those tasks will wait on this one)"),
112
114
  linked_tasks: zod_1.z.array(zod_1.z.string()).optional().describe("Optional array of task IDs to link as related tasks without blocking (will replace existing linked tasks)")
113
115
  }, {
114
- readOnlyHint: false
116
+ readOnlyHint: false,
117
+ destructiveHint: true,
118
+ idempotentHint: false,
119
+ openWorldHint: true
115
120
  }, async ({ task_id, name, append_description, status, priority, due_date, start_date, time_estimate, tags, parent_task_id, assignees, blocking, waiting_on, linked_tasks }) => {
116
121
  try {
117
122
  const userData = await (0, utils_1.getCurrentUser)();
@@ -289,7 +294,10 @@ function registerTaskToolsWrite(server, userData) {
289
294
  parent_task_id: zod_1.z.string().optional().describe("Optional parent task ID to create this as a subtask"),
290
295
  assignees: zod_1.z.array(zod_1.z.string()).optional().describe(createAssigneeDescription(userData))
291
296
  }, {
292
- readOnlyHint: false
297
+ readOnlyHint: false,
298
+ destructiveHint: false,
299
+ idempotentHint: false,
300
+ openWorldHint: true
293
301
  }, async ({ list_id, name, description, status, priority, due_date, start_date, time_estimate, tags, parent_task_id, assignees }) => {
294
302
  try {
295
303
  const userData = await (0, utils_1.getCurrentUser)();
@@ -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;AA0DpE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,QAgFtD;AAwLD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,QAsFvD"}
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,QAgFtD;AAwLD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,QAwFvD"}
@@ -271,7 +271,9 @@ function registerTimeToolsWrite(server) {
271
271
  description: zod_1.z.string().optional().describe("Optional description for the time entry"),
272
272
  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)")
273
273
  }, {
274
- readOnlyHint: false
274
+ readOnlyHint: false,
275
+ destructiveHint: false,
276
+ idempotentHint: false,
275
277
  }, async ({ task_id, hours, description, start_time }) => {
276
278
  try {
277
279
  // Convert hours to milliseconds (ClickUp API uses milliseconds)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hauptsache.net/clickup-mcp",
3
- "version": "1.4.3",
3
+ "version": "1.5.1",
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",