@hauptsache.net/clickup-mcp 1.1.1 → 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 +20 -21
- package/dist/cli.js +8 -0
- package/dist/clickup-text.d.ts +5 -2
- package/dist/clickup-text.d.ts.map +1 -1
- package/dist/clickup-text.js +133 -61
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +37 -13
- package/dist/shared/config.d.ts +1 -0
- package/dist/shared/config.d.ts.map +1 -1
- package/dist/shared/config.js +1 -0
- package/dist/shared/image-processing.d.ts +13 -0
- package/dist/shared/image-processing.d.ts.map +1 -0
- package/dist/shared/image-processing.js +126 -0
- package/dist/shared/types.d.ts +7 -1
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/shared/utils.d.ts +24 -26
- package/dist/shared/utils.d.ts.map +1 -1
- package/dist/shared/utils.js +424 -101
- 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/list-tools.js +26 -149
- package/dist/tools/search-tools.d.ts +1 -1
- package/dist/tools/search-tools.d.ts.map +1 -1
- package/dist/tools/search-tools.js +85 -62
- package/dist/tools/space-tools.d.ts.map +1 -1
- package/dist/tools/space-tools.js +153 -27
- package/dist/tools/task-tools.d.ts +1 -2
- package/dist/tools/task-tools.d.ts.map +1 -1
- package/dist/tools/task-tools.js +86 -405
- package/dist/tools/task-write-tools.d.ts +3 -0
- package/dist/tools/task-write-tools.d.ts.map +1 -0
- package/dist/tools/task-write-tools.js +375 -0
- package/dist/tools/time-tools.d.ts.map +1 -1
- package/dist/tools/time-tools.js +2 -30
- package/package.json +15 -5
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.downloadImages = downloadImages;
|
|
4
|
+
const config_1 = require("./config");
|
|
5
|
+
const buffer_1 = require("buffer");
|
|
6
|
+
/**
|
|
7
|
+
* Downloads images from image_metadata blocks and applies smart size/count limiting
|
|
8
|
+
* Prioritizes keeping the most recent images (assumes content is ordered with newest items last)
|
|
9
|
+
* Uses intelligent size calculation accounting for text content
|
|
10
|
+
*
|
|
11
|
+
* @param content Array of content blocks that may contain image_metadata blocks
|
|
12
|
+
* @param maxImages Maximum number of images to keep (defaults to CONFIG.maxImages)
|
|
13
|
+
* @param maxSizeMB Maximum response size in MB (defaults to CONFIG.maxResponseSizeMB)
|
|
14
|
+
* @returns Promise resolving to content array with downloaded images or placeholders
|
|
15
|
+
*/
|
|
16
|
+
async function downloadImages(content, maxImages = config_1.CONFIG.maxImages, maxSizeMB = config_1.CONFIG.maxResponseSizeMB) {
|
|
17
|
+
// First apply count-based limiting to image_metadata blocks
|
|
18
|
+
const countLimitedContent = applyCountBasedLimitToImageMetadata(content, maxImages);
|
|
19
|
+
// Calculate text size to determine available image budget
|
|
20
|
+
const textContent = countLimitedContent.filter(block => block.type !== "image_metadata");
|
|
21
|
+
const textSizeBytes = JSON.stringify(textContent).length;
|
|
22
|
+
const maxSizeBytes = maxSizeMB * 1024 * 1024;
|
|
23
|
+
const availableImageBudget = Math.max(0, maxSizeBytes - textSizeBytes);
|
|
24
|
+
// Calculate per-image budget
|
|
25
|
+
const imageMetadataBlocks = countLimitedContent.filter(block => block.type === "image_metadata");
|
|
26
|
+
const perImageBudget = imageMetadataBlocks.length > 0 ? availableImageBudget / imageMetadataBlocks.length : 0;
|
|
27
|
+
// Download images in parallel and replace image_metadata blocks
|
|
28
|
+
const downloadPromises = countLimitedContent.map(async (block) => {
|
|
29
|
+
if (block.type === "image_metadata") {
|
|
30
|
+
return await downloadSingleImage(block, perImageBudget);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
return block;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
return Promise.all(downloadPromises);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Apply count-based limiting to image_metadata blocks
|
|
40
|
+
* Keeps the most recent image_metadata blocks (newest last)
|
|
41
|
+
*/
|
|
42
|
+
function applyCountBasedLimitToImageMetadata(content, maxImages) {
|
|
43
|
+
// Find all image_metadata block indices
|
|
44
|
+
const imageMetadataIndices = [];
|
|
45
|
+
content.forEach((block, index) => {
|
|
46
|
+
if (block.type === "image_metadata") {
|
|
47
|
+
imageMetadataIndices.push(index);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
// If we have fewer images than the limit, return the original content
|
|
51
|
+
if (imageMetadataIndices.length <= maxImages) {
|
|
52
|
+
return content;
|
|
53
|
+
}
|
|
54
|
+
// Determine which image_metadata blocks to remove (keep the most recent ones)
|
|
55
|
+
const imagesToRemove = imageMetadataIndices.slice(0, imageMetadataIndices.length - maxImages);
|
|
56
|
+
// Create a new content array with excess image_metadata blocks replaced by text placeholders
|
|
57
|
+
return content.map((block, index) => {
|
|
58
|
+
if (block.type === "image_metadata" && imagesToRemove.includes(index)) {
|
|
59
|
+
return {
|
|
60
|
+
type: "text",
|
|
61
|
+
text: "[Image removed due to count limitations. Only the most recent images are shown.]",
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return block;
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Download a single image from an image_metadata block, trying different sizes if needed
|
|
69
|
+
*/
|
|
70
|
+
async function downloadSingleImage(imageMetadata, perImageBudget) {
|
|
71
|
+
const fallbackText = {
|
|
72
|
+
type: "text",
|
|
73
|
+
text: `[Image "${imageMetadata.alt}" removed due to size limitations.]`,
|
|
74
|
+
};
|
|
75
|
+
// Try each URL in order (largest to smallest)
|
|
76
|
+
for (const url of imageMetadata.urls) {
|
|
77
|
+
const abortController = new AbortController();
|
|
78
|
+
try {
|
|
79
|
+
const response = await fetch(url, {
|
|
80
|
+
signal: abortController.signal
|
|
81
|
+
});
|
|
82
|
+
if (!response.ok) {
|
|
83
|
+
console.error(`Failed to fetch image from ${url}: ${response.status}`);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
// Check Content-Length header first to avoid downloading large images
|
|
87
|
+
const contentLength = response.headers.get("Content-Length");
|
|
88
|
+
if (contentLength) {
|
|
89
|
+
const imageSizeBytes = parseInt(contentLength, 10);
|
|
90
|
+
if (imageSizeBytes > perImageBudget) {
|
|
91
|
+
// Cancel the request to stop any ongoing download
|
|
92
|
+
abortController.abort();
|
|
93
|
+
console.error(`Image from ${url} is ${imageSizeBytes} bytes (from Content-Length), exceeds budget of ${perImageBudget} bytes`);
|
|
94
|
+
// Continue to try smaller thumbnail
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const imageBuffer = await response.arrayBuffer();
|
|
99
|
+
const actualSizeBytes = imageBuffer.byteLength;
|
|
100
|
+
// Double-check actual size (in case Content-Length was missing or incorrect)
|
|
101
|
+
if (actualSizeBytes <= perImageBudget) {
|
|
102
|
+
return {
|
|
103
|
+
type: "image",
|
|
104
|
+
mimeType: response.headers.get("Content-Type") || "image/png",
|
|
105
|
+
data: buffer_1.Buffer.from(imageBuffer).toString("base64"),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
// Cancel to clean up the connection
|
|
110
|
+
abortController.abort();
|
|
111
|
+
console.error(`Image from ${url} is ${actualSizeBytes} bytes (actual size), exceeds budget of ${perImageBudget} bytes`);
|
|
112
|
+
// Continue to try smaller thumbnail
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
if (error.name === 'AbortError') {
|
|
117
|
+
// Request was cancelled, this is expected - continue to next URL
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
console.error(`Error fetching image from ${url}: ${error.message || "Unknown error"}`);
|
|
121
|
+
// Continue to try next URL
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// If all URLs failed or were too large, return fallback text
|
|
125
|
+
return fallbackText;
|
|
126
|
+
}
|
package/dist/shared/types.d.ts
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import { CallToolResult } from "@modelcontextprotocol/sdk/types";
|
|
2
2
|
export type ContentBlock = CallToolResult['content'][number];
|
|
3
|
+
export interface ImageMetadataBlock {
|
|
4
|
+
type: "image_metadata";
|
|
5
|
+
urls: string[];
|
|
6
|
+
alt: string;
|
|
7
|
+
}
|
|
3
8
|
export interface DatedContentEvent {
|
|
4
9
|
date: string;
|
|
5
|
-
contentBlocks: ContentBlock[];
|
|
10
|
+
contentBlocks: (ContentBlock | ImageMetadataBlock)[];
|
|
6
11
|
}
|
|
7
12
|
export interface Config {
|
|
8
13
|
apiKey: string;
|
|
9
14
|
teamId: string;
|
|
10
15
|
maxImages: number;
|
|
16
|
+
maxResponseSizeMB: number;
|
|
11
17
|
primaryLanguageHint: string | undefined;
|
|
12
18
|
}
|
|
13
19
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/shared/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAGjE,MAAM,MAAM,YAAY,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AAG7D,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,YAAY,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/shared/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAGjE,MAAM,MAAM,YAAY,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AAG7D,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,gBAAgB,CAAC;IACvB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb;AAGD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,CAAC,YAAY,GAAG,kBAAkB,CAAC,EAAE,CAAC;CACtD;AAGD,MAAM,WAAW,MAAM;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,mBAAmB,EAAE,MAAM,GAAG,SAAS,CAAC;CACzC"}
|
package/dist/shared/utils.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ContentBlock } from "./types";
|
|
2
1
|
import Fuse from 'fuse.js';
|
|
3
2
|
/**
|
|
4
3
|
* Checks if a string looks like a valid ClickUp task ID
|
|
@@ -7,24 +6,17 @@ import Fuse from 'fuse.js';
|
|
|
7
6
|
export declare function isTaskId(str: string): boolean;
|
|
8
7
|
/**
|
|
9
8
|
* Get current authenticated user information from ClickUp API
|
|
10
|
-
* Caches the
|
|
9
|
+
* Caches the promise to prevent race conditions on concurrent calls
|
|
11
10
|
*/
|
|
12
11
|
export declare function getCurrentUser(): Promise<any>;
|
|
13
|
-
|
|
14
|
-
* Limits the number of images in the content array, replacing excess images with text placeholders
|
|
15
|
-
* Prioritizes keeping the most recent images (assumes content is ordered with newest items last)
|
|
16
|
-
*
|
|
17
|
-
* @param content Array of content blocks that may contain images
|
|
18
|
-
* @param maxImages Maximum number of images to keep
|
|
19
|
-
* @returns Modified content array with limited images
|
|
20
|
-
*/
|
|
21
|
-
export declare function limitImages(content: ContentBlock[], maxImages: number): ContentBlock[];
|
|
12
|
+
export { downloadImages } from "./image-processing";
|
|
22
13
|
/**
|
|
23
14
|
* Function to get space details, using a cache to avoid redundant fetches
|
|
24
15
|
*/
|
|
25
16
|
export declare function getSpaceDetails(spaceId: string): Promise<any>;
|
|
26
17
|
/**
|
|
27
18
|
* Get or create a task search index with specified filters
|
|
19
|
+
* Caches promises to prevent race conditions on concurrent calls
|
|
28
20
|
*/
|
|
29
21
|
export declare function getTaskSearchIndex(space_ids?: string[], list_ids?: string[], assignees?: string[]): Promise<Fuse<any> | null>;
|
|
30
22
|
/**
|
|
@@ -44,30 +36,36 @@ export declare function generateSpaceUrl(spaceId: string): string;
|
|
|
44
36
|
*/
|
|
45
37
|
export declare function generateFolderUrl(folderId: string): string;
|
|
46
38
|
/**
|
|
47
|
-
*
|
|
39
|
+
* Generate a ClickUp document URL from a document ID and optional page ID
|
|
48
40
|
*/
|
|
49
|
-
export declare function
|
|
41
|
+
export declare function generateDocumentUrl(docId: string, pageId?: string): string;
|
|
50
42
|
/**
|
|
51
|
-
*
|
|
43
|
+
* Get or refresh the space search index
|
|
44
|
+
* Caches promise to prevent race conditions on concurrent calls
|
|
52
45
|
*/
|
|
53
|
-
export declare function
|
|
46
|
+
export declare function getSpaceSearchIndex(): Promise<Fuse<any> | null>;
|
|
54
47
|
/**
|
|
55
|
-
*
|
|
48
|
+
* Get lists, folders, and documents for a specific space with caching
|
|
56
49
|
*/
|
|
57
|
-
export declare function
|
|
50
|
+
export declare function getSpaceContent(spaceId: string): Promise<{
|
|
51
|
+
lists: any[];
|
|
52
|
+
folders: any[];
|
|
53
|
+
documents: any[];
|
|
54
|
+
}>;
|
|
58
55
|
/**
|
|
59
|
-
*
|
|
56
|
+
* Gets all team members from ClickUp API with caching
|
|
60
57
|
*/
|
|
61
|
-
export declare function
|
|
58
|
+
export declare function getAllTeamMembers(): Promise<string[]>;
|
|
62
59
|
/**
|
|
63
|
-
*
|
|
60
|
+
* Get or create a document search index with space name resolution
|
|
61
|
+
* Caches promises to prevent race conditions on concurrent calls
|
|
64
62
|
*/
|
|
65
|
-
export declare function
|
|
63
|
+
export declare function getDocumentSearchIndex(space_ids?: string[]): Promise<Fuse<any> | null>;
|
|
66
64
|
/**
|
|
67
|
-
*
|
|
65
|
+
* Performs multi-term search with aggressive boosting for items matching multiple terms
|
|
66
|
+
* @param searchIndex Fuse search index to search within
|
|
67
|
+
* @param terms Array of search terms
|
|
68
|
+
* @returns Array of items sorted by relevance (multi-term matches ranked higher)
|
|
68
69
|
*/
|
|
69
|
-
export declare function
|
|
70
|
-
text: string;
|
|
71
|
-
url: string;
|
|
72
|
-
}[]): string;
|
|
70
|
+
export declare function performMultiTermSearch<T>(searchIndex: Fuse<T>, terms: string[]): Promise<T[]>;
|
|
73
71
|
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/shared/utils.ts"],"names":[],"mappings":"
|
|
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;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"}
|