@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.
@@ -30,36 +30,10 @@ function registerSpaceTools(server) {
30
30
  matchingSpaces = searchIndex._docs || [];
31
31
  }
32
32
  else {
33
- // Search with fuzzy matching
34
- const uniqueResults = new Map();
35
- terms.forEach(term => {
36
- const trimmedTerm = term.trim();
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
- spacesWithContent.forEach(({ space, lists, folders }, index) => {
94
- const spaceLines = [];
95
- const totalLists = lists.length + folders.reduce((sum, f) => sum + (f.lists?.length || 0), 0);
96
- // Space header
97
- 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`);
98
- // Create a tree structure
99
- const hasDirectLists = lists.length > 0;
100
- const hasFolders = folders.length > 0;
101
- // Direct lists (not in folders)
102
- if (hasDirectLists) {
103
- lists.forEach((list, listIndex) => {
104
- const isLastDirectList = listIndex === lists.length - 1;
105
- const isLastOverall = !hasFolders && isLastDirectList;
106
- const treeChar = isLastOverall ? '└──' : '├──';
107
- const extraInfo = [
108
- ...(list.task_count ? [`${list.task_count} tasks`] : []),
109
- ...(list.private ? ['private'] : []),
110
- ...(list.archived ? ['archived'] : [])
111
- ].join(', ');
112
- const listLine = `${treeChar} 📝 ${list.name} (list_id: ${list.id}${extraInfo ? `, ${extraInfo}` : ''}) ${(0, utils_1.generateListUrl)(list.id)}`;
113
- spaceLines.push(listLine);
114
- });
115
- }
116
- // Folders and their lists
117
- if (hasFolders) {
118
- folders.forEach((folder, folderIndex) => {
119
- const isLastFolder = folderIndex === folders.length - 1;
120
- const folderTreeChar = isLastFolder ? '└──' : '├──';
121
- const folderContinuation = isLastFolder ? ' ' : '│ ';
122
- const folderExtraInfo = [
123
- ...(folder.lists?.length ? [`${folder.lists.length} lists`] : []),
124
- ...(folder.private ? ['private'] : []),
125
- ...(folder.archived ? ['archived'] : [])
126
- ].join(', ');
127
- const folderLine = `${folderTreeChar} 📂 ${folder.name} (folder_id: ${folder.id}${folderExtraInfo ? `, ${folderExtraInfo}` : ''}) ${(0, utils_1.generateFolderUrl)(folder.id)}`;
128
- spaceLines.push(folderLine);
129
- // Lists within this folder
130
- if (folder.lists && folder.lists.length > 0) {
131
- folder.lists.forEach((list, listIndex) => {
132
- const isLastListInFolder = listIndex === folder.lists.length - 1;
133
- const listTreeChar = isLastListInFolder ? '└──' : '├──';
134
- const listExtraInfo = [
135
- ...(list.task_count ? [`${list.task_count} tasks`] : []),
136
- ...(list.private ? ['private'] : []),
137
- ...(list.archived ? ['archived'] : [])
138
- ].join(', ');
139
- const listLine = `${folderContinuation}${listTreeChar} 📝 ${list.name} (list_id: ${list.id}${listExtraInfo ? `, ${listExtraInfo}` : ''}) ${(0, utils_1.generateListUrl)(list.id)}`;
140
- spaceLines.push(listLine);
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
- // Add the complete space as a single content block
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: spaceLines.join('\n')
148
+ text: allSpaceLines.join('\n')
149
149
  });
150
- // Add separator between spaces (except for the last one)
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
- ? `Found ${matchingSpaces.length} space(s) with complete tree structure (${totalLists} total lists):`
172
- : `Found ${matchingSpaces.length} space(s) - too many to show detailed list information. Please search more precisely to get complete tree structure with lists and folders:`
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
  ],
@@ -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.2.0",
4
- "description": "Transform your AI assistant into a powerful ClickUp integration for both agentic coding and productivity management. Enables seamless task context sharing, intelligent search, time tracking, and complete project management workflows.",
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": "ISC",
39
+ "license": "MIT",
40
40
  "dependencies": {
41
41
  "@modelcontextprotocol/sdk": "^1.9.0",
42
42
  "fuse.js": "^7.1.0",