@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 +75 -35
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -2
- package/dist/resources/space-resources.d.ts.map +1 -1
- package/dist/resources/space-resources.js +1 -0
- package/dist/shared/utils.d.ts +0 -5
- package/dist/shared/utils.d.ts.map +1 -1
- package/dist/shared/utils.js +0 -151
- package/dist/tools/doc-tools.d.ts.map +1 -1
- package/dist/tools/doc-tools.js +134 -194
- package/dist/tools/search-tools.d.ts.map +1 -1
- package/dist/tools/search-tools.js +2 -49
- package/dist/tools/time-tools.d.ts.map +1 -1
- package/package.json +4 -3
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
|
-
##
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,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,
|
|
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"}
|
package/dist/shared/utils.d.ts
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/shared/utils.js
CHANGED
|
@@ -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,
|
|
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"}
|
package/dist/tools/doc-tools.js
CHANGED
|
@@ -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 \`
|
|
102
|
-
- Example:
|
|
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 \`
|
|
162
|
-
result.push(`Example:
|
|
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
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
"
|
|
186
|
-
"
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
.
|
|
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("
|
|
198
|
-
|
|
199
|
-
.
|
|
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("
|
|
210
|
+
.describe("Whether to append content to existing page content (default: false - replaces content)")
|
|
202
211
|
}, {
|
|
203
|
-
readOnlyHint:
|
|
204
|
-
}, async ({
|
|
212
|
+
readOnlyHint: false
|
|
213
|
+
}, async ({ doc_id, page_id, name, content, append = false }) => {
|
|
205
214
|
try {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
265
|
+
console.error('Error updating document page:', error);
|
|
273
266
|
return {
|
|
274
267
|
content: [
|
|
275
268
|
{
|
|
276
269
|
type: "text",
|
|
277
|
-
text: `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
|
-
|
|
285
|
-
|
|
286
|
-
"
|
|
287
|
-
"
|
|
288
|
-
"
|
|
289
|
-
"
|
|
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
|
-
|
|
284
|
+
space_id: zod_1.z
|
|
294
285
|
.string()
|
|
295
286
|
.optional()
|
|
296
|
-
.describe("
|
|
297
|
-
|
|
298
|
-
.
|
|
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("
|
|
301
|
-
|
|
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("
|
|
305
|
-
|
|
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("
|
|
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("
|
|
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 ({
|
|
310
|
+
}, async ({ space_id, list_id, doc_id, parent_page_id, name, content }) => {
|
|
320
311
|
try {
|
|
321
|
-
// Validate
|
|
322
|
-
|
|
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:
|
|
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 (
|
|
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:
|
|
326
|
+
text: "Error: parent_page_id requires doc_id to be provided."
|
|
366
327
|
}],
|
|
367
328
|
};
|
|
368
329
|
}
|
|
369
|
-
// Case
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
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:
|
|
375
|
-
create_page: false,
|
|
336
|
+
name: name,
|
|
337
|
+
create_page: false,
|
|
376
338
|
parent: {
|
|
377
|
-
id:
|
|
378
|
-
type:
|
|
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
|
-
|
|
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
|
-
|
|
394
|
-
// Create the first page
|
|
356
|
+
const newDocId = docData.id;
|
|
357
|
+
// Create the first page
|
|
395
358
|
const pageRequestBody = {
|
|
396
|
-
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/${
|
|
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
|
-
|
|
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 "${
|
|
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
|
-
|
|
420
|
-
|
|
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:
|
|
386
|
+
name: name,
|
|
424
387
|
content: content || '',
|
|
425
388
|
};
|
|
426
|
-
|
|
427
|
-
|
|
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
|
|
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
|
-
|
|
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;
|
|
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
|
|
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:
|
|
416
|
+
content: [{ type: "text", text: "Error: Unexpected state in document/page creation." }],
|
|
477
417
|
};
|
|
478
418
|
}
|
|
479
419
|
catch (error) {
|
|
480
|
-
console.error('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
|
|
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;
|
|
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
|
|
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
|
|
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;
|
|
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.
|
|
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
|
-
"
|
|
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",
|