@alanse/clickup-multi-mcp-server 1.0.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/Dockerfile +38 -0
- package/LICENSE +21 -0
- package/README.md +470 -0
- package/build/config.js +237 -0
- package/build/index.js +87 -0
- package/build/logger.js +163 -0
- package/build/middleware/security.js +231 -0
- package/build/server.js +288 -0
- package/build/services/clickup/base.js +432 -0
- package/build/services/clickup/bulk.js +180 -0
- package/build/services/clickup/document.js +159 -0
- package/build/services/clickup/folder.js +136 -0
- package/build/services/clickup/index.js +76 -0
- package/build/services/clickup/list.js +191 -0
- package/build/services/clickup/tag.js +239 -0
- package/build/services/clickup/task/index.js +32 -0
- package/build/services/clickup/task/task-attachments.js +105 -0
- package/build/services/clickup/task/task-comments.js +114 -0
- package/build/services/clickup/task/task-core.js +604 -0
- package/build/services/clickup/task/task-custom-fields.js +107 -0
- package/build/services/clickup/task/task-search.js +986 -0
- package/build/services/clickup/task/task-service.js +104 -0
- package/build/services/clickup/task/task-tags.js +113 -0
- package/build/services/clickup/time.js +244 -0
- package/build/services/clickup/types.js +33 -0
- package/build/services/clickup/workspace.js +397 -0
- package/build/services/shared.js +61 -0
- package/build/sse_server.js +277 -0
- package/build/tools/documents.js +489 -0
- package/build/tools/folder.js +331 -0
- package/build/tools/index.js +16 -0
- package/build/tools/list.js +428 -0
- package/build/tools/member.js +106 -0
- package/build/tools/tag.js +833 -0
- package/build/tools/task/attachments.js +357 -0
- package/build/tools/task/attachments.types.js +9 -0
- package/build/tools/task/bulk-operations.js +338 -0
- package/build/tools/task/handlers.js +919 -0
- package/build/tools/task/index.js +30 -0
- package/build/tools/task/main.js +233 -0
- package/build/tools/task/single-operations.js +469 -0
- package/build/tools/task/time-tracking.js +575 -0
- package/build/tools/task/utilities.js +310 -0
- package/build/tools/task/workspace-operations.js +258 -0
- package/build/tools/tool-enhancer.js +37 -0
- package/build/tools/utils.js +12 -0
- package/build/tools/workspace-helper.js +44 -0
- package/build/tools/workspace.js +73 -0
- package/build/utils/color-processor.js +183 -0
- package/build/utils/concurrency-utils.js +248 -0
- package/build/utils/date-utils.js +542 -0
- package/build/utils/resolver-utils.js +135 -0
- package/build/utils/sponsor-service.js +93 -0
- package/build/utils/token-utils.js +49 -0
- package/package.json +77 -0
- package/smithery.yaml +23 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPDX-FileCopyrightText: © 2025 Talib Kareem <taazkareem@icloud.com>
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*
|
|
5
|
+
* Security Middleware for ClickUp MCP Server
|
|
6
|
+
*
|
|
7
|
+
* This module provides optional security enhancements that can be enabled
|
|
8
|
+
* without breaking existing functionality. All security features are opt-in
|
|
9
|
+
* to maintain backwards compatibility.
|
|
10
|
+
*/
|
|
11
|
+
import rateLimit from 'express-rate-limit';
|
|
12
|
+
import cors from 'cors';
|
|
13
|
+
import config from '../config.js';
|
|
14
|
+
import { Logger } from '../logger.js';
|
|
15
|
+
const logger = new Logger('Security');
|
|
16
|
+
/**
|
|
17
|
+
* Origin validation middleware - validates Origin header against whitelist
|
|
18
|
+
* Only enabled when ENABLE_ORIGIN_VALIDATION=true
|
|
19
|
+
*/
|
|
20
|
+
export function createOriginValidationMiddleware() {
|
|
21
|
+
return (req, res, next) => {
|
|
22
|
+
if (!config.enableOriginValidation) {
|
|
23
|
+
next();
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const origin = req.headers.origin;
|
|
27
|
+
const referer = req.headers.referer;
|
|
28
|
+
// For non-browser requests (like n8n, MCP Inspector), origin might be undefined
|
|
29
|
+
// In such cases, we allow the request but log it for monitoring
|
|
30
|
+
if (!origin && !referer) {
|
|
31
|
+
logger.debug('Request without Origin/Referer header - allowing (likely non-browser client)', {
|
|
32
|
+
userAgent: req.headers['user-agent'],
|
|
33
|
+
ip: req.ip,
|
|
34
|
+
path: req.path
|
|
35
|
+
});
|
|
36
|
+
next();
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
// Check if origin is in allowed list
|
|
40
|
+
if (origin && !config.allowedOrigins.includes(origin)) {
|
|
41
|
+
logger.warn('Blocked request from unauthorized origin', {
|
|
42
|
+
origin,
|
|
43
|
+
ip: req.ip,
|
|
44
|
+
path: req.path,
|
|
45
|
+
userAgent: req.headers['user-agent']
|
|
46
|
+
});
|
|
47
|
+
res.status(403).json({
|
|
48
|
+
jsonrpc: '2.0',
|
|
49
|
+
error: {
|
|
50
|
+
code: -32000,
|
|
51
|
+
message: 'Forbidden: Origin not allowed'
|
|
52
|
+
},
|
|
53
|
+
id: null
|
|
54
|
+
});
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
// If referer is present, validate it too
|
|
58
|
+
if (referer) {
|
|
59
|
+
try {
|
|
60
|
+
const refererOrigin = new URL(referer).origin;
|
|
61
|
+
if (!config.allowedOrigins.includes(refererOrigin)) {
|
|
62
|
+
logger.warn('Blocked request from unauthorized referer', {
|
|
63
|
+
referer,
|
|
64
|
+
refererOrigin,
|
|
65
|
+
ip: req.ip,
|
|
66
|
+
path: req.path
|
|
67
|
+
});
|
|
68
|
+
res.status(403).json({
|
|
69
|
+
jsonrpc: '2.0',
|
|
70
|
+
error: {
|
|
71
|
+
code: -32000,
|
|
72
|
+
message: 'Forbidden: Referer not allowed'
|
|
73
|
+
},
|
|
74
|
+
id: null
|
|
75
|
+
});
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
logger.warn('Invalid referer URL', { referer, error: error.message });
|
|
81
|
+
// Continue processing if referer is malformed
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
logger.debug('Origin validation passed', { origin, referer });
|
|
85
|
+
next();
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Rate limiting middleware - protects against DoS attacks
|
|
90
|
+
* Only enabled when ENABLE_RATE_LIMIT=true
|
|
91
|
+
*/
|
|
92
|
+
export function createRateLimitMiddleware() {
|
|
93
|
+
if (!config.enableRateLimit) {
|
|
94
|
+
return (_req, _res, next) => next();
|
|
95
|
+
}
|
|
96
|
+
return rateLimit({
|
|
97
|
+
windowMs: config.rateLimitWindowMs,
|
|
98
|
+
max: config.rateLimitMax,
|
|
99
|
+
message: {
|
|
100
|
+
jsonrpc: '2.0',
|
|
101
|
+
error: {
|
|
102
|
+
code: -32000,
|
|
103
|
+
message: 'Too many requests, please try again later'
|
|
104
|
+
},
|
|
105
|
+
id: null
|
|
106
|
+
},
|
|
107
|
+
standardHeaders: true,
|
|
108
|
+
legacyHeaders: false,
|
|
109
|
+
handler: (req, res) => {
|
|
110
|
+
logger.warn('Rate limit exceeded', {
|
|
111
|
+
ip: req.ip,
|
|
112
|
+
path: req.path,
|
|
113
|
+
userAgent: req.headers['user-agent']
|
|
114
|
+
});
|
|
115
|
+
res.status(429).json({
|
|
116
|
+
jsonrpc: '2.0',
|
|
117
|
+
error: {
|
|
118
|
+
code: -32000,
|
|
119
|
+
message: 'Too many requests, please try again later'
|
|
120
|
+
},
|
|
121
|
+
id: null
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* CORS middleware - configures cross-origin resource sharing
|
|
128
|
+
* Only enabled when ENABLE_CORS=true
|
|
129
|
+
*/
|
|
130
|
+
export function createCorsMiddleware() {
|
|
131
|
+
if (!config.enableCors) {
|
|
132
|
+
return (_req, _res, next) => next();
|
|
133
|
+
}
|
|
134
|
+
return cors({
|
|
135
|
+
origin: (origin, callback) => {
|
|
136
|
+
// Allow requests with no origin (like mobile apps, Postman, etc.)
|
|
137
|
+
if (!origin)
|
|
138
|
+
return callback(null, true);
|
|
139
|
+
if (config.allowedOrigins.includes(origin)) {
|
|
140
|
+
callback(null, true);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
logger.warn('CORS blocked origin', { origin });
|
|
144
|
+
callback(new Error('Not allowed by CORS'));
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
credentials: true,
|
|
148
|
+
methods: ['GET', 'POST', 'DELETE', 'OPTIONS'],
|
|
149
|
+
allowedHeaders: ['Content-Type', 'mcp-session-id', 'Authorization'],
|
|
150
|
+
exposedHeaders: ['mcp-session-id']
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Security headers middleware - adds security-related HTTP headers
|
|
155
|
+
* Only enabled when ENABLE_SECURITY_FEATURES=true
|
|
156
|
+
*/
|
|
157
|
+
export function createSecurityHeadersMiddleware() {
|
|
158
|
+
return (req, res, next) => {
|
|
159
|
+
if (!config.enableSecurityFeatures) {
|
|
160
|
+
return next();
|
|
161
|
+
}
|
|
162
|
+
// Add security headers
|
|
163
|
+
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
164
|
+
res.setHeader('X-Frame-Options', 'DENY');
|
|
165
|
+
res.setHeader('X-XSS-Protection', '1; mode=block');
|
|
166
|
+
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
|
|
167
|
+
// Only add HSTS for HTTPS
|
|
168
|
+
if (req.secure || req.headers['x-forwarded-proto'] === 'https') {
|
|
169
|
+
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
|
|
170
|
+
}
|
|
171
|
+
logger.debug('Security headers applied');
|
|
172
|
+
next();
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Request logging middleware for security monitoring
|
|
177
|
+
*/
|
|
178
|
+
export function createSecurityLoggingMiddleware() {
|
|
179
|
+
return (req, res, next) => {
|
|
180
|
+
if (!config.enableSecurityFeatures) {
|
|
181
|
+
return next();
|
|
182
|
+
}
|
|
183
|
+
const startTime = Date.now();
|
|
184
|
+
res.on('finish', () => {
|
|
185
|
+
const duration = Date.now() - startTime;
|
|
186
|
+
const logData = {
|
|
187
|
+
method: req.method,
|
|
188
|
+
path: req.path,
|
|
189
|
+
statusCode: res.statusCode,
|
|
190
|
+
duration,
|
|
191
|
+
ip: req.ip,
|
|
192
|
+
userAgent: req.headers['user-agent'],
|
|
193
|
+
origin: req.headers.origin,
|
|
194
|
+
sessionId: req.headers['mcp-session-id']
|
|
195
|
+
};
|
|
196
|
+
if (res.statusCode >= 400) {
|
|
197
|
+
logger.warn('HTTP error response', logData);
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
logger.debug('HTTP request completed', logData);
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
next();
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Input validation middleware - validates request size and content
|
|
208
|
+
*/
|
|
209
|
+
export function createInputValidationMiddleware() {
|
|
210
|
+
return (req, res, next) => {
|
|
211
|
+
// Always enforce reasonable request size limits
|
|
212
|
+
const contentLength = req.headers['content-length'];
|
|
213
|
+
if (contentLength && parseInt(contentLength) > 50 * 1024 * 1024) { // 50MB hard limit
|
|
214
|
+
logger.warn('Request too large', {
|
|
215
|
+
contentLength,
|
|
216
|
+
ip: req.ip,
|
|
217
|
+
path: req.path
|
|
218
|
+
});
|
|
219
|
+
res.status(413).json({
|
|
220
|
+
jsonrpc: '2.0',
|
|
221
|
+
error: {
|
|
222
|
+
code: -32000,
|
|
223
|
+
message: 'Request entity too large'
|
|
224
|
+
},
|
|
225
|
+
id: null
|
|
226
|
+
});
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
next();
|
|
230
|
+
};
|
|
231
|
+
}
|
package/build/server.js
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPDX-FileCopyrightText: © 2025 Talib Kareem <taazkareem@icloud.com>
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*
|
|
5
|
+
* MCP Server for ClickUp integration
|
|
6
|
+
*/
|
|
7
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
8
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, ListResourcesRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
9
|
+
import config from "./config.js";
|
|
10
|
+
import { workspaceHierarchyTool, handleGetWorkspaceHierarchy } from "./tools/workspace.js";
|
|
11
|
+
import { createTaskTool, updateTaskTool, moveTaskTool, duplicateTaskTool, getTaskTool, deleteTaskTool, getTaskCommentsTool, createTaskCommentTool, createBulkTasksTool, updateBulkTasksTool, moveBulkTasksTool, deleteBulkTasksTool, attachTaskFileTool, getWorkspaceTasksTool, getTaskTimeEntriesTool, startTimeTrackingTool, stopTimeTrackingTool, addTimeEntryTool, deleteTimeEntryTool, getCurrentTimeEntryTool, handleCreateTask, handleUpdateTask, handleMoveTask, handleDuplicateTask, handleDeleteTask, handleGetTaskComments, handleCreateTaskComment, handleCreateBulkTasks, handleUpdateBulkTasks, handleMoveBulkTasks, handleDeleteBulkTasks, handleGetTask, handleAttachTaskFile, handleGetWorkspaceTasks, handleGetTaskTimeEntries, handleStartTimeTracking, handleStopTimeTracking, handleAddTimeEntry, handleDeleteTimeEntry, handleGetCurrentTimeEntry } from "./tools/task/index.js";
|
|
12
|
+
import { createListTool, handleCreateList, createListInFolderTool, handleCreateListInFolder, getListTool, handleGetList, updateListTool, handleUpdateList, deleteListTool, handleDeleteList } from "./tools/list.js";
|
|
13
|
+
import { createFolderTool, handleCreateFolder, getFolderTool, handleGetFolder, updateFolderTool, handleUpdateFolder, deleteFolderTool, handleDeleteFolder } from "./tools/folder.js";
|
|
14
|
+
import { getSpaceTagsTool, handleGetSpaceTags, addTagToTaskTool, handleAddTagToTask, removeTagFromTaskTool, handleRemoveTagFromTask } from "./tools/tag.js";
|
|
15
|
+
import { createDocumentTool, handleCreateDocument, getDocumentTool, handleGetDocument, listDocumentsTool, handleListDocuments, listDocumentPagesTool, handleListDocumentPages, getDocumentPagesTool, handleGetDocumentPages, createDocumentPageTool, handleCreateDocumentPage, updateDocumentPageTool, handleUpdateDocumentPage } from "./tools/documents.js";
|
|
16
|
+
import { getWorkspaceMembersTool, handleGetWorkspaceMembers, findMemberByNameTool, handleFindMemberByName, resolveAssigneesTool, handleResolveAssignees } from "./tools/member.js";
|
|
17
|
+
import { Logger } from "./logger.js";
|
|
18
|
+
import { clickUpServices } from "./services/shared.js";
|
|
19
|
+
import { enhanceToolsWithWorkspace } from "./tools/tool-enhancer.js";
|
|
20
|
+
// Create a logger instance for server
|
|
21
|
+
const logger = new Logger('Server');
|
|
22
|
+
// Use existing services from shared module instead of creating new ones
|
|
23
|
+
const { workspace } = clickUpServices;
|
|
24
|
+
/**
|
|
25
|
+
* Determines if a tool should be enabled based on ENABLED_TOOLS and DISABLED_TOOLS configuration.
|
|
26
|
+
*
|
|
27
|
+
* Logic:
|
|
28
|
+
* 1. If ENABLED_TOOLS is specified, only tools in that list are enabled (ENABLED_TOOLS takes precedence)
|
|
29
|
+
* 2. If ENABLED_TOOLS is not specified but DISABLED_TOOLS is, all tools except those in DISABLED_TOOLS are enabled
|
|
30
|
+
* 3. If neither is specified, all tools are enabled
|
|
31
|
+
*
|
|
32
|
+
* @param toolName - The name of the tool to check
|
|
33
|
+
* @returns true if the tool should be enabled, false otherwise
|
|
34
|
+
*/
|
|
35
|
+
const isToolEnabled = (toolName) => {
|
|
36
|
+
// If ENABLED_TOOLS is specified, it takes precedence
|
|
37
|
+
if (config.enabledTools.length > 0) {
|
|
38
|
+
return config.enabledTools.includes(toolName);
|
|
39
|
+
}
|
|
40
|
+
// If only DISABLED_TOOLS is specified, enable all tools except those disabled
|
|
41
|
+
if (config.disabledTools.length > 0) {
|
|
42
|
+
return !config.disabledTools.includes(toolName);
|
|
43
|
+
}
|
|
44
|
+
// If neither is specified, enable all tools
|
|
45
|
+
return true;
|
|
46
|
+
};
|
|
47
|
+
export const server = new Server({
|
|
48
|
+
name: "clickup-mcp-server",
|
|
49
|
+
version: "0.8.5",
|
|
50
|
+
}, {
|
|
51
|
+
capabilities: {
|
|
52
|
+
tools: {},
|
|
53
|
+
prompts: {},
|
|
54
|
+
resources: {},
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
const documentModule = () => {
|
|
58
|
+
if (config.documentSupport === 'true') {
|
|
59
|
+
return [
|
|
60
|
+
createDocumentTool,
|
|
61
|
+
getDocumentTool,
|
|
62
|
+
listDocumentsTool,
|
|
63
|
+
listDocumentPagesTool,
|
|
64
|
+
getDocumentPagesTool,
|
|
65
|
+
createDocumentPageTool,
|
|
66
|
+
updateDocumentPageTool,
|
|
67
|
+
];
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Configure the server routes and handlers
|
|
75
|
+
*/
|
|
76
|
+
export function configureServer() {
|
|
77
|
+
logger.info("Registering server request handlers");
|
|
78
|
+
// Register ListTools handler
|
|
79
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
80
|
+
logger.debug("Received ListTools request");
|
|
81
|
+
// Collect all tool definitions
|
|
82
|
+
const allTools = [
|
|
83
|
+
workspaceHierarchyTool,
|
|
84
|
+
createTaskTool,
|
|
85
|
+
getTaskTool,
|
|
86
|
+
updateTaskTool,
|
|
87
|
+
moveTaskTool,
|
|
88
|
+
duplicateTaskTool,
|
|
89
|
+
deleteTaskTool,
|
|
90
|
+
getTaskCommentsTool,
|
|
91
|
+
createTaskCommentTool,
|
|
92
|
+
attachTaskFileTool,
|
|
93
|
+
createBulkTasksTool,
|
|
94
|
+
updateBulkTasksTool,
|
|
95
|
+
moveBulkTasksTool,
|
|
96
|
+
deleteBulkTasksTool,
|
|
97
|
+
getWorkspaceTasksTool,
|
|
98
|
+
getTaskTimeEntriesTool,
|
|
99
|
+
startTimeTrackingTool,
|
|
100
|
+
stopTimeTrackingTool,
|
|
101
|
+
addTimeEntryTool,
|
|
102
|
+
deleteTimeEntryTool,
|
|
103
|
+
getCurrentTimeEntryTool,
|
|
104
|
+
createListTool,
|
|
105
|
+
createListInFolderTool,
|
|
106
|
+
getListTool,
|
|
107
|
+
updateListTool,
|
|
108
|
+
deleteListTool,
|
|
109
|
+
createFolderTool,
|
|
110
|
+
getFolderTool,
|
|
111
|
+
updateFolderTool,
|
|
112
|
+
deleteFolderTool,
|
|
113
|
+
getSpaceTagsTool,
|
|
114
|
+
addTagToTaskTool,
|
|
115
|
+
removeTagFromTaskTool,
|
|
116
|
+
getWorkspaceMembersTool,
|
|
117
|
+
findMemberByNameTool,
|
|
118
|
+
resolveAssigneesTool,
|
|
119
|
+
...documentModule()
|
|
120
|
+
];
|
|
121
|
+
// Enhance all tools with workspace parameter support
|
|
122
|
+
const enhancedTools = enhanceToolsWithWorkspace(allTools);
|
|
123
|
+
// Filter based on enabled/disabled tools configuration
|
|
124
|
+
return {
|
|
125
|
+
tools: enhancedTools.filter(tool => isToolEnabled(tool.name))
|
|
126
|
+
};
|
|
127
|
+
});
|
|
128
|
+
// Add handler for resources/list
|
|
129
|
+
server.setRequestHandler(ListResourcesRequestSchema, async (req) => {
|
|
130
|
+
logger.debug("Received ListResources request");
|
|
131
|
+
return { resources: [] };
|
|
132
|
+
});
|
|
133
|
+
// Register CallTool handler with proper logging
|
|
134
|
+
logger.info("Registering tool handlers", {
|
|
135
|
+
toolCount: 36,
|
|
136
|
+
categories: ["workspace", "task", "time-tracking", "list", "folder", "tag", "member", "document"]
|
|
137
|
+
});
|
|
138
|
+
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
139
|
+
const { name, arguments: params } = req.params;
|
|
140
|
+
// Improved logging with more context
|
|
141
|
+
logger.info(`Received CallTool request for tool: ${name}`, {
|
|
142
|
+
params
|
|
143
|
+
});
|
|
144
|
+
// Check if the tool is enabled
|
|
145
|
+
if (!isToolEnabled(name)) {
|
|
146
|
+
const reason = config.enabledTools.length > 0
|
|
147
|
+
? `Tool '${name}' is not in the enabled tools list.`
|
|
148
|
+
: `Tool '${name}' is disabled.`;
|
|
149
|
+
logger.warn(`Tool execution blocked: ${reason}`);
|
|
150
|
+
throw {
|
|
151
|
+
code: -32601,
|
|
152
|
+
message: reason
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
// Handle tool calls by routing to the appropriate handler
|
|
157
|
+
switch (name) {
|
|
158
|
+
case "get_workspace_hierarchy":
|
|
159
|
+
return handleGetWorkspaceHierarchy();
|
|
160
|
+
case "create_task":
|
|
161
|
+
return handleCreateTask(params);
|
|
162
|
+
case "update_task":
|
|
163
|
+
return handleUpdateTask(params);
|
|
164
|
+
case "move_task":
|
|
165
|
+
return handleMoveTask(params);
|
|
166
|
+
case "duplicate_task":
|
|
167
|
+
return handleDuplicateTask(params);
|
|
168
|
+
case "get_task":
|
|
169
|
+
return handleGetTask(params);
|
|
170
|
+
case "delete_task":
|
|
171
|
+
return handleDeleteTask(params);
|
|
172
|
+
case "get_task_comments":
|
|
173
|
+
return handleGetTaskComments(params);
|
|
174
|
+
case "create_task_comment":
|
|
175
|
+
return handleCreateTaskComment(params);
|
|
176
|
+
case "attach_task_file":
|
|
177
|
+
return handleAttachTaskFile(params);
|
|
178
|
+
case "create_bulk_tasks":
|
|
179
|
+
return handleCreateBulkTasks(params);
|
|
180
|
+
case "update_bulk_tasks":
|
|
181
|
+
return handleUpdateBulkTasks(params);
|
|
182
|
+
case "move_bulk_tasks":
|
|
183
|
+
return handleMoveBulkTasks(params);
|
|
184
|
+
case "delete_bulk_tasks":
|
|
185
|
+
return handleDeleteBulkTasks(params);
|
|
186
|
+
case "get_workspace_tasks":
|
|
187
|
+
return handleGetWorkspaceTasks(params);
|
|
188
|
+
case "create_list":
|
|
189
|
+
return handleCreateList(params);
|
|
190
|
+
case "create_list_in_folder":
|
|
191
|
+
return handleCreateListInFolder(params);
|
|
192
|
+
case "get_list":
|
|
193
|
+
return handleGetList(params);
|
|
194
|
+
case "update_list":
|
|
195
|
+
return handleUpdateList(params);
|
|
196
|
+
case "delete_list":
|
|
197
|
+
return handleDeleteList(params);
|
|
198
|
+
case "create_folder":
|
|
199
|
+
return handleCreateFolder(params);
|
|
200
|
+
case "get_folder":
|
|
201
|
+
return handleGetFolder(params);
|
|
202
|
+
case "update_folder":
|
|
203
|
+
return handleUpdateFolder(params);
|
|
204
|
+
case "delete_folder":
|
|
205
|
+
return handleDeleteFolder(params);
|
|
206
|
+
case "get_space_tags":
|
|
207
|
+
return handleGetSpaceTags(params);
|
|
208
|
+
case "add_tag_to_task":
|
|
209
|
+
return handleAddTagToTask(params);
|
|
210
|
+
case "remove_tag_from_task":
|
|
211
|
+
return handleRemoveTagFromTask(params);
|
|
212
|
+
case "get_task_time_entries":
|
|
213
|
+
return handleGetTaskTimeEntries(params);
|
|
214
|
+
case "start_time_tracking":
|
|
215
|
+
return handleStartTimeTracking(params);
|
|
216
|
+
case "stop_time_tracking":
|
|
217
|
+
return handleStopTimeTracking(params);
|
|
218
|
+
case "add_time_entry":
|
|
219
|
+
return handleAddTimeEntry(params);
|
|
220
|
+
case "delete_time_entry":
|
|
221
|
+
return handleDeleteTimeEntry(params);
|
|
222
|
+
case "get_current_time_entry":
|
|
223
|
+
return handleGetCurrentTimeEntry(params);
|
|
224
|
+
case "create_document":
|
|
225
|
+
return handleCreateDocument(params);
|
|
226
|
+
case "get_document":
|
|
227
|
+
return handleGetDocument(params);
|
|
228
|
+
case "list_documents":
|
|
229
|
+
return handleListDocuments(params);
|
|
230
|
+
case "list_document_pages":
|
|
231
|
+
return handleListDocumentPages(params);
|
|
232
|
+
case "get_document_pages":
|
|
233
|
+
return handleGetDocumentPages(params);
|
|
234
|
+
case "create_document_page":
|
|
235
|
+
return handleCreateDocumentPage(params);
|
|
236
|
+
case "update_document_page":
|
|
237
|
+
return handleUpdateDocumentPage(params);
|
|
238
|
+
case "get_workspace_members":
|
|
239
|
+
return handleGetWorkspaceMembers();
|
|
240
|
+
case "find_member_by_name":
|
|
241
|
+
return handleFindMemberByName(params);
|
|
242
|
+
case "resolve_assignees":
|
|
243
|
+
return handleResolveAssignees(params);
|
|
244
|
+
default:
|
|
245
|
+
logger.error(`Unknown tool requested: ${name}`);
|
|
246
|
+
const error = new Error(`Unknown tool: ${name}`);
|
|
247
|
+
error.name = "UnknownToolError";
|
|
248
|
+
throw error;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
catch (err) {
|
|
252
|
+
logger.error(`Error executing tool: ${name}`, err);
|
|
253
|
+
// Transform error to a more descriptive JSON-RPC error
|
|
254
|
+
if (err.name === "UnknownToolError") {
|
|
255
|
+
throw {
|
|
256
|
+
code: -32601,
|
|
257
|
+
message: `Method not found: ${name}`
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
else if (err.name === "ValidationError") {
|
|
261
|
+
throw {
|
|
262
|
+
code: -32602,
|
|
263
|
+
message: `Invalid params for tool ${name}: ${err.message}`
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
// Generic server error
|
|
268
|
+
throw {
|
|
269
|
+
code: -32000,
|
|
270
|
+
message: `Error executing tool ${name}: ${err.message}`
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
276
|
+
logger.info("Received ListPrompts request");
|
|
277
|
+
return { prompts: [] };
|
|
278
|
+
});
|
|
279
|
+
server.setRequestHandler(GetPromptRequestSchema, async () => {
|
|
280
|
+
logger.error("Received GetPrompt request, but prompts are not supported");
|
|
281
|
+
throw new Error("Prompt not found");
|
|
282
|
+
});
|
|
283
|
+
return server;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Export the clickup service for use in tool handlers
|
|
287
|
+
*/
|
|
288
|
+
export { workspace };
|