@hauptsache.net/clickup-mcp 1.2.0 → 1.3.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/LICENSE +21 -0
- package/README.md +18 -24
- package/dist/cli.js +7 -0
- package/dist/clickup-text.d.ts +1 -1
- package/dist/clickup-text.d.ts.map +1 -1
- package/dist/clickup-text.js +17 -13
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -9
- package/dist/shared/utils.d.ts +16 -26
- package/dist/shared/utils.d.ts.map +1 -1
- package/dist/shared/utils.js +247 -61
- package/dist/tools/doc-tools.d.ts +4 -0
- package/dist/tools/doc-tools.d.ts.map +1 -0
- package/dist/tools/doc-tools.js +485 -0
- package/dist/tools/list-tools.d.ts.map +1 -1
- package/dist/tools/search-tools.d.ts.map +1 -1
- package/dist/tools/search-tools.js +7 -29
- package/dist/tools/space-tools.d.ts.map +1 -1
- package/dist/tools/space-tools.js +94 -98
- package/dist/tools/task-tools.js +1 -1
- package/package.json +3 -3
|
@@ -30,36 +30,10 @@ function registerSpaceTools(server) {
|
|
|
30
30
|
matchingSpaces = searchIndex._docs || [];
|
|
31
31
|
}
|
|
32
32
|
else {
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (trimmedTerm.length === 0)
|
|
38
|
-
return;
|
|
39
|
-
// Check if it's an exact space ID first
|
|
40
|
-
const exactMatch = searchIndex._docs.find((space) => space.id === trimmedTerm);
|
|
41
|
-
if (exactMatch) {
|
|
42
|
-
uniqueResults.set(exactMatch.id, { item: exactMatch, score: 0 });
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
// Fuzzy search
|
|
46
|
-
const results = searchIndex.search(trimmedTerm);
|
|
47
|
-
results.forEach(result => {
|
|
48
|
-
if (result.item && typeof result.item.id === 'string') {
|
|
49
|
-
const currentScore = result.score ?? 1;
|
|
50
|
-
const existing = uniqueResults.get(result.item.id);
|
|
51
|
-
if (!existing || currentScore < existing.score) {
|
|
52
|
-
uniqueResults.set(result.item.id, {
|
|
53
|
-
item: result.item,
|
|
54
|
-
score: currentScore
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
matchingSpaces = Array.from(uniqueResults.values())
|
|
61
|
-
.sort((a, b) => a.score - b.score)
|
|
62
|
-
.map(entry => entry.item);
|
|
33
|
+
// Perform multi-term search with aggressive boosting
|
|
34
|
+
matchingSpaces = await (0, utils_1.performMultiTermSearch)(searchIndex, terms
|
|
35
|
+
// No ID matcher or direct fetcher for spaces - they don't have direct API endpoints
|
|
36
|
+
);
|
|
63
37
|
}
|
|
64
38
|
// Filter by archived status
|
|
65
39
|
if (!archived) {
|
|
@@ -75,87 +49,105 @@ function registerSpaceTools(server) {
|
|
|
75
49
|
try {
|
|
76
50
|
if (matchingSpaces.length <= 5) {
|
|
77
51
|
// Detailed mode: fetch lists and folders for this space
|
|
78
|
-
const { lists, folders } = await (0, utils_1.getSpaceContent)(space.id);
|
|
79
|
-
return { space, lists, folders };
|
|
52
|
+
const { lists, folders, documents } = await (0, utils_1.getSpaceContent)(space.id);
|
|
53
|
+
return { space, lists, folders, documents };
|
|
80
54
|
}
|
|
81
55
|
else {
|
|
82
56
|
// Summary mode: just return space without content
|
|
83
|
-
return { space, lists: [], folders: [] };
|
|
57
|
+
return { space, lists: [], folders: [], documents: [] };
|
|
84
58
|
}
|
|
85
59
|
}
|
|
86
60
|
catch (error) {
|
|
87
61
|
console.error(`Error fetching content for space ${space.id}:`, error);
|
|
88
|
-
return { space, lists: [], folders: [] };
|
|
62
|
+
return { space, lists: [], folders: [], documents: [] };
|
|
89
63
|
}
|
|
90
64
|
});
|
|
91
65
|
const spacesWithContent = await Promise.all(spaceContentPromises);
|
|
92
66
|
const contentBlocks = [];
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
67
|
+
const isDetailedMode = matchingSpaces.length <= 5;
|
|
68
|
+
if (isDetailedMode) {
|
|
69
|
+
// Detailed mode: create separate blocks for each space
|
|
70
|
+
spacesWithContent.forEach(({ space, lists, folders, documents }) => {
|
|
71
|
+
const spaceLines = [];
|
|
72
|
+
const totalLists = lists.length + folders.reduce((sum, f) => sum + (f.lists?.length || 0), 0);
|
|
73
|
+
// Space header
|
|
74
|
+
spaceLines.push(`🏢 SPACE: ${space.name} (space_id: ${space.id}${space.private ? ', private' : ''}${space.archived ? ', archived' : ''}) ${(0, utils_1.generateSpaceUrl)(space.id)}`, ` ${totalLists} lists, ${folders.length} folders, ${documents.length} documents`);
|
|
75
|
+
// Create a tree structure
|
|
76
|
+
const hasDirectLists = lists.length > 0;
|
|
77
|
+
const hasFolders = folders.length > 0;
|
|
78
|
+
const hasDocuments = documents.length > 0;
|
|
79
|
+
// Direct lists (not in folders)
|
|
80
|
+
if (hasDirectLists) {
|
|
81
|
+
lists.forEach((list, listIndex) => {
|
|
82
|
+
const isLastDirectList = listIndex === lists.length - 1;
|
|
83
|
+
const isLastOverall = !hasFolders && !hasDocuments && isLastDirectList;
|
|
84
|
+
const treeChar = isLastOverall ? '└──' : '├──';
|
|
85
|
+
const extraInfo = [
|
|
86
|
+
...(list.task_count ? [`${list.task_count} tasks`] : []),
|
|
87
|
+
...(list.private ? ['private'] : []),
|
|
88
|
+
...(list.archived ? ['archived'] : [])
|
|
89
|
+
].join(', ');
|
|
90
|
+
const listLine = `${treeChar} 📝 ${list.name} (list_id: ${list.id}${extraInfo ? `, ${extraInfo}` : ''}) ${(0, utils_1.generateListUrl)(list.id)}`;
|
|
91
|
+
spaceLines.push(listLine);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
// Folders and their lists
|
|
95
|
+
if (hasFolders) {
|
|
96
|
+
folders.forEach((folder, folderIndex) => {
|
|
97
|
+
const isLastFolder = folderIndex === folders.length - 1;
|
|
98
|
+
const isLastOverall = !hasDocuments && isLastFolder;
|
|
99
|
+
const folderTreeChar = isLastOverall ? '└──' : '├──';
|
|
100
|
+
const folderContinuation = isLastOverall ? ' ' : '│ ';
|
|
101
|
+
const folderExtraInfo = [
|
|
102
|
+
...(folder.lists?.length ? [`${folder.lists.length} lists`] : []),
|
|
103
|
+
...(folder.private ? ['private'] : []),
|
|
104
|
+
...(folder.archived ? ['archived'] : [])
|
|
105
|
+
].join(', ');
|
|
106
|
+
const folderLine = `${folderTreeChar} 📂 ${folder.name} (folder_id: ${folder.id}${folderExtraInfo ? `, ${folderExtraInfo}` : ''}) ${(0, utils_1.generateFolderUrl)(folder.id)}`;
|
|
107
|
+
spaceLines.push(folderLine);
|
|
108
|
+
// Lists within this folder
|
|
109
|
+
if (folder.lists && folder.lists.length > 0) {
|
|
110
|
+
folder.lists.forEach((list, listIndex) => {
|
|
111
|
+
const isLastListInFolder = listIndex === folder.lists.length - 1;
|
|
112
|
+
const listTreeChar = isLastListInFolder ? '└──' : '├──';
|
|
113
|
+
const listExtraInfo = [
|
|
114
|
+
...(list.task_count ? [`${list.task_count} tasks`] : []),
|
|
115
|
+
...(list.private ? ['private'] : []),
|
|
116
|
+
...(list.archived ? ['archived'] : [])
|
|
117
|
+
].join(', ');
|
|
118
|
+
const listLine = `${folderContinuation}${listTreeChar} 📝 ${list.name} (list_id: ${list.id}${listExtraInfo ? `, ${listExtraInfo}` : ''}) ${(0, utils_1.generateListUrl)(list.id)}`;
|
|
119
|
+
spaceLines.push(listLine);
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
// Documents attached to this space
|
|
125
|
+
if (hasDocuments) {
|
|
126
|
+
documents.forEach((document, docIndex) => {
|
|
127
|
+
const isLastDocument = docIndex === documents.length - 1;
|
|
128
|
+
const docTreeChar = isLastDocument ? '└──' : '├──';
|
|
129
|
+
const docLine = `${docTreeChar} 📄 ${document.name} (doc_id: ${document.id}) ${(0, utils_1.generateDocumentUrl)(document.id)}`;
|
|
130
|
+
spaceLines.push(docLine);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
// Add the complete space as a single content block
|
|
134
|
+
contentBlocks.push({
|
|
135
|
+
type: "text",
|
|
136
|
+
text: spaceLines.join('\n')
|
|
143
137
|
});
|
|
144
|
-
}
|
|
145
|
-
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
// Summary mode: create a single combined block with all spaces
|
|
142
|
+
const allSpaceLines = [];
|
|
143
|
+
spacesWithContent.forEach(({ space }) => {
|
|
144
|
+
allSpaceLines.push(`🏢 SPACE: ${space.name} (space_id: ${space.id}${space.private ? ', private' : ''}${space.archived ? ', archived' : ''})`);
|
|
145
|
+
});
|
|
146
146
|
contentBlocks.push({
|
|
147
147
|
type: "text",
|
|
148
|
-
text:
|
|
148
|
+
text: allSpaceLines.join('\n')
|
|
149
149
|
});
|
|
150
|
-
|
|
151
|
-
if (index < spacesWithContent.length - 1) {
|
|
152
|
-
contentBlocks.push({
|
|
153
|
-
type: "text",
|
|
154
|
-
text: '─'.repeat(50)
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
});
|
|
158
|
-
const totalLists = spacesWithContent.reduce((sum, { lists, folders }) => sum + lists.length + folders.reduce((folderSum, f) => folderSum + (f.lists?.length || 0), 0), 0);
|
|
150
|
+
}
|
|
159
151
|
// Add tip message for summary mode (when there are too many spaces)
|
|
160
152
|
if (matchingSpaces.length > 5) {
|
|
161
153
|
contentBlocks.push({
|
|
@@ -168,8 +160,12 @@ function registerSpaceTools(server) {
|
|
|
168
160
|
{
|
|
169
161
|
type: "text",
|
|
170
162
|
text: matchingSpaces.length <= 5
|
|
171
|
-
?
|
|
172
|
-
|
|
163
|
+
? (() => {
|
|
164
|
+
const totalLists = spacesWithContent.reduce((sum, { lists, folders }) => sum + lists.length + folders.reduce((folderSum, f) => folderSum + (f.lists?.length || 0), 0), 0);
|
|
165
|
+
const totalDocuments = spacesWithContent.reduce((sum, { documents }) => sum + documents.length, 0);
|
|
166
|
+
return `Found ${matchingSpaces.length} space(s) with complete tree structure (${totalLists} total lists, ${totalDocuments} total documents):`;
|
|
167
|
+
})()
|
|
168
|
+
: `Found ${matchingSpaces.length} space(s) - showing names and IDs only. Use more specific search terms to get detailed information:`
|
|
173
169
|
},
|
|
174
170
|
...contentBlocks
|
|
175
171
|
],
|
package/dist/tools/task-tools.js
CHANGED
|
@@ -91,7 +91,7 @@ async function loadTaskContent(taskId) {
|
|
|
91
91
|
return await generateTaskMetadata(task, timeEntries, true);
|
|
92
92
|
})(),
|
|
93
93
|
// process markdown and download images
|
|
94
|
-
(0, clickup_text_1.processClickUpMarkdown)(task.markdown_description || "", task.attachments),
|
|
94
|
+
(0, clickup_text_1.processClickUpMarkdown)(task.markdown_description || "", task.attachments || []),
|
|
95
95
|
]);
|
|
96
96
|
return [taskMetadata, ...content];
|
|
97
97
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hauptsache.net/clickup-mcp",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.3.0",
|
|
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",
|
|
7
7
|
"bin": "dist/index.js",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"name": "Marco Pfeiffer",
|
|
37
37
|
"email": "marco@hauptsache.net"
|
|
38
38
|
},
|
|
39
|
-
"license": "
|
|
39
|
+
"license": "MIT",
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@modelcontextprotocol/sdk": "^1.9.0",
|
|
42
42
|
"fuse.js": "^7.1.0",
|