@taazkareem/clickup-mcp-server 0.5.1 → 0.6.1
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 +29 -38
- package/build/config.js +33 -2
- package/build/index.js +16 -29
- package/build/logger.js +8 -11
- package/build/server.js +33 -8
- package/build/services/clickup/base.js +3 -0
- package/build/services/clickup/bulk.js +3 -0
- package/build/services/clickup/folder.js +3 -0
- package/build/services/clickup/index.js +9 -1
- package/build/services/clickup/list.js +3 -0
- package/build/services/clickup/tag.js +190 -0
- package/build/services/clickup/task.js +165 -2
- package/build/services/clickup/types.js +3 -0
- package/build/services/clickup/workspace.js +3 -0
- package/build/services/shared.js +3 -0
- package/build/tools/folder.js +3 -0
- package/build/tools/index.js +4 -0
- package/build/tools/list.js +3 -0
- package/build/tools/tag.js +824 -0
- package/build/tools/task/attachments.js +3 -0
- package/build/tools/task/bulk-operations.js +10 -0
- package/build/tools/task/handlers.js +76 -10
- package/build/tools/task/index.js +8 -1
- package/build/tools/task/main.js +18 -2
- package/build/tools/task/single-operations.js +64 -46
- package/build/tools/task/utilities.js +40 -3
- package/build/tools/task/workspace-operations.js +222 -0
- package/build/tools/utils.js +3 -0
- package/build/tools/workspace.js +9 -46
- package/build/utils/color-processor.js +183 -0
- package/build/utils/concurrency-utils.js +3 -0
- package/build/utils/date-utils.js +3 -0
- package/build/utils/resolver-utils.js +3 -0
- package/build/utils/sponsor-service.js +5 -2
- package/build/utils/token-utils.js +49 -0
- package/package.json +1 -1
package/build/tools/workspace.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* SPDX-FileCopyrightText: © 2025 Talib Kareem <taazkareem@icloud.com>
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*
|
|
2
5
|
* ClickUp MCP Workspace Tools
|
|
3
6
|
*
|
|
4
7
|
* This module defines workspace-related tools like retrieving workspace hierarchy.
|
|
@@ -6,11 +9,11 @@
|
|
|
6
9
|
*/
|
|
7
10
|
import { Logger } from '../logger.js';
|
|
8
11
|
import { sponsorService } from '../utils/sponsor-service.js';
|
|
12
|
+
import { clickUpServices } from '../services/shared.js';
|
|
9
13
|
// Create a logger for workspace tools
|
|
10
14
|
const logger = new Logger('WorkspaceTool');
|
|
11
|
-
// Use the workspace service
|
|
12
|
-
|
|
13
|
-
let workspaceService;
|
|
15
|
+
// Use the workspace service from the shared services
|
|
16
|
+
const { workspace: workspaceService } = clickUpServices;
|
|
14
17
|
/**
|
|
15
18
|
* Tool definition for retrieving the complete workspace hierarchy
|
|
16
19
|
*/
|
|
@@ -33,20 +36,6 @@ Notes:
|
|
|
33
36
|
properties: {}
|
|
34
37
|
}
|
|
35
38
|
};
|
|
36
|
-
/**
|
|
37
|
-
* Initialize the tool with services
|
|
38
|
-
*/
|
|
39
|
-
export function initializeWorkspaceTool(services) {
|
|
40
|
-
logger.info('Initializing workspace tool');
|
|
41
|
-
if (!services || !services.workspace) {
|
|
42
|
-
logger.error('Failed to initialize workspace tool: services not provided');
|
|
43
|
-
throw new Error('Workspace service not available');
|
|
44
|
-
}
|
|
45
|
-
workspaceService = services.workspace;
|
|
46
|
-
logger.info('Workspace tool initialized successfully', {
|
|
47
|
-
serviceType: workspaceService.constructor.name
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
39
|
/**
|
|
51
40
|
* Handler for the get_workspace_hierarchy tool
|
|
52
41
|
*/
|
|
@@ -56,40 +45,13 @@ export async function handleGetWorkspaceHierarchy() {
|
|
|
56
45
|
const hierarchy = await workspaceService.getWorkspaceHierarchy();
|
|
57
46
|
// Generate tree representation
|
|
58
47
|
const treeOutput = formatTreeOutput(hierarchy);
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}, true);
|
|
48
|
+
// Use sponsor service to create the response with optional sponsor message
|
|
49
|
+
return sponsorService.createResponse({ hierarchy: treeOutput }, true);
|
|
62
50
|
}
|
|
63
51
|
catch (error) {
|
|
64
52
|
return sponsorService.createErrorResponse(`Error getting workspace hierarchy: ${error.message}`);
|
|
65
53
|
}
|
|
66
54
|
}
|
|
67
|
-
/**
|
|
68
|
-
* Format the hierarchy for the response
|
|
69
|
-
*/
|
|
70
|
-
function formatHierarchyResponse(hierarchy) {
|
|
71
|
-
try {
|
|
72
|
-
const treeOutput = formatTreeOutput(hierarchy);
|
|
73
|
-
return {
|
|
74
|
-
content: [
|
|
75
|
-
{
|
|
76
|
-
type: "text",
|
|
77
|
-
text: treeOutput
|
|
78
|
-
}
|
|
79
|
-
]
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
catch (error) {
|
|
83
|
-
return {
|
|
84
|
-
content: [
|
|
85
|
-
{
|
|
86
|
-
type: "text",
|
|
87
|
-
text: `Error formatting workspace hierarchy: ${error.message}`
|
|
88
|
-
}
|
|
89
|
-
]
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
55
|
/**
|
|
94
56
|
* Format the hierarchy as a tree string
|
|
95
57
|
*/
|
|
@@ -114,5 +76,6 @@ function formatTreeOutput(hierarchy) {
|
|
|
114
76
|
};
|
|
115
77
|
// Generate tree representation
|
|
116
78
|
const treeLines = formatNodeAsTree(hierarchy.root);
|
|
79
|
+
// Return plain text instead of adding code block markers
|
|
117
80
|
return treeLines.join('\n');
|
|
118
81
|
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPDX-FileCopyrightText: © 2025 Talib Kareem <taazkareem@icloud.com>
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*
|
|
5
|
+
* Color Processor Utility
|
|
6
|
+
*
|
|
7
|
+
* Processes natural language color commands and converts them to HEX color values.
|
|
8
|
+
* Also generates appropriate foreground (text) colors for optimal contrast.
|
|
9
|
+
*/
|
|
10
|
+
// Basic color mapping with common color names to their HEX values
|
|
11
|
+
const COLOR_MAP = {
|
|
12
|
+
// Primary colors
|
|
13
|
+
red: '#FF0000',
|
|
14
|
+
green: '#00FF00',
|
|
15
|
+
blue: '#0000FF',
|
|
16
|
+
// Secondary colors
|
|
17
|
+
yellow: '#FFFF00',
|
|
18
|
+
purple: '#800080',
|
|
19
|
+
orange: '#FFA500',
|
|
20
|
+
pink: '#FFC0CB',
|
|
21
|
+
brown: '#A52A2A',
|
|
22
|
+
// Neutrals
|
|
23
|
+
black: '#000000',
|
|
24
|
+
white: '#FFFFFF',
|
|
25
|
+
gray: '#808080',
|
|
26
|
+
grey: '#808080',
|
|
27
|
+
// Extended colors
|
|
28
|
+
navy: '#000080',
|
|
29
|
+
teal: '#008080',
|
|
30
|
+
olive: '#808000',
|
|
31
|
+
maroon: '#800000',
|
|
32
|
+
aqua: '#00FFFF',
|
|
33
|
+
cyan: '#00FFFF',
|
|
34
|
+
magenta: '#FF00FF',
|
|
35
|
+
fuchsia: '#FF00FF',
|
|
36
|
+
lime: '#00FF00',
|
|
37
|
+
indigo: '#4B0082',
|
|
38
|
+
violet: '#EE82EE',
|
|
39
|
+
gold: '#FFD700',
|
|
40
|
+
silver: '#C0C0C0',
|
|
41
|
+
beige: '#F5F5DC',
|
|
42
|
+
tan: '#D2B48C',
|
|
43
|
+
coral: '#FF7F50',
|
|
44
|
+
crimson: '#DC143C',
|
|
45
|
+
khaki: '#F0E68C',
|
|
46
|
+
lavender: '#E6E6FA',
|
|
47
|
+
plum: '#DDA0DD',
|
|
48
|
+
salmon: '#FA8072',
|
|
49
|
+
turquoise: '#40E0D0',
|
|
50
|
+
};
|
|
51
|
+
// Extended color variations
|
|
52
|
+
const COLOR_VARIATIONS = {
|
|
53
|
+
red: {
|
|
54
|
+
light: '#FF6666',
|
|
55
|
+
dark: '#8B0000',
|
|
56
|
+
bright: '#FF0000',
|
|
57
|
+
deep: '#8B0000',
|
|
58
|
+
},
|
|
59
|
+
blue: {
|
|
60
|
+
light: '#ADD8E6',
|
|
61
|
+
dark: '#00008B',
|
|
62
|
+
sky: '#87CEEB',
|
|
63
|
+
navy: '#000080',
|
|
64
|
+
royal: '#4169E1',
|
|
65
|
+
deep: '#00008B',
|
|
66
|
+
},
|
|
67
|
+
green: {
|
|
68
|
+
light: '#90EE90',
|
|
69
|
+
dark: '#006400',
|
|
70
|
+
forest: '#228B22',
|
|
71
|
+
lime: '#32CD32',
|
|
72
|
+
mint: '#98FB98',
|
|
73
|
+
olive: '#808000',
|
|
74
|
+
},
|
|
75
|
+
yellow: {
|
|
76
|
+
light: '#FFFFE0',
|
|
77
|
+
dark: '#BDB76B',
|
|
78
|
+
pale: '#FFF9C4',
|
|
79
|
+
gold: '#FFD700',
|
|
80
|
+
lemon: '#FFFACD',
|
|
81
|
+
},
|
|
82
|
+
// Add more variations for other colors as needed
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Extracts a color name from natural language text
|
|
86
|
+
* @param text - Natural language text that contains a color reference
|
|
87
|
+
* @returns The extracted color name or null if no color is found
|
|
88
|
+
*/
|
|
89
|
+
export function extractColorFromText(text) {
|
|
90
|
+
if (!text)
|
|
91
|
+
return null;
|
|
92
|
+
// Convert to lowercase for case-insensitive matching
|
|
93
|
+
const lowercaseText = text.toLowerCase();
|
|
94
|
+
// First check for color variations (e.g., "dark blue", "light green")
|
|
95
|
+
for (const [baseColor, variations] of Object.entries(COLOR_VARIATIONS)) {
|
|
96
|
+
for (const [variation, _] of Object.entries(variations)) {
|
|
97
|
+
const colorPhrase = `${variation} ${baseColor}`;
|
|
98
|
+
if (lowercaseText.includes(colorPhrase)) {
|
|
99
|
+
return colorPhrase;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Then check for base colors
|
|
104
|
+
for (const color of Object.keys(COLOR_MAP)) {
|
|
105
|
+
// Use word boundary to make sure we're matching whole words
|
|
106
|
+
const regex = new RegExp(`\\b${color}\\b`, 'i');
|
|
107
|
+
if (regex.test(lowercaseText)) {
|
|
108
|
+
return color;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Converts a color name to its HEX value
|
|
115
|
+
* @param colorName - Name of the color to convert (e.g., "blue", "dark red")
|
|
116
|
+
* @returns HEX color code or null if color name is not recognized
|
|
117
|
+
*/
|
|
118
|
+
export function colorNameToHex(colorName) {
|
|
119
|
+
if (!colorName)
|
|
120
|
+
return null;
|
|
121
|
+
const lowercaseColor = colorName.toLowerCase();
|
|
122
|
+
// Check if it's a color variation (e.g., "dark blue")
|
|
123
|
+
const parts = lowercaseColor.split(' ');
|
|
124
|
+
if (parts.length === 2) {
|
|
125
|
+
const variation = parts[0];
|
|
126
|
+
const baseColor = parts[1];
|
|
127
|
+
if (COLOR_VARIATIONS[baseColor] && COLOR_VARIATIONS[baseColor][variation]) {
|
|
128
|
+
return COLOR_VARIATIONS[baseColor][variation];
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// Check if it's a base color
|
|
132
|
+
return COLOR_MAP[lowercaseColor] || null;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Calculates the relative luminance of a color for WCAG contrast calculations
|
|
136
|
+
* @param hex - HEX color code
|
|
137
|
+
* @returns Relative luminance value
|
|
138
|
+
*/
|
|
139
|
+
function calculateLuminance(hex) {
|
|
140
|
+
// Remove # if present
|
|
141
|
+
const color = hex.startsWith('#') ? hex.slice(1) : hex;
|
|
142
|
+
// Convert HEX to RGB
|
|
143
|
+
const r = parseInt(color.substr(0, 2), 16) / 255;
|
|
144
|
+
const g = parseInt(color.substr(2, 2), 16) / 255;
|
|
145
|
+
const b = parseInt(color.substr(4, 2), 16) / 255;
|
|
146
|
+
// Calculate luminance using the formula from WCAG 2.0
|
|
147
|
+
const R = r <= 0.03928 ? r / 12.92 : Math.pow((r + 0.055) / 1.055, 2.4);
|
|
148
|
+
const G = g <= 0.03928 ? g / 12.92 : Math.pow((g + 0.055) / 1.055, 2.4);
|
|
149
|
+
const B = b <= 0.03928 ? b / 12.92 : Math.pow((b + 0.055) / 1.055, 2.4);
|
|
150
|
+
return 0.2126 * R + 0.7152 * G + 0.0722 * B;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Generates a contrasting foreground color for optimal readability
|
|
154
|
+
* @param backgroundColor - HEX code of the background color
|
|
155
|
+
* @returns HEX code of the foreground color (either black or white)
|
|
156
|
+
*/
|
|
157
|
+
export function generateContrastingForeground(backgroundColor) {
|
|
158
|
+
const luminance = calculateLuminance(backgroundColor);
|
|
159
|
+
// Use white text on dark backgrounds and black text on light backgrounds
|
|
160
|
+
// The threshold 0.5 is based on WCAG guidelines for contrast
|
|
161
|
+
return luminance > 0.5 ? '#000000' : '#FFFFFF';
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Processes a natural language command to extract a color and convert it to HEX values
|
|
165
|
+
* @param command - Natural language command (e.g., "Create a blue tag")
|
|
166
|
+
* @returns Object containing background and foreground HEX colors, or null if color not recognized
|
|
167
|
+
*/
|
|
168
|
+
export function processColorCommand(command) {
|
|
169
|
+
// Extract color name from command
|
|
170
|
+
const colorName = extractColorFromText(command);
|
|
171
|
+
if (!colorName)
|
|
172
|
+
return null;
|
|
173
|
+
// Convert color name to HEX background color
|
|
174
|
+
const backgroundColor = colorNameToHex(colorName);
|
|
175
|
+
if (!backgroundColor)
|
|
176
|
+
return null;
|
|
177
|
+
// Generate appropriate foreground color
|
|
178
|
+
const foregroundColor = generateContrastingForeground(backgroundColor);
|
|
179
|
+
return {
|
|
180
|
+
background: backgroundColor,
|
|
181
|
+
foreground: foregroundColor
|
|
182
|
+
};
|
|
183
|
+
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* SPDX-FileCopyrightText: © 2025 Talib Kareem <taazkareem@icloud.com>
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*
|
|
2
5
|
* Sponsor Service Module
|
|
3
6
|
*
|
|
4
7
|
* Provides configuration and utilities for sponsorship functionality
|
|
@@ -12,8 +15,8 @@ const logger = new Logger('SponsorService');
|
|
|
12
15
|
*/
|
|
13
16
|
export class SponsorService {
|
|
14
17
|
constructor() {
|
|
18
|
+
this.sponsorUrl = 'https://github.com/sponsors/taazkareem';
|
|
15
19
|
this.isEnabled = config.enableSponsorMessage;
|
|
16
|
-
this.sponsorUrl = config.sponsorUrl;
|
|
17
20
|
logger.info('SponsorService initialized', { enabled: this.isEnabled });
|
|
18
21
|
}
|
|
19
22
|
/**
|
|
@@ -56,7 +59,7 @@ export class SponsorService {
|
|
|
56
59
|
if (this.isEnabled && includeSponsorMessage) {
|
|
57
60
|
content.push({
|
|
58
61
|
type: "text",
|
|
59
|
-
text:
|
|
62
|
+
text: `⎯ Support this project by sponsoring the developer at ${this.sponsorUrl}`
|
|
60
63
|
});
|
|
61
64
|
}
|
|
62
65
|
return { content };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPDX-FileCopyrightText: © 2025 Talib Kareem <taazkareem@icloud.com>
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*
|
|
5
|
+
* Token Utilities
|
|
6
|
+
*
|
|
7
|
+
* Functions for estimating token counts for LLM processing
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Simple heuristic to estimate token count from text
|
|
11
|
+
* Based on the approximate ratio of 4 characters per token for English text
|
|
12
|
+
*
|
|
13
|
+
* @param text Text to estimate tokens for
|
|
14
|
+
* @returns Estimated token count
|
|
15
|
+
*/
|
|
16
|
+
export function estimateTokensFromText(text) {
|
|
17
|
+
if (!text)
|
|
18
|
+
return 0;
|
|
19
|
+
// Characters per token varies by language, but ~4 chars per token
|
|
20
|
+
// is a reasonable approximation for English text
|
|
21
|
+
const CHARS_PER_TOKEN = 4;
|
|
22
|
+
// Add some overhead for non-text elements and special tokens
|
|
23
|
+
const OVERHEAD_FACTOR = 1.1;
|
|
24
|
+
return Math.ceil((text.length / CHARS_PER_TOKEN) * OVERHEAD_FACTOR);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Estimate tokens for a JSON object
|
|
28
|
+
*
|
|
29
|
+
* @param obj Object to estimate tokens for
|
|
30
|
+
* @returns Estimated token count
|
|
31
|
+
*/
|
|
32
|
+
export function estimateTokensFromObject(obj) {
|
|
33
|
+
// Convert to JSON string
|
|
34
|
+
const jsonString = JSON.stringify(obj);
|
|
35
|
+
// Use text estimation on the JSON string
|
|
36
|
+
// JSON has more special chars than plain text, so we adjust overhead
|
|
37
|
+
return Math.ceil(estimateTokensFromText(jsonString) * 1.2);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Check if an object would exceed the token limit when serialized to JSON
|
|
41
|
+
*
|
|
42
|
+
* @param obj Object to check
|
|
43
|
+
* @param tokenLimit Token limit to check against
|
|
44
|
+
* @returns Whether the object would exceed the token limit
|
|
45
|
+
*/
|
|
46
|
+
export function wouldExceedTokenLimit(obj, tokenLimit) {
|
|
47
|
+
const estimatedTokens = estimateTokensFromObject(obj);
|
|
48
|
+
return estimatedTokens > tokenLimit;
|
|
49
|
+
}
|
package/package.json
CHANGED