@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.
Files changed (56) hide show
  1. package/Dockerfile +38 -0
  2. package/LICENSE +21 -0
  3. package/README.md +470 -0
  4. package/build/config.js +237 -0
  5. package/build/index.js +87 -0
  6. package/build/logger.js +163 -0
  7. package/build/middleware/security.js +231 -0
  8. package/build/server.js +288 -0
  9. package/build/services/clickup/base.js +432 -0
  10. package/build/services/clickup/bulk.js +180 -0
  11. package/build/services/clickup/document.js +159 -0
  12. package/build/services/clickup/folder.js +136 -0
  13. package/build/services/clickup/index.js +76 -0
  14. package/build/services/clickup/list.js +191 -0
  15. package/build/services/clickup/tag.js +239 -0
  16. package/build/services/clickup/task/index.js +32 -0
  17. package/build/services/clickup/task/task-attachments.js +105 -0
  18. package/build/services/clickup/task/task-comments.js +114 -0
  19. package/build/services/clickup/task/task-core.js +604 -0
  20. package/build/services/clickup/task/task-custom-fields.js +107 -0
  21. package/build/services/clickup/task/task-search.js +986 -0
  22. package/build/services/clickup/task/task-service.js +104 -0
  23. package/build/services/clickup/task/task-tags.js +113 -0
  24. package/build/services/clickup/time.js +244 -0
  25. package/build/services/clickup/types.js +33 -0
  26. package/build/services/clickup/workspace.js +397 -0
  27. package/build/services/shared.js +61 -0
  28. package/build/sse_server.js +277 -0
  29. package/build/tools/documents.js +489 -0
  30. package/build/tools/folder.js +331 -0
  31. package/build/tools/index.js +16 -0
  32. package/build/tools/list.js +428 -0
  33. package/build/tools/member.js +106 -0
  34. package/build/tools/tag.js +833 -0
  35. package/build/tools/task/attachments.js +357 -0
  36. package/build/tools/task/attachments.types.js +9 -0
  37. package/build/tools/task/bulk-operations.js +338 -0
  38. package/build/tools/task/handlers.js +919 -0
  39. package/build/tools/task/index.js +30 -0
  40. package/build/tools/task/main.js +233 -0
  41. package/build/tools/task/single-operations.js +469 -0
  42. package/build/tools/task/time-tracking.js +575 -0
  43. package/build/tools/task/utilities.js +310 -0
  44. package/build/tools/task/workspace-operations.js +258 -0
  45. package/build/tools/tool-enhancer.js +37 -0
  46. package/build/tools/utils.js +12 -0
  47. package/build/tools/workspace-helper.js +44 -0
  48. package/build/tools/workspace.js +73 -0
  49. package/build/utils/color-processor.js +183 -0
  50. package/build/utils/concurrency-utils.js +248 -0
  51. package/build/utils/date-utils.js +542 -0
  52. package/build/utils/resolver-utils.js +135 -0
  53. package/build/utils/sponsor-service.js +93 -0
  54. package/build/utils/token-utils.js +49 -0
  55. package/package.json +77 -0
  56. package/smithery.yaml +23 -0
@@ -0,0 +1,489 @@
1
+ /**
2
+ * SPDX-FileCopyrightText: © 2025 João Santana <joaosantana@gmail.com>
3
+ * SPDX-License-Identifier: MIT
4
+ *
5
+ * ClickUp MCP Document Tools
6
+ *
7
+ * This module defines document-related tools including creating,
8
+ * retrieving, updating, and deleting documents.
9
+ */
10
+ import { workspaceService } from '../services/shared.js';
11
+ import config from '../config.js';
12
+ import { sponsorService } from '../utils/sponsor-service.js';
13
+ import { Logger } from "../logger.js";
14
+ import { clickUpServices } from "../services/shared.js";
15
+ const logger = new Logger('DocumentTools');
16
+ const { document: documentService } = clickUpServices;
17
+ /**
18
+ * Tool definition for creating a document
19
+ */
20
+ export const createDocumentTool = {
21
+ name: "create_document",
22
+ description: `Creates a document in a ClickUp space, folder, or list. Requires name, parent info, visibility and create_page flag.
23
+
24
+ Example usage:
25
+ - For list: parent: {"id": "901407953112", "type": 6}
26
+ - For space: parent: {"id": "90141392755", "type": 4}
27
+ - For folder: parent: {"id": "90144231850", "type": 5}
28
+
29
+ Note: Document creation permissions may vary by ClickUp plan and parent container type.`,
30
+ inputSchema: {
31
+ type: "object",
32
+ properties: {
33
+ name: {
34
+ type: "string",
35
+ description: "Name and Title of the document"
36
+ },
37
+ parent: {
38
+ type: "object",
39
+ properties: {
40
+ id: {
41
+ type: "string",
42
+ description: "ID of the parent container (space, folder, or list). Use actual ID from workspace hierarchy."
43
+ },
44
+ type: {
45
+ type: "number",
46
+ enum: [4, 5, 6, 7, 12],
47
+ description: "Type of the parent container: 4=space, 5=folder, 6=list, 7=everything, 12=workspace. Most commonly use 6 for lists."
48
+ }
49
+ },
50
+ required: ["id", "type"],
51
+ description: "Parent container object with id and type properties. Example: {\"id\": \"901407953112\", \"type\": 6}"
52
+ },
53
+ visibility: {
54
+ type: "string",
55
+ enum: ["PUBLIC", "PRIVATE"],
56
+ description: "Document visibility setting"
57
+ },
58
+ create_page: {
59
+ type: "boolean",
60
+ description: "Whether to create an initial blank page"
61
+ }
62
+ },
63
+ required: ["name", "parent", "visibility", "create_page"]
64
+ }
65
+ };
66
+ /**
67
+ * Tool definition for getting a document
68
+ */
69
+ export const getDocumentTool = {
70
+ name: "get_document",
71
+ description: `Gets details of a ClickUp document. Use documentId (preferred) or search by title in a container.`,
72
+ inputSchema: {
73
+ type: "object",
74
+ properties: {
75
+ documentId: {
76
+ type: "string",
77
+ description: "ID of the document to retrieve"
78
+ },
79
+ },
80
+ required: ["documentId"]
81
+ }
82
+ };
83
+ /**
84
+ * Tool definition for listing documents
85
+ */
86
+ export const listDocumentsTool = {
87
+ name: "list_documents",
88
+ description: `Lists all documents in a ClickUp space, folder, or list.`,
89
+ inputSchema: {
90
+ type: "object",
91
+ properties: {
92
+ id: {
93
+ type: "string",
94
+ description: "Optional document ID to filter by"
95
+ },
96
+ creator: {
97
+ type: "number",
98
+ description: "Optional creator ID to filter by"
99
+ },
100
+ deleted: {
101
+ type: "boolean",
102
+ description: "Whether to include deleted documents"
103
+ },
104
+ archived: {
105
+ type: "boolean",
106
+ description: "Whether to include archived documents"
107
+ },
108
+ parent_id: {
109
+ type: "string",
110
+ description: "ID of the parent container to list documents from"
111
+ },
112
+ parent_type: {
113
+ type: "string",
114
+ enum: ["TASK", "SPACE", "FOLDER", "LIST", "EVERYTHING", "WORKSPACE"],
115
+ description: "Type of the parent container"
116
+ },
117
+ limit: {
118
+ type: "number",
119
+ description: "Maximum number of documents to return"
120
+ },
121
+ next_cursor: {
122
+ type: "string",
123
+ description: "Cursor for pagination"
124
+ }
125
+ },
126
+ required: []
127
+ }
128
+ };
129
+ /**
130
+ * Tool definition for listing document pages
131
+ */
132
+ export const listDocumentPagesTool = {
133
+ name: "list_document_pages",
134
+ description: "Lists all pages in a document with optional depth control",
135
+ inputSchema: {
136
+ type: "object",
137
+ properties: {
138
+ documentId: {
139
+ type: "string",
140
+ description: "ID of the document to list pages from"
141
+ },
142
+ max_page_depth: {
143
+ type: "number",
144
+ description: "Maximum depth of pages to retrieve (-1 for unlimited)"
145
+ }
146
+ },
147
+ required: ["documentId"]
148
+ }
149
+ };
150
+ /**
151
+ * Tool definition for getting document pages
152
+ */
153
+ export const getDocumentPagesTool = {
154
+ name: "get_document_pages",
155
+ description: "Gets the content of specific pages from a document",
156
+ inputSchema: {
157
+ type: "object",
158
+ properties: {
159
+ documentId: {
160
+ type: "string",
161
+ description: "ID of the document to get pages from"
162
+ },
163
+ pageIds: {
164
+ type: "array",
165
+ items: {
166
+ type: "string"
167
+ },
168
+ description: "Array of page IDs to retrieve"
169
+ },
170
+ content_format: {
171
+ type: "string",
172
+ enum: ["text/md", "text/html"],
173
+ description: "Format of the content to retrieve"
174
+ }
175
+ },
176
+ required: ["documentId", "pageIds"]
177
+ }
178
+ };
179
+ /**
180
+ * Tool definition for creating a document page
181
+ */
182
+ export const createDocumentPageTool = {
183
+ name: "create_document_page",
184
+ description: "Creates a new page in a ClickUp document",
185
+ inputSchema: {
186
+ type: "object",
187
+ properties: {
188
+ documentId: {
189
+ type: "string",
190
+ description: "ID of the document to create the page in"
191
+ },
192
+ content: {
193
+ type: "string",
194
+ description: "Content of the page"
195
+ },
196
+ name: {
197
+ type: "string",
198
+ description: "Name and title of the page",
199
+ },
200
+ sub_title: {
201
+ type: "string",
202
+ description: "Subtitle of the page"
203
+ },
204
+ parent_page_id: {
205
+ type: "string",
206
+ description: "ID of the parent page (if this is a sub-page)"
207
+ }
208
+ },
209
+ required: ["documentId", "name"]
210
+ }
211
+ };
212
+ /**
213
+ * Tool definition for updating a document page
214
+ */
215
+ export const updateDocumentPageTool = {
216
+ name: "update_document_page",
217
+ description: "Updates an existing page in a ClickUp document. Supports updating name, subtitle, and content with different edit modes (replace/append/prepend).",
218
+ inputSchema: {
219
+ type: "object",
220
+ properties: {
221
+ documentId: {
222
+ type: "string",
223
+ description: "ID of the document containing the page"
224
+ },
225
+ pageId: {
226
+ type: "string",
227
+ description: "ID of the page to update"
228
+ },
229
+ name: {
230
+ type: "string",
231
+ description: "New name for the page"
232
+ },
233
+ sub_title: {
234
+ type: "string",
235
+ description: "New subtitle for the page"
236
+ },
237
+ content: {
238
+ type: "string",
239
+ description: "New content for the page"
240
+ },
241
+ content_edit_mode: {
242
+ type: "string",
243
+ enum: ["replace", "append", "prepend"],
244
+ description: "How to update the content. Defaults to replace"
245
+ },
246
+ content_format: {
247
+ type: "string",
248
+ enum: ["text/md", "text/plain"],
249
+ description: "Format of the content. Defaults to text/md"
250
+ },
251
+ },
252
+ required: ["documentId", "pageId"]
253
+ }
254
+ };
255
+ /**
256
+ * Helper function to find a document by title in a container
257
+ */
258
+ async function findDocumentByTitle(parentId, title) {
259
+ const response = await documentService.listDocuments({
260
+ parent_id: parentId
261
+ });
262
+ const document = response.docs.find(doc => doc.name === title);
263
+ return document ? document.id : null;
264
+ }
265
+ /**
266
+ * Helper function to find parent container ID by name and type
267
+ */
268
+ async function findParentIdByName(name, type) {
269
+ const hierarchy = await workspaceService.getWorkspaceHierarchy();
270
+ const container = workspaceService.findIDByNameInHierarchy(hierarchy, name, type);
271
+ return container ? container.id : null;
272
+ }
273
+ /**
274
+ * Handler for the create_document tool
275
+ */
276
+ export async function handleCreateDocument(parameters) {
277
+ const { name, parent, visibility, create_page } = parameters;
278
+ if (!parent || !visibility || !create_page) {
279
+ return sponsorService.createErrorResponse('Parent, visibility, and create_page are required');
280
+ }
281
+ // Prepare document data
282
+ const documentData = {
283
+ name,
284
+ parent,
285
+ visibility,
286
+ create_page
287
+ };
288
+ try {
289
+ // Create the document
290
+ const newDocument = await clickUpServices.document.createDocument(documentData);
291
+ return sponsorService.createResponse({
292
+ id: newDocument.id,
293
+ name: newDocument.name,
294
+ parent: newDocument.parent,
295
+ url: `https://app.clickup.com/${config.clickupTeamId}/v/d/${newDocument.id}`,
296
+ message: `Document "${name}" created successfully`
297
+ }, true);
298
+ }
299
+ catch (error) {
300
+ return sponsorService.createErrorResponse(`Failed to create document: ${error.message}`);
301
+ }
302
+ }
303
+ /**
304
+ * Handler for the get_document tool
305
+ */
306
+ export async function handleGetDocument(parameters) {
307
+ const { documentId, title, parentId } = parameters;
308
+ let targetDocumentId = documentId;
309
+ // If no documentId but title and parentId are provided, look up the document ID
310
+ if (!targetDocumentId && title && parentId) {
311
+ targetDocumentId = await findDocumentByTitle(parentId, title);
312
+ if (!targetDocumentId) {
313
+ throw new Error(`Document "${title}" not found`);
314
+ }
315
+ }
316
+ if (!targetDocumentId) {
317
+ throw new Error("Either documentId or (title + parentId) must be provided");
318
+ }
319
+ try {
320
+ // Get the document
321
+ const document = await documentService.getDocument(targetDocumentId);
322
+ return sponsorService.createResponse({
323
+ id: document.id,
324
+ name: document.name,
325
+ parent: document.parent,
326
+ created: new Date(document.date_created).toISOString(),
327
+ updated: new Date(document.date_updated).toISOString(),
328
+ creator: document.creator,
329
+ public: document.public,
330
+ type: document.type,
331
+ url: `https://app.clickup.com/${config.clickupTeamId}/v/d/${document.id}`
332
+ }, true);
333
+ }
334
+ catch (error) {
335
+ return sponsorService.createErrorResponse(`Failed to retrieve document: ${error.message}`);
336
+ }
337
+ }
338
+ /**
339
+ * Handler for the list_documents tool
340
+ */
341
+ export async function handleListDocuments(parameters) {
342
+ const { id, creator, deleted, archived, parent_id, parent_type, limit, next_cursor } = parameters;
343
+ try {
344
+ // Prepare options object with all possible parameters
345
+ const options = {};
346
+ // Add each parameter to options only if it's defined
347
+ if (id !== undefined)
348
+ options.id = id;
349
+ if (creator !== undefined)
350
+ options.creator = creator;
351
+ if (deleted !== undefined)
352
+ options.deleted = deleted;
353
+ if (archived !== undefined)
354
+ options.archived = archived;
355
+ if (parent_id !== undefined)
356
+ options.parent_id = parent_id;
357
+ if (parent_type !== undefined)
358
+ options.parent_type = parent_type;
359
+ if (limit !== undefined)
360
+ options.limit = limit;
361
+ if (next_cursor !== undefined)
362
+ options.next_cursor = next_cursor;
363
+ const response = await documentService.listDocuments(options);
364
+ // Ensure we have a valid response
365
+ if (!response || !response.docs) {
366
+ return sponsorService.createResponse({
367
+ documents: [],
368
+ message: "No documents found"
369
+ }, true);
370
+ }
371
+ // Map the documents to a simpler format
372
+ const documents = response.docs.map(doc => ({
373
+ id: doc.id,
374
+ name: doc.name,
375
+ url: `https://app.clickup.com/${config.clickupTeamId}/v/d/${doc.id}`,
376
+ parent: doc.parent,
377
+ created: new Date(doc.date_created).toISOString(),
378
+ updated: new Date(doc.date_updated).toISOString(),
379
+ creator: doc.creator,
380
+ public: doc.public,
381
+ type: doc.type
382
+ }));
383
+ return sponsorService.createResponse({
384
+ documents,
385
+ count: documents.length,
386
+ next_cursor: response.next_cursor,
387
+ message: `Found ${documents.length} document(s)`
388
+ }, true);
389
+ }
390
+ catch (error) {
391
+ return sponsorService.createErrorResponse(`Failed to list documents: ${error.message}`);
392
+ }
393
+ }
394
+ /**
395
+ * Handler for listing document pages
396
+ */
397
+ export async function handleListDocumentPages(params) {
398
+ logger.info('Listing document pages', { params });
399
+ try {
400
+ const { documentId, max_page_depth = -1 } = params;
401
+ const pages = await documentService.listDocumentPages(documentId, { max_page_depth });
402
+ return sponsorService.createResponse(pages);
403
+ }
404
+ catch (error) {
405
+ logger.error('Error listing document pages', error);
406
+ return sponsorService.createErrorResponse(error);
407
+ }
408
+ }
409
+ /**
410
+ * Handler for getting document pages
411
+ */
412
+ export async function handleGetDocumentPages(params) {
413
+ const { documentId, pageIds, content_format } = params;
414
+ if (!documentId) {
415
+ return sponsorService.createErrorResponse('Document ID is required');
416
+ }
417
+ if (!pageIds || !Array.isArray(pageIds) || pageIds.length === 0) {
418
+ return sponsorService.createErrorResponse('Page IDs array is required');
419
+ }
420
+ try {
421
+ const options = {};
422
+ // Adiciona content_format nas options se fornecido
423
+ if (content_format) {
424
+ options.content_format = content_format;
425
+ }
426
+ const pages = await clickUpServices.document.getDocumentPages(documentId, pageIds, options);
427
+ return sponsorService.createResponse(pages);
428
+ }
429
+ catch (error) {
430
+ return sponsorService.createErrorResponse(`Failed to get document pages: ${error.message}`);
431
+ }
432
+ }
433
+ /**
434
+ * Handler for creating a new page in a document
435
+ */
436
+ export async function handleCreateDocumentPage(parameters) {
437
+ const { documentId, content, sub_title, name, parent_page_id } = parameters;
438
+ if (!documentId) {
439
+ return sponsorService.createErrorResponse('Document ID is required');
440
+ }
441
+ if (!name) {
442
+ return sponsorService.createErrorResponse('Title/Name is required');
443
+ }
444
+ try {
445
+ const page = await clickUpServices.document.createPage(documentId, {
446
+ content,
447
+ sub_title,
448
+ name,
449
+ parent_page_id,
450
+ });
451
+ return sponsorService.createResponse(page);
452
+ }
453
+ catch (error) {
454
+ return sponsorService.createErrorResponse(`Failed to create document page: ${error.message}`);
455
+ }
456
+ }
457
+ /**
458
+ * Handler for updating a document page
459
+ */
460
+ export async function handleUpdateDocumentPage(parameters) {
461
+ const { documentId, pageId, name, sub_title, content, content_format, content_edit_mode } = parameters;
462
+ if (!documentId) {
463
+ return sponsorService.createErrorResponse('Document ID is required');
464
+ }
465
+ if (!pageId) {
466
+ return sponsorService.createErrorResponse('Page ID is required');
467
+ }
468
+ // Prepare update data
469
+ const updateData = {};
470
+ if (name)
471
+ updateData.name = name;
472
+ if (sub_title)
473
+ updateData.sub_title = sub_title;
474
+ if (content)
475
+ updateData.content = content;
476
+ if (content_format)
477
+ updateData.content_format = content_format;
478
+ if (content_edit_mode)
479
+ updateData.content_edit_mode = content_edit_mode;
480
+ try {
481
+ const page = await clickUpServices.document.updatePage(documentId, pageId, updateData);
482
+ return sponsorService.createResponse({
483
+ message: `Page updated successfully`
484
+ }, true);
485
+ }
486
+ catch (error) {
487
+ return sponsorService.createErrorResponse(`Failed to update document page: ${error.message}`);
488
+ }
489
+ }