@hauptsache.net/clickup-mcp 1.4.2 → 1.5.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
@@ -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:
@@ -74,38 +71,81 @@ Turn natural language into powerful ClickUp actions:
74
71
  - **Append-Only Descriptions**: Description fields are never overwritten - new content is safely appended with timestamps
75
72
  - **Normal Field Updates**: Status, priority, assignees, tags, and dates can be updated normally (easily revertible through ClickUp's history)
76
73
 
77
- ## Setup for Claude Desktop, Windsurf, or Cursor
78
-
79
- 1. **Prerequisites**:
80
- - Your `CLICKUP_API_KEY` (Profile Icon > Settings > Apps > API Token ~ usually starts with pk_)
81
- - and your `CLICKUP_TEAM_ID` (The ~7 digit number in the url when you are in the settings)
82
-
83
- 2. **Configuration**:
84
- Add the following to your MCP configuration file:
85
-
86
- ```json
87
- {
88
- "mcpServers": {
89
- "clickup": {
90
- "command": "npx",
91
- "args": [
92
- "@hauptsache.net/clickup-mcp@latest"
93
- ],
94
- "env": {
95
- "CLICKUP_API_KEY": "your_api_key",
96
- "CLICKUP_TEAM_ID": "your_team_id"
97
- }
98
- }
99
- }
100
- }
101
- ```
102
-
103
- Replace `your_api_key` and `your_team_id` with your actual ClickUp credentials.
104
-
105
- 3. **Connect Your AI Assistant**:
106
- - **Claude Desktop**: Add this configuration in Settings > Developer > Edit Config
107
- - **Windsurf**: Add to your MCP configuration file
108
- - **Cursor**: Configure through the MCP settings panel
74
+ ## Installation
75
+
76
+ ### Prerequisites
77
+
78
+ For all installation methods, you'll need:
79
+ - Your `CLICKUP_API_KEY` (Profile Icon > Settings > Apps > API Token ~ usually starts with pk_)
80
+ - Your `CLICKUP_TEAM_ID` (The 7–10 digit number in the URL when you are in the settings)
81
+
82
+ ### Option 1: MCPB Bundle (Recommended for Claude Desktop)
83
+
84
+ Download the pre-built bundle from our [releases page](https://github.com/hauptsacheNet/clickup-mcp/releases). This method requires no Node.js installation.
85
+
86
+ You'll get a configuration screen where you are prompted to enter your API key and team ID.
87
+
88
+ ### Option 2: NPX Installation
89
+
90
+ This method automatically updates to the latest version and is preferred for users who want the newest features.
91
+
92
+ **For Claude Desktop, Windsurf, Cursor and others:**
93
+
94
+ Add the following to your MCP configuration file:
95
+
96
+ ```json
97
+ {
98
+ "mcpServers": {
99
+ "clickup": {
100
+ "command": "npx",
101
+ "args": [
102
+ "@hauptsache.net/clickup-mcp@latest"
103
+ ],
104
+ "env": {
105
+ "CLICKUP_API_KEY": "your_api_key",
106
+ "CLICKUP_TEAM_ID": "your_team_id"
107
+ }
108
+ }
109
+ }
110
+ }
111
+ ```
112
+
113
+ Replace `your_api_key` and `your_team_id` with your actual ClickUp credentials.
114
+
115
+ **Where to add this configuration:**
116
+ - **Claude Desktop**: Settings > Developer > Edit Config
117
+ - **Windsurf**: Add to your MCP configuration file
118
+ - **Cursor**: Configure through the MCP settings panel
119
+
120
+ ### Option 3: Coding Tools Integration
121
+
122
+ **Claude Code (CLI):**
123
+ ```bash
124
+ claude mcp add --scope user clickup \
125
+ --env CLICKUP_API_KEY=YOUR_KEY \
126
+ --env CLICKUP_TEAM_ID=YOUR_ID \
127
+ --env CLICKUP_MCP_MODE=read-minimal \
128
+ --env MAX_IMAGES=16 \
129
+ --env MAX_RESPONSE_SIZE_MB=4 \
130
+ -- npx -y @hauptsache.net/clickup-mcp
131
+ ```
132
+
133
+ > Claude Code can handle a lot of images, thus the recommended increased limits.
134
+
135
+ > Note the `CLICKUP_MCP_MODE=read-minimal`. This is my usage recommendation, but feel free to use one of the other modes.
136
+
137
+ **OpenAI Codex:**
138
+ Add these lines to your `~/.codex/config.toml` file:
139
+ ```toml
140
+ [mcp_servers.clickup]
141
+ command = "npx"
142
+ args = ["-y", "@hauptsache.net/clickup-mcp@latest"]
143
+ env = { "CLICKUP_API_KEY" = "YOUR_KEY", "CLICKUP_TEAM_ID" = "YOUR_ID", "CLICKUP_MCP_MODE" = "read-minimal" }
144
+ ```
145
+
146
+ > Codex seems to not be able to handle images from MCP's. See [this issue](https://github.com/openai/codex/issues/3741) for more details.
147
+
148
+ > Note the `CLICKUP_MCP_MODE=read-minimal`. This is my usage recommendation, but feel free to use one of the other modes.
109
149
 
110
150
  ## MCP Modes & Available Tools
111
151
 
@@ -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",
@@ -1 +1 @@
1
- {"version":3,"file":"space-resources.d.ts","sourceRoot":"","sources":["../../src/resources/space-resources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAoB,MAAM,yCAAyC,CAAC;AAiBtF;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,QAuFvD"}
1
+ {"version":3,"file":"space-resources.d.ts","sourceRoot":"","sources":["../../src/resources/space-resources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAoB,MAAM,yCAAyC,CAAC;AAiBtF;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,QAwFvD"}
@@ -35,6 +35,7 @@ function registerSpaceResources(server) {
35
35
  resources: activeSpaces.map((space) => ({
36
36
  uri: `clickup://space/${space.id}`,
37
37
  name: space.name,
38
+ title: space.name,
38
39
  mimeType: "text/plain"
39
40
  }))
40
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,QA+R3D"}
@@ -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,164 @@ 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
+ }, async ({ doc_id, page_id, name, content, append = false }) => {
205
214
  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
- };
215
+ const requestBody = {};
216
+ if (name) {
217
+ requestBody.name = name;
217
218
  }
218
- let results;
219
- if (terms && terms.length > 0) {
220
- // Perform multi-term search
221
- results = await (0, utils_1.performMultiTermSearch)(searchIndex, terms);
219
+ if (content !== undefined) {
220
+ requestBody.content = content;
221
+ requestBody.content_edit_mode = append ? 'append' : 'replace';
222
222
  }
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());
228
- }
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(", ")}` : "";
223
+ if (Object.keys(requestBody).length === 0) {
234
224
  return {
235
- content: [
236
- {
225
+ content: [{
237
226
  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
- ],
227
+ text: "Error: At least one of 'name' or 'content' must be provided for update."
228
+ }],
244
229
  };
245
230
  }
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}`);
231
+ const response = await fetch(`https://api.clickup.com/api/v3/workspaces/${config_1.CONFIG.teamId}/docs/${doc_id}/pages/${page_id}`, {
232
+ method: 'PUT',
233
+ headers: {
234
+ Authorization: config_1.CONFIG.apiKey,
235
+ 'Content-Type': 'application/json',
236
+ },
237
+ body: JSON.stringify(requestBody),
260
238
  });
261
- responseLines.push("", "Use `readDocument` with a document ID to view full content and page structure.");
239
+ if (!response.ok) {
240
+ const errorText = await response.text();
241
+ throw new Error(`Error updating page: ${response.status} ${response.statusText}. ${errorText}`);
242
+ }
243
+ // Handle potentially empty response
244
+ const responseText = await response.text();
245
+ let updatedPage = {};
246
+ if (responseText && responseText.trim()) {
247
+ try {
248
+ const data = JSON.parse(responseText);
249
+ updatedPage = data.page || data;
250
+ }
251
+ catch (e) {
252
+ // If JSON parsing fails, continue with empty updatedPage object
253
+ console.error('Warning: Could not parse response JSON, but update was successful');
254
+ }
255
+ }
256
+ const pageName = updatedPage.name || name || "page";
262
257
  return {
263
- content: [
264
- {
258
+ content: [{
265
259
  type: "text",
266
- text: responseLines.join("\n"),
267
- },
268
- ],
260
+ text: `✅ Successfully updated page "${pageName}" (page_id: ${page_id})\n\nPage URL: ${(0, utils_1.generateDocumentUrl)(doc_id, page_id)}`
261
+ }],
269
262
  };
270
263
  }
271
264
  catch (error) {
272
- console.error('Error searching documents:', error);
265
+ console.error('Error updating document page:', error);
273
266
  return {
274
267
  content: [
275
268
  {
276
269
  type: "text",
277
- text: `Error searching documents: ${error instanceof Error ? error.message : 'Unknown error'}`,
270
+ text: `Error updating page: ${error instanceof Error ? error.message : 'Unknown error'}`,
278
271
  },
279
272
  ],
280
273
  };
281
274
  }
282
275
  });
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."
276
+ server.tool("createDocumentOrPage", [
277
+ "Creates a new document with its first page, OR adds a page to an existing document.",
278
+ "To create a NEW document: provide space_id OR list_id + name + content",
279
+ "To add a page to EXISTING document: provide doc_id + name + content",
280
+ "To create a sub-page: provide doc_id + parent_page_id + name + content",
281
+ "Content is in markdown format and supports ClickUp's markdown features.",
282
+ "Always reference documents by their URLs when sharing with users."
292
283
  ].join("\n"), {
293
- page_id: zod_1.z
284
+ space_id: zod_1.z
294
285
  .string()
295
286
  .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"])
287
+ .describe("Create NEW document in this space (mutually exclusive with list_id and doc_id)"),
288
+ list_id: zod_1.z
289
+ .string()
299
290
  .optional()
300
- .describe("Type of parent when creating new content (required if page_id not provided)"),
301
- parent_id: zod_1.z
291
+ .describe("Create NEW document in this list (mutually exclusive with space_id and doc_id)"),
292
+ doc_id: zod_1.z
302
293
  .string()
303
294
  .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
295
+ .describe("Add page to EXISTING document with this ID (mutually exclusive with space_id and list_id)"),
296
+ parent_page_id: zod_1.z
306
297
  .string()
307
298
  .optional()
308
- .describe("Name for the page (required for new pages, optional for updates to rename)"),
299
+ .describe("Optional: when provided with doc_id, creates a sub-page under this parent page"),
300
+ name: zod_1.z
301
+ .string()
302
+ .min(1)
303
+ .describe("Name for the document/page being created"),
309
304
  content: zod_1.z
310
305
  .string()
311
306
  .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)")
307
+ .describe("Optional: page content in markdown format")
317
308
  }, {
318
309
  readOnlyHint: false
319
- }, async ({ page_id, parent_type, parent_id, page_name, content, append = false }) => {
310
+ }, async ({ space_id, list_id, doc_id, parent_page_id, name, content }) => {
320
311
  try {
321
- // Validate input parameters
322
- if (!page_id && (!parent_type || !parent_id)) {
312
+ // Validate mutually exclusive parameters
313
+ const locationParams = [space_id, list_id, doc_id].filter(Boolean).length;
314
+ if (locationParams !== 1) {
323
315
  return {
324
316
  content: [{
325
317
  type: "text",
326
- text: "Error: Either page_id (for updates) or both parent_type and parent_id (for creation) must be provided."
318
+ 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
319
  }],
328
320
  };
329
321
  }
330
- if (!page_id && !page_name) {
331
- return {
332
- content: [{
333
- type: "text",
334
- text: "Error: page_name is required when creating new pages."
335
- }],
336
- };
337
- }
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;
322
+ if (parent_page_id && !doc_id) {
362
323
  return {
363
324
  content: [{
364
325
  type: "text",
365
- text: `✅ Successfully updated page "${updatedPage.name}" (${page_id})\n\nPage URL: ${(0, utils_1.generateDocumentUrl)(updatedPage.doc_id, page_id)}`
326
+ text: "Error: parent_page_id requires doc_id to be provided."
366
327
  }],
367
328
  };
368
329
  }
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
330
+ // Case 1: Create new document in space or list
331
+ if (space_id || list_id) {
332
+ const parentId = space_id || list_id;
333
+ const parentType = space_id ? 4 : 6; // 4=Space, 6=List
334
+ // Create new document
373
335
  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
336
+ name: name,
337
+ create_page: false,
376
338
  parent: {
377
- id: parent_id,
378
- type: parent_type === "space" ? 4 : parent_type === "list" ? 6 : 7 // 4=Space, 6=List, 7=Workspace
339
+ id: parentId,
340
+ type: parentType
379
341
  }
380
342
  };
381
343
  const docResponse = await fetch(`https://api.clickup.com/api/v3/workspaces/${config_1.CONFIG.teamId}/docs`, {
@@ -387,16 +349,17 @@ function registerDocumentToolsWrite(server) {
387
349
  body: JSON.stringify(docRequestBody),
388
350
  });
389
351
  if (!docResponse.ok) {
390
- throw new Error(`Error creating document: ${docResponse.status} ${docResponse.statusText}`);
352
+ const errorText = await docResponse.text();
353
+ throw new Error(`Error creating document: ${docResponse.status} ${docResponse.statusText}. ${errorText}`);
391
354
  }
392
355
  const docData = await docResponse.json();
393
- docId = docData.id;
394
- // Create the first page manually for better control
356
+ const newDocId = docData.id;
357
+ // Create the first page
395
358
  const pageRequestBody = {
396
- name: page_name,
359
+ name: name,
397
360
  content: content || '',
398
361
  };
399
- const pageResponse = await fetch(`https://api.clickup.com/api/v3/workspaces/${config_1.CONFIG.teamId}/docs/${docId}/pages`, {
362
+ const pageResponse = await fetch(`https://api.clickup.com/api/v3/workspaces/${config_1.CONFIG.teamId}/docs/${newDocId}/pages`, {
400
363
  method: 'POST',
401
364
  headers: {
402
365
  Authorization: config_1.CONFIG.apiKey,
@@ -405,54 +368,28 @@ function registerDocumentToolsWrite(server) {
405
368
  body: JSON.stringify(pageRequestBody),
406
369
  });
407
370
  if (!pageResponse.ok) {
408
- throw new Error(`Error creating first page: ${pageResponse.status} ${pageResponse.statusText}`);
371
+ const errorText = await pageResponse.text();
372
+ throw new Error(`Error creating first page: ${pageResponse.status} ${pageResponse.statusText}. ${errorText}`);
409
373
  }
410
374
  const pageData = await pageResponse.json();
411
375
  const firstPage = pageData.page || pageData;
412
376
  return {
413
377
  content: [{
414
378
  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)}`
379
+ 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
380
  }],
417
381
  };
418
382
  }
419
- else if (parent_type === "doc") {
420
- // Create new page in existing document
421
- docId = parent_id;
383
+ // Case 2: Add page to existing document or create sub-page
384
+ if (doc_id) {
422
385
  const pageRequestBody = {
423
- name: page_name,
386
+ name: name,
424
387
  content: content || '',
425
388
  };
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}`);
389
+ if (parent_page_id) {
390
+ pageRequestBody.parent_page_id = parent_page_id;
436
391
  }
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`, {
392
+ const response = await fetch(`https://api.clickup.com/api/v3/workspaces/${config_1.CONFIG.teamId}/docs/${doc_id}/pages`, {
456
393
  method: 'POST',
457
394
  headers: {
458
395
  Authorization: config_1.CONFIG.apiKey,
@@ -461,28 +398,31 @@ function registerDocumentToolsWrite(server) {
461
398
  body: JSON.stringify(pageRequestBody),
462
399
  });
463
400
  if (!response.ok) {
464
- throw new Error(`Error creating sub-page: ${response.status} ${response.statusText}`);
401
+ const errorText = await response.text();
402
+ throw new Error(`Error creating page: ${response.status} ${response.statusText}. ${errorText}`);
465
403
  }
466
404
  const data = await response.json();
467
- const newPage = data.page || data; // Handle both nested and flat response formats
405
+ const newPage = data.page || data;
406
+ const pageType = parent_page_id ? "sub-page" : "page";
407
+ const parentInfo = parent_page_id ? ` under parent page ${parent_page_id}` : "";
468
408
  return {
469
409
  content: [{
470
410
  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)}`
411
+ text: `✅ Successfully created ${pageType} "${newPage.name}" (page_id: ${newPage.id})${parentInfo}\n\nPage URL: ${(0, utils_1.generateDocumentUrl)(doc_id, newPage.id)}`
472
412
  }],
473
413
  };
474
414
  }
475
415
  return {
476
- content: [{ type: "text", text: "Error: Invalid parent_type specified." }],
416
+ content: [{ type: "text", text: "Error: Unexpected state in document/page creation." }],
477
417
  };
478
418
  }
479
419
  catch (error) {
480
- console.error('Error in writeDocument:', error);
420
+ console.error('Error creating document or page:', error);
481
421
  return {
482
422
  content: [
483
423
  {
484
424
  type: "text",
485
- text: `Error in document/page operation: ${error instanceof Error ? error.message : 'Unknown error'}`,
425
+ text: `Error creating document or page: ${error instanceof Error ? error.message : 'Unknown error'}`,
486
426
  },
487
427
  ],
488
428
  };
@@ -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":"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,QAsFvD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hauptsache.net/clickup-mcp",
3
- "version": "1.4.2",
3
+ "version": "1.5.0",
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",
@@ -12,12 +12,12 @@
12
12
  "scripts": {
13
13
  "build": "tsc",
14
14
  "start": "node dist/index.js",
15
- "dev": "tsc -w & nodemon dist/index.js",
15
+ "dev": "npx tsc -w & nodemon dist/index.js",
16
16
  "cli": "npx ts-node src/cli.ts",
17
17
  "prettier": "prettier --write src/**/*.ts",
18
18
  "prepublishOnly": "rm -r dist && npm run build",
19
19
  "release": "npm run build && npm publish --access public && git add . && git commit -m \"Release v$(node -p 'require(\"./package.json\").version')\" && git tag -a v$(node -p 'require(\"./package.json\").version') -m \"Release v$(node -p 'require(\"./package.json\").version')\" && git push && git push --tags",
20
- "dxt": "npm run build && npx dxt pack",
20
+ "mcpb": "npm run build && mcpb pack . ClickUp.mcpb",
21
21
  "test": "node --test -r ts-node/register src/**/*.test.ts"
22
22
  },
23
23
  "keywords": [
@@ -45,6 +45,7 @@
45
45
  "zod": "^3.24.2"
46
46
  },
47
47
  "devDependencies": {
48
+ "@anthropic-ai/mcpb": "^1.1.1",
48
49
  "@types/node": "^22.14.1",
49
50
  "dotenv": "^16.5.0",
50
51
  "nodemon": "^3.1.9",