@hauptsache.net/clickup-mcp 1.6.0 → 1.6.2

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,6 +2,34 @@
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
+ ## This MCP vs Official ClickUp MCP
6
+
7
+ > See also: [Official ClickUp MCP Documentation](https://developer.clickup.com/docs/connect-an-ai-assistant-to-clickups-mcp-server)
8
+
9
+ | Feature | This MCP | Official ClickUp MCP |
10
+ |----------------------|-------------------------------------------------------|---------------------------------------------|
11
+ | **Setup** | Local npm/npx install | Remote MCP (no install) |
12
+ | **Authentication** | API key only | OAuth only |
13
+ | **Task Context** | Complete with comments, status history, inline images | Requires mutiple tool calls for full contxt |
14
+ | **Image Support** | Inline images with smart size budgeting | Not documented |
15
+ | **Search** | Fuzzy search on recent tasks (limited scope) | Full ClickUp search database |
16
+ | **Documents** | CRUD operations | CRUD + document search |
17
+ | **Time Tracking** | View and create entries | Timers and entries |
18
+ | **Chat Integration** | Not supported | Supported |
19
+ | **Connected Apps** | Not supported | Connected Search |
20
+ | **Best For** | Coding tools, automation, context gathering | Chat apps, task management |
21
+ | **Support** | Community (open source) | Official ClickUp |
22
+
23
+ **Choose this MCP when:**
24
+ - You need rich task context with inline images for AI coding tools
25
+ - You need API key authentication for automation or CI/CD pipelines
26
+ - You want the `read-minimal` mode optimized for development workflows
27
+
28
+ **Choose Official MCP when:**
29
+ - You need OAuth authentication for enterprise security compliance
30
+ - You need Chat integration or Connected Search features
31
+ - You want official support and no local installation
32
+
5
33
  ## What You Can Do
6
34
 
7
35
  Turn natural language into powerful ClickUp actions:
@@ -1 +1 @@
1
- {"version":3,"file":"image-processing.d.ts","sourceRoot":"","sources":["../../src/shared/image-processing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAE,kBAAkB,EAAC,MAAM,SAAS,CAAC;AAKzD;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,CAAC,YAAY,GAAG,kBAAkB,CAAC,EAAE,EAAE,SAAS,GAAE,MAAyB,EAAE,SAAS,GAAE,MAAiC,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CA0BhM"}
1
+ {"version":3,"file":"image-processing.d.ts","sourceRoot":"","sources":["../../src/shared/image-processing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAE,kBAAkB,EAAC,MAAM,SAAS,CAAC;AAmDzD;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,CAAC,YAAY,GAAG,kBAAkB,CAAC,EAAE,EAAE,SAAS,GAAE,MAAyB,EAAE,SAAS,GAAE,MAAiC,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CA0BhM"}
@@ -4,6 +4,46 @@ exports.downloadImages = downloadImages;
4
4
  const config_1 = require("./config");
5
5
  const data_uri_1 = require("./data-uri");
6
6
  const buffer_1 = require("buffer");
7
+ /**
8
+ * Detect MIME type from image binary data using magic bytes (file signatures)
9
+ * Returns null if the format is not recognized
10
+ */
11
+ function detectMimeTypeFromBuffer(buffer) {
12
+ const bytes = new Uint8Array(buffer);
13
+ if (bytes.length < 12)
14
+ return null;
15
+ // PNG: 89 50 4E 47 (‰PNG)
16
+ if (bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4E && bytes[3] === 0x47) {
17
+ return "image/png";
18
+ }
19
+ // JPEG: FF D8 FF
20
+ if (bytes[0] === 0xFF && bytes[1] === 0xD8 && bytes[2] === 0xFF) {
21
+ return "image/jpeg";
22
+ }
23
+ // GIF: 47 49 46 38 (GIF8)
24
+ if (bytes[0] === 0x47 && bytes[1] === 0x49 && bytes[2] === 0x46 && bytes[3] === 0x38) {
25
+ return "image/gif";
26
+ }
27
+ // WebP: RIFF....WEBP
28
+ if (bytes[0] === 0x52 && bytes[1] === 0x49 && bytes[2] === 0x46 && bytes[3] === 0x46 &&
29
+ bytes[8] === 0x57 && bytes[9] === 0x45 && bytes[10] === 0x42 && bytes[11] === 0x50) {
30
+ return "image/webp";
31
+ }
32
+ return null;
33
+ }
34
+ /**
35
+ * Detect MIME type from base64-encoded image data
36
+ * Decodes just enough bytes to check magic numbers
37
+ */
38
+ function detectMimeTypeFromBase64(base64Data) {
39
+ // Need at least 16 base64 chars to decode 12 bytes for magic number detection
40
+ if (base64Data.length < 16)
41
+ return null;
42
+ // Create a Uint8Array directly from the decoded bytes to avoid Buffer pooling issues
43
+ const header = buffer_1.Buffer.from(base64Data.slice(0, 16), "base64");
44
+ const bytes = new Uint8Array(header);
45
+ return detectMimeTypeFromBuffer(bytes.buffer);
46
+ }
7
47
  /**
8
48
  * Downloads images from image_metadata blocks and applies smart size/count limiting
9
49
  * Prioritizes keeping the most recent images (assumes content is ordered with newest items last)
@@ -98,9 +138,12 @@ async function downloadSingleImage(imageMetadata, perImageBudget) {
98
138
  const actualSizeBytes = imageBuffer.byteLength;
99
139
  // Double-check actual size (in case Content-Length was missing or incorrect)
100
140
  if (actualSizeBytes <= perImageBudget) {
141
+ // Detect actual MIME type from binary data, fall back to header or default
142
+ const detectedMimeType = detectMimeTypeFromBuffer(imageBuffer);
143
+ const mimeType = detectedMimeType || response.headers.get("Content-Type") || "image/png";
101
144
  return {
102
145
  type: "image",
103
- mimeType: response.headers.get("Content-Type") || "image/png",
146
+ mimeType,
104
147
  data: buffer_1.Buffer.from(imageBuffer).toString("base64"),
105
148
  };
106
149
  }
@@ -145,9 +188,12 @@ function convertInlineImage(imageMetadata, perImageBudget) {
145
188
  console.error(`Inline image for "${imageMetadata.alt}" is ${estimatedSize} bytes, exceeds budget of ${perImageBudget} bytes`);
146
189
  return createImageFallback(imageMetadata);
147
190
  }
191
+ // Detect actual MIME type from binary data, fall back to declared type or default
192
+ const detectedMimeType = detectMimeTypeFromBase64(inlineData.base64Data);
193
+ const mimeType = detectedMimeType || inlineData.mimeType || "image/png";
148
194
  return {
149
195
  type: "image",
150
- mimeType: inlineData.mimeType || "image/png",
196
+ mimeType,
151
197
  data: inlineData.base64Data,
152
198
  };
153
199
  }
@@ -1 +1 @@
1
- {"version":3,"file":"task-tools.d.ts","sourceRoot":"","sources":["../../src/tools/task-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGpE,OAAO,EAAE,YAAY,EAAyC,MAAM,iBAAiB,CAAC;AAOtF,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,QA2DrE;AA+MD;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,GAAG,EAAE,WAAW,CAAC,EAAE,GAAG,EAAE,EAAE,YAAY,GAAE,OAAe,GAAG,OAAO,CAAC,YAAY,CAAC,CA+G/H"}
1
+ {"version":3,"file":"task-tools.d.ts","sourceRoot":"","sources":["../../src/tools/task-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGpE,OAAO,EAAE,YAAY,EAAyC,MAAM,iBAAiB,CAAC;AAOtF,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,QA2DrE;AA+MD;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,GAAG,EAAE,WAAW,CAAC,EAAE,GAAG,EAAE,EAAE,YAAY,GAAE,OAAe,GAAG,OAAO,CAAC,YAAY,CAAC,CAmH/H"}
@@ -229,9 +229,14 @@ async function generateTaskMetadata(task, timeEntries, isDetailView = false) {
229
229
  let spaceName = task.space?.name || 'Unknown Space';
230
230
  let spaceIdForDisplay = task.space?.id || 'N/A';
231
231
  if (spaceName === 'Unknown Space' && task.space?.id) {
232
- const spaceDetails = await (0, utils_1.getSpaceDetails)(task.space.id);
233
- if (spaceDetails && spaceDetails.name) {
234
- spaceName = spaceDetails.name;
232
+ try {
233
+ const spaceDetails = await (0, utils_1.getSpaceDetails)(task.space.id);
234
+ if (spaceDetails && spaceDetails.name) {
235
+ spaceName = spaceDetails.name;
236
+ }
237
+ }
238
+ catch {
239
+ // Space details fetch can fail (e.g. 401) - gracefully keep "Unknown Space"
235
240
  }
236
241
  }
237
242
  const metadataLines = [
@@ -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;AAyDpE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,QAgFtD;AAwLD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,QAwFvD"}
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;AAyLD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,QAwFvD"}
@@ -243,7 +243,8 @@ function processTimeEntriesData(data, task_id, start_date, end_date, include_all
243
243
  else {
244
244
  entryDuration = formatDuration(rawDuration);
245
245
  }
246
- outputLines.push(`${entryPrefix} ${entryStart} - ${entryDuration}`);
246
+ const entryDescription = entry.description ? ` | ${entry.description}` : '';
247
+ outputLines.push(`${entryPrefix} ${entryStart} - ${entryDuration}${entryDescription}`);
247
248
  });
248
249
  }
249
250
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hauptsache.net/clickup-mcp",
3
- "version": "1.6.0",
3
+ "version": "1.6.2",
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",
@@ -40,7 +40,7 @@
40
40
  },
41
41
  "license": "MIT",
42
42
  "dependencies": {
43
- "@modelcontextprotocol/sdk": "^1.15.1",
43
+ "@modelcontextprotocol/sdk": "1.15.1",
44
44
  "fuse.js": "^7.1.0",
45
45
  "remark-gfm": "^4.0.1",
46
46
  "remark-parse": "^11.0.0",