@pripanggalih/clickup-mcp 1.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.
Files changed (57) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +295 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +184 -0
  6. package/dist/clickup-text.d.ts +83 -0
  7. package/dist/clickup-text.d.ts.map +1 -0
  8. package/dist/clickup-text.js +563 -0
  9. package/dist/index.d.ts +5 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +135 -0
  12. package/dist/resources/space-resources.d.ts +6 -0
  13. package/dist/resources/space-resources.d.ts.map +1 -0
  14. package/dist/resources/space-resources.js +95 -0
  15. package/dist/shared/config.d.ts +11 -0
  16. package/dist/shared/config.d.ts.map +1 -0
  17. package/dist/shared/config.js +61 -0
  18. package/dist/shared/data-uri.d.ts +14 -0
  19. package/dist/shared/data-uri.d.ts.map +1 -0
  20. package/dist/shared/data-uri.js +34 -0
  21. package/dist/shared/image-processing.d.ts +13 -0
  22. package/dist/shared/image-processing.d.ts.map +1 -0
  23. package/dist/shared/image-processing.js +199 -0
  24. package/dist/shared/types.d.ts +21 -0
  25. package/dist/shared/types.d.ts.map +1 -0
  26. package/dist/shared/types.js +2 -0
  27. package/dist/shared/utils.d.ts +71 -0
  28. package/dist/shared/utils.d.ts.map +1 -0
  29. package/dist/shared/utils.js +508 -0
  30. package/dist/test-utils.d.ts +23 -0
  31. package/dist/test-utils.d.ts.map +1 -0
  32. package/dist/test-utils.js +44 -0
  33. package/dist/tools/admin-tools.d.ts +3 -0
  34. package/dist/tools/admin-tools.d.ts.map +1 -0
  35. package/dist/tools/admin-tools.js +288 -0
  36. package/dist/tools/doc-tools.d.ts +4 -0
  37. package/dist/tools/doc-tools.d.ts.map +1 -0
  38. package/dist/tools/doc-tools.js +436 -0
  39. package/dist/tools/list-tools.d.ts +4 -0
  40. package/dist/tools/list-tools.d.ts.map +1 -0
  41. package/dist/tools/list-tools.js +175 -0
  42. package/dist/tools/search-tools.d.ts +3 -0
  43. package/dist/tools/search-tools.d.ts.map +1 -0
  44. package/dist/tools/search-tools.js +161 -0
  45. package/dist/tools/space-tools.d.ts +3 -0
  46. package/dist/tools/space-tools.d.ts.map +1 -0
  47. package/dist/tools/space-tools.js +128 -0
  48. package/dist/tools/task-tools.d.ts +8 -0
  49. package/dist/tools/task-tools.d.ts.map +1 -0
  50. package/dist/tools/task-tools.js +329 -0
  51. package/dist/tools/task-write-tools.d.ts +3 -0
  52. package/dist/tools/task-write-tools.d.ts.map +1 -0
  53. package/dist/tools/task-write-tools.js +567 -0
  54. package/dist/tools/time-tools.d.ts +4 -0
  55. package/dist/tools/time-tools.d.ts.map +1 -0
  56. package/dist/tools/time-tools.js +338 -0
  57. package/package.json +74 -0
@@ -0,0 +1,436 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerDocumentToolsRead = registerDocumentToolsRead;
4
+ exports.registerDocumentToolsWrite = registerDocumentToolsWrite;
5
+ const zod_1 = require("zod");
6
+ const config_1 = require("../shared/config");
7
+ const utils_1 = require("../shared/utils");
8
+ /**
9
+ * Helper function to recursively extract all pages from nested page structure
10
+ */
11
+ function extractAllPages(pageGroup) {
12
+ const allPages = [];
13
+ // Add the page itself
14
+ allPages.push({
15
+ id: pageGroup.id,
16
+ name: pageGroup.name,
17
+ doc_id: pageGroup.doc_id,
18
+ parent_page_id: pageGroup.parent_page_id || null
19
+ });
20
+ // Recursively add nested pages
21
+ if (pageGroup.pages && Array.isArray(pageGroup.pages)) {
22
+ pageGroup.pages.forEach((nestedPage) => {
23
+ allPages.push(...extractAllPages(nestedPage));
24
+ });
25
+ }
26
+ return allPages;
27
+ }
28
+ /**
29
+ * Helper function to display page hierarchy with proper indentation
30
+ */
31
+ function displayPageHierarchy(pageGroup, currentPageId, depth = 0) {
32
+ const result = [];
33
+ const indent = ' '.repeat(depth); // 2 spaces per level
34
+ const isCurrentPage = pageGroup.id === currentPageId;
35
+ const prefix = isCurrentPage ? '▶️ ' : ' ';
36
+ const pageIndicator = isCurrentPage ? ' ← **Currently viewing**' : '';
37
+ // Display this page
38
+ result.push(`${indent}${prefix}${pageGroup.name} (${pageGroup.id})${pageIndicator}`);
39
+ // Recursively display nested pages
40
+ if (pageGroup.pages && Array.isArray(pageGroup.pages)) {
41
+ pageGroup.pages.forEach((nestedPage) => {
42
+ result.push(...displayPageHierarchy(nestedPage, currentPageId, depth + 1));
43
+ });
44
+ }
45
+ return result;
46
+ }
47
+ function registerDocumentToolsRead(server) {
48
+ server.tool("readDocument", [
49
+ "Get a ClickUp document with page structure and content.",
50
+ "Documents can be discovered via searchSpaces (which includes documents in space tree) or by direct URL from the user or within tasks.",
51
+ "Always use the document URL when referencing documents in conversations or sharing with others.",
52
+ "The response provides complete document metadata, page structure, and requested page content.",
53
+ `Document URLs look like this: ${(0, utils_1.generateDocumentUrl)('doc_id', 'page_id')}`,
54
+ ].join("\n"), {
55
+ doc_id: zod_1.z
56
+ .string()
57
+ .min(1)
58
+ .describe("The document ID to read"),
59
+ page: zod_1.z
60
+ .string()
61
+ .optional()
62
+ .describe("Optional specific page ID or name to read (defaults to first page)")
63
+ }, {
64
+ readOnlyHint: true
65
+ }, async ({ doc_id, page }) => {
66
+ try {
67
+ // First get the document details and page structure
68
+ const [docResponse, pagesResponse] = await Promise.all([
69
+ fetch(`https://api.clickup.com/api/v3/workspaces/${config_1.CONFIG.teamId}/docs/${doc_id}`, {
70
+ headers: { Authorization: config_1.CONFIG.apiKey },
71
+ }),
72
+ fetch(`https://api.clickup.com/api/v3/workspaces/${config_1.CONFIG.teamId}/docs/${doc_id}/pageListing`, {
73
+ headers: { Authorization: config_1.CONFIG.apiKey },
74
+ })
75
+ ]);
76
+ if (!docResponse.ok) {
77
+ throw new Error(`Error fetching document: ${docResponse.status} ${docResponse.statusText}`);
78
+ }
79
+ if (!pagesResponse.ok) {
80
+ throw new Error(`Error fetching pages: ${pagesResponse.status} ${pagesResponse.statusText}`);
81
+ }
82
+ const docData = await docResponse.json();
83
+ const pagesData = await pagesResponse.json();
84
+ const doc = docData; // Document data is flat
85
+ // Extract all pages while preserving hierarchy for display
86
+ const pages = [];
87
+ const hierarchicalPages = pagesData; // Keep original structure for hierarchy display
88
+ // Extract flat list of pages for searching and navigation
89
+ pagesData.forEach((pageGroup) => {
90
+ pages.push(...extractAllPages(pageGroup));
91
+ });
92
+ if (pages.length === 0) {
93
+ return {
94
+ content: [{
95
+ type: "text",
96
+ text: `📄 **Document: ${doc.name}** (doc_id: ${doc_id})
97
+ 📎 Document URL: ${(0, utils_1.generateDocumentUrl)(doc_id)}
98
+
99
+ ⚠️ This document exists but has no pages yet.
100
+
101
+ **Next steps:**
102
+ - Use \`createDocumentOrPage\` with doc_id="${doc_id}" to add the first page
103
+ - Example: createDocumentOrPage(doc_id="${doc_id}", name="Introduction", content="Your content here")`
104
+ }],
105
+ };
106
+ }
107
+ // Determine which page to read
108
+ let targetPage = null;
109
+ if (page) {
110
+ // Look for page by ID first, then by name
111
+ targetPage = pages.find((p) => p.id === page || p.name === page);
112
+ if (!targetPage) {
113
+ return {
114
+ content: [{
115
+ type: "text",
116
+ text: `Page "${page}" not found in document "${doc.name}". Available pages: ${pages.map((p) => `"${p.name}" (${p.id})`).join(', ')}`
117
+ }],
118
+ };
119
+ }
120
+ }
121
+ else {
122
+ // Default to first page
123
+ targetPage = pages[0];
124
+ }
125
+ // Get the specific page content
126
+ const pageResponse = await fetch(`https://api.clickup.com/api/v3/workspaces/${config_1.CONFIG.teamId}/docs/${doc_id}/pages/${targetPage.id}`, {
127
+ headers: { Authorization: config_1.CONFIG.apiKey },
128
+ });
129
+ if (!pageResponse.ok) {
130
+ throw new Error(`Error fetching page content: ${pageResponse.status} ${pageResponse.statusText}`);
131
+ }
132
+ const pageData = await pageResponse.json();
133
+ const pageContent = pageData; // Page data is flat
134
+ // Build the response
135
+ const result = [];
136
+ // Document header with metadata
137
+ result.push(`doc_id: ${doc.id}`);
138
+ result.push(`Document Title: ${doc.name}`);
139
+ result.push(`Document URL: ${(0, utils_1.generateDocumentUrl)(doc_id)}`);
140
+ result.push(`Current page_id: ${targetPage.id}`);
141
+ result.push(`Current Page Title: ${pageContent.name}`);
142
+ result.push(`Current Page URL: ${(0, utils_1.generateDocumentUrl)(doc_id, targetPage.id)}`);
143
+ // Page structure overview with hierarchy
144
+ result.push('Page Structure:');
145
+ hierarchicalPages.forEach((pageGroup) => {
146
+ result.push(...displayPageHierarchy(pageGroup, targetPage.id));
147
+ });
148
+ // Current page content
149
+ if (pageContent.content && pageContent.content.trim()) {
150
+ result.push(`Page Content:`);
151
+ return {
152
+ content: [
153
+ { type: "text", text: result.join('\n') },
154
+ { type: "text", text: pageContent.content },
155
+ ],
156
+ };
157
+ }
158
+ else {
159
+ result.push('*This page is empty.*');
160
+ result.push('');
161
+ result.push('**💡 To add content to this page:**');
162
+ result.push(`Use \`updateDocumentPage\` with doc_id="${doc_id}", page_id="${targetPage.id}" and your content.`);
163
+ result.push(`Example: updateDocumentPage(doc_id="${doc_id}", page_id="${targetPage.id}", content="Your content here")`);
164
+ return {
165
+ content: [
166
+ { type: "text", text: result.join('\n') },
167
+ ],
168
+ };
169
+ }
170
+ }
171
+ catch (error) {
172
+ console.error('Error reading document:', error);
173
+ return {
174
+ content: [
175
+ {
176
+ type: "text",
177
+ text: `Error reading document ${doc_id}: ${error instanceof Error ? error.message : 'Unknown error'}`,
178
+ },
179
+ ],
180
+ };
181
+ }
182
+ });
183
+ }
184
+ function registerDocumentToolsWrite(server) {
185
+ server.tool("updateDocumentPage", [
186
+ "Updates an existing document page's content and/or name.",
187
+ "Use this when you have both doc_id and page_id from readDocument.",
188
+ "Content is in markdown format and can be replaced or appended.",
189
+ "Always reference documents by their URLs when sharing with users."
190
+ ].join("\n"), {
191
+ doc_id: zod_1.z
192
+ .string()
193
+ .min(1)
194
+ .describe("The document ID containing the page (from readDocument)"),
195
+ page_id: zod_1.z
196
+ .string()
197
+ .min(1)
198
+ .describe("The page ID to update (from readDocument)"),
199
+ name: zod_1.z
200
+ .string()
201
+ .optional()
202
+ .describe("Optional: new name for the page"),
203
+ content: zod_1.z
204
+ .string()
205
+ .optional()
206
+ .describe("Optional: page content in markdown format"),
207
+ append: zod_1.z
208
+ .boolean()
209
+ .optional()
210
+ .describe("Whether to append content to existing page content (default: false - replaces content)")
211
+ }, {
212
+ readOnlyHint: false,
213
+ destructiveHint: true,
214
+ idempotentHint: false,
215
+ }, async ({ doc_id, page_id, name, content, append = false }) => {
216
+ try {
217
+ const requestBody = {};
218
+ if (name) {
219
+ requestBody.name = name;
220
+ }
221
+ if (content !== undefined) {
222
+ requestBody.content = content;
223
+ requestBody.content_edit_mode = append ? 'append' : 'replace';
224
+ }
225
+ if (Object.keys(requestBody).length === 0) {
226
+ return {
227
+ content: [{
228
+ type: "text",
229
+ text: "Error: At least one of 'name' or 'content' must be provided for update."
230
+ }],
231
+ };
232
+ }
233
+ const response = await fetch(`https://api.clickup.com/api/v3/workspaces/${config_1.CONFIG.teamId}/docs/${doc_id}/pages/${page_id}`, {
234
+ method: 'PUT',
235
+ headers: {
236
+ Authorization: config_1.CONFIG.apiKey,
237
+ 'Content-Type': 'application/json',
238
+ },
239
+ body: JSON.stringify(requestBody),
240
+ });
241
+ if (!response.ok) {
242
+ const errorText = await response.text();
243
+ throw new Error(`Error updating page: ${response.status} ${response.statusText}. ${errorText}`);
244
+ }
245
+ // Handle potentially empty response
246
+ const responseText = await response.text();
247
+ let updatedPage = {};
248
+ if (responseText && responseText.trim()) {
249
+ try {
250
+ const data = JSON.parse(responseText);
251
+ updatedPage = data.page || data;
252
+ }
253
+ catch (e) {
254
+ // If JSON parsing fails, continue with empty updatedPage object
255
+ console.error('Warning: Could not parse response JSON, but update was successful');
256
+ }
257
+ }
258
+ const pageName = updatedPage.name || name || "page";
259
+ return {
260
+ content: [{
261
+ type: "text",
262
+ text: `✅ Successfully updated page "${pageName}" (page_id: ${page_id})\n\nPage URL: ${(0, utils_1.generateDocumentUrl)(doc_id, page_id)}`
263
+ }],
264
+ };
265
+ }
266
+ catch (error) {
267
+ console.error('Error updating document page:', error);
268
+ return {
269
+ content: [
270
+ {
271
+ type: "text",
272
+ text: `Error updating page: ${error instanceof Error ? error.message : 'Unknown error'}`,
273
+ },
274
+ ],
275
+ };
276
+ }
277
+ });
278
+ server.tool("createDocumentOrPage", [
279
+ "Creates a new document with its first page, OR adds a page to an existing document.",
280
+ "To create a NEW document: provide space_id OR list_id + name + content",
281
+ "To add a page to EXISTING document: provide doc_id + name + content",
282
+ "To create a sub-page: provide doc_id + parent_page_id + name + content",
283
+ "Content is in markdown format and supports ClickUp's markdown features.",
284
+ "Always reference documents by their URLs when sharing with users."
285
+ ].join("\n"), {
286
+ space_id: zod_1.z
287
+ .string()
288
+ .optional()
289
+ .describe("Create NEW document in this space (mutually exclusive with list_id and doc_id)"),
290
+ list_id: zod_1.z
291
+ .string()
292
+ .optional()
293
+ .describe("Create NEW document in this list (mutually exclusive with space_id and doc_id)"),
294
+ doc_id: zod_1.z
295
+ .string()
296
+ .optional()
297
+ .describe("Add page to EXISTING document with this ID (mutually exclusive with space_id and list_id)"),
298
+ parent_page_id: zod_1.z
299
+ .string()
300
+ .optional()
301
+ .describe("Optional: when provided with doc_id, creates a sub-page under this parent page"),
302
+ name: zod_1.z
303
+ .string()
304
+ .min(1)
305
+ .describe("Name for the document/page being created"),
306
+ content: zod_1.z
307
+ .string()
308
+ .optional()
309
+ .describe("Optional: page content in markdown format")
310
+ }, {
311
+ readOnlyHint: false,
312
+ destructiveHint: false,
313
+ idempotentHint: false,
314
+ openWorldHint: true
315
+ }, async ({ space_id, list_id, doc_id, parent_page_id, name, content }) => {
316
+ try {
317
+ // Validate mutually exclusive parameters
318
+ const locationParams = [space_id, list_id, doc_id].filter(Boolean).length;
319
+ if (locationParams !== 1) {
320
+ return {
321
+ content: [{
322
+ type: "text",
323
+ text: "Error: Provide exactly ONE of: space_id (new doc in space), list_id (new doc in list), or doc_id (page in existing doc)."
324
+ }],
325
+ };
326
+ }
327
+ if (parent_page_id && !doc_id) {
328
+ return {
329
+ content: [{
330
+ type: "text",
331
+ text: "Error: parent_page_id requires doc_id to be provided."
332
+ }],
333
+ };
334
+ }
335
+ // Case 1: Create new document in space or list
336
+ if (space_id || list_id) {
337
+ const parentId = space_id || list_id;
338
+ const parentType = space_id ? 4 : 6; // 4=Space, 6=List
339
+ // Create new document
340
+ const docRequestBody = {
341
+ name: name,
342
+ create_page: false,
343
+ parent: {
344
+ id: parentId,
345
+ type: parentType
346
+ }
347
+ };
348
+ const docResponse = await fetch(`https://api.clickup.com/api/v3/workspaces/${config_1.CONFIG.teamId}/docs`, {
349
+ method: 'POST',
350
+ headers: {
351
+ Authorization: config_1.CONFIG.apiKey,
352
+ 'Content-Type': 'application/json',
353
+ },
354
+ body: JSON.stringify(docRequestBody),
355
+ });
356
+ if (!docResponse.ok) {
357
+ const errorText = await docResponse.text();
358
+ throw new Error(`Error creating document: ${docResponse.status} ${docResponse.statusText}. ${errorText}`);
359
+ }
360
+ const docData = await docResponse.json();
361
+ const newDocId = docData.id;
362
+ // Create the first page
363
+ const pageRequestBody = {
364
+ name: name,
365
+ content: content || '',
366
+ };
367
+ const pageResponse = await fetch(`https://api.clickup.com/api/v3/workspaces/${config_1.CONFIG.teamId}/docs/${newDocId}/pages`, {
368
+ method: 'POST',
369
+ headers: {
370
+ Authorization: config_1.CONFIG.apiKey,
371
+ 'Content-Type': 'application/json',
372
+ },
373
+ body: JSON.stringify(pageRequestBody),
374
+ });
375
+ if (!pageResponse.ok) {
376
+ const errorText = await pageResponse.text();
377
+ throw new Error(`Error creating first page: ${pageResponse.status} ${pageResponse.statusText}. ${errorText}`);
378
+ }
379
+ const pageData = await pageResponse.json();
380
+ const firstPage = pageData.page || pageData;
381
+ return {
382
+ content: [{
383
+ type: "text",
384
+ text: `✅ Successfully created new document "${name}" (doc_id: ${newDocId})\n\nDocument URL: ${(0, utils_1.generateDocumentUrl)(newDocId)}\nFirst Page URL: ${(0, utils_1.generateDocumentUrl)(newDocId, firstPage.id)}`
385
+ }],
386
+ };
387
+ }
388
+ // Case 2: Add page to existing document or create sub-page
389
+ if (doc_id) {
390
+ const pageRequestBody = {
391
+ name: name,
392
+ content: content || '',
393
+ };
394
+ if (parent_page_id) {
395
+ pageRequestBody.parent_page_id = parent_page_id;
396
+ }
397
+ const response = await fetch(`https://api.clickup.com/api/v3/workspaces/${config_1.CONFIG.teamId}/docs/${doc_id}/pages`, {
398
+ method: 'POST',
399
+ headers: {
400
+ Authorization: config_1.CONFIG.apiKey,
401
+ 'Content-Type': 'application/json',
402
+ },
403
+ body: JSON.stringify(pageRequestBody),
404
+ });
405
+ if (!response.ok) {
406
+ const errorText = await response.text();
407
+ throw new Error(`Error creating page: ${response.status} ${response.statusText}. ${errorText}`);
408
+ }
409
+ const data = await response.json();
410
+ const newPage = data.page || data;
411
+ const pageType = parent_page_id ? "sub-page" : "page";
412
+ const parentInfo = parent_page_id ? ` under parent page ${parent_page_id}` : "";
413
+ return {
414
+ content: [{
415
+ type: "text",
416
+ text: `✅ Successfully created ${pageType} "${newPage.name}" (page_id: ${newPage.id})${parentInfo}\n\nPage URL: ${(0, utils_1.generateDocumentUrl)(doc_id, newPage.id)}`
417
+ }],
418
+ };
419
+ }
420
+ return {
421
+ content: [{ type: "text", text: "Error: Unexpected state in document/page creation." }],
422
+ };
423
+ }
424
+ catch (error) {
425
+ console.error('Error creating document or page:', error);
426
+ return {
427
+ content: [
428
+ {
429
+ type: "text",
430
+ text: `Error creating document or page: ${error instanceof Error ? error.message : 'Unknown error'}`,
431
+ },
432
+ ],
433
+ };
434
+ }
435
+ });
436
+ }
@@ -0,0 +1,4 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerListToolsRead(server: McpServer): void;
3
+ export declare function registerListToolsWrite(server: McpServer): void;
4
+ //# sourceMappingURL=list-tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-tools.d.ts","sourceRoot":"","sources":["../../src/tools/list-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAKpE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,QAkHtD;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,QA8EvD"}
@@ -0,0 +1,175 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerListToolsRead = registerListToolsRead;
4
+ exports.registerListToolsWrite = registerListToolsWrite;
5
+ const zod_1 = require("zod");
6
+ const config_1 = require("../shared/config");
7
+ const utils_1 = require("../shared/utils");
8
+ function registerListToolsRead(server) {
9
+ server.tool("getListInfo", [
10
+ "Gets comprehensive information about a list including description and available statuses.",
11
+ "ALWAYS use the list URL (https://app.clickup.com/v/l/LIST_ID) when referencing lists.",
12
+ "Use this before creating tasks to understand the list context and available statuses for new tasks.",
13
+ "IMPORTANT: The list description often contains valuable project context, requirements, or guidelines - read and consider this information when creating or updating tasks in this list.",
14
+ "Share the clickable list URL when suggesting list-related actions."
15
+ ].join("\n"), {
16
+ list_id: zod_1.z.string().min(1).describe("The list ID to get information for")
17
+ }, {
18
+ readOnlyHint: true
19
+ }, async ({ list_id }) => {
20
+ try {
21
+ // Get list details including statuses (try to get markdown content)
22
+ const listResponse = await fetch(`https://api.clickup.com/api/v2/list/${list_id}?include_markdown_description=true`, {
23
+ headers: { Authorization: config_1.CONFIG.apiKey },
24
+ });
25
+ if (!listResponse.ok) {
26
+ throw new Error(`Error fetching list details: ${listResponse.status} ${listResponse.statusText}`);
27
+ }
28
+ const listData = await listResponse.json();
29
+ // Fetch space tags in parallel (don't let this fail the main request)
30
+ let spaceTags = [];
31
+ if (listData.space?.id) {
32
+ try {
33
+ const spaceTagsResponse = await fetch(`https://api.clickup.com/api/v2/space/${listData.space.id}/tag`, {
34
+ headers: { Authorization: config_1.CONFIG.apiKey },
35
+ });
36
+ if (spaceTagsResponse.ok) {
37
+ const spaceTagsData = await spaceTagsResponse.json();
38
+ spaceTags = spaceTagsData.tags || [];
39
+ }
40
+ }
41
+ catch (error) {
42
+ console.error(`Error fetching space tags for space ${listData.space.id}:`, error);
43
+ }
44
+ }
45
+ const responseLines = [
46
+ `List Information:`,
47
+ `list_id: ${list_id}`,
48
+ `list_url: ${(0, utils_1.generateListUrl)(list_id)}`,
49
+ `name: ${listData.name}`,
50
+ `folder: ${listData.folder?.name || 'No folder'}`,
51
+ `space: ${listData.space?.name || 'Unknown'} (${listData.space?.id || 'N/A'})`,
52
+ `space_url: ${(0, utils_1.generateSpaceUrl)(listData.space?.id || '')}`,
53
+ `archived: ${listData.archived || false}`,
54
+ `task_count: ${listData.task_count || 0}`,
55
+ ];
56
+ // Add description if available (check both content and markdown fields)
57
+ const description = listData.markdown_description || listData.markdown_content || listData.content;
58
+ if (description) {
59
+ responseLines.push(`description: ${description}`);
60
+ }
61
+ // Add available statuses
62
+ if (listData.statuses && Array.isArray(listData.statuses)) {
63
+ const statuses = listData.statuses.map((status) => ({
64
+ name: status.status,
65
+ color: status.color || 'none',
66
+ type: status.type || 'custom'
67
+ }));
68
+ responseLines.push(`Available statuses (${statuses.length} total):`);
69
+ statuses.forEach((status) => {
70
+ responseLines.push(` - ${status.name} (${status.type})`);
71
+ });
72
+ responseLines.push(`Valid status names for createTask/updateTask: ${statuses.map((s) => s.name).join(', ')}`);
73
+ }
74
+ else {
75
+ responseLines.push('No statuses found for this list.');
76
+ }
77
+ // Add space tags information
78
+ if (spaceTags.length > 0) {
79
+ const tagNames = spaceTags.map((tag) => tag.name).filter(Boolean).sort();
80
+ if (tagNames.length > 0) {
81
+ responseLines.push(`Available tags in space (shared across all lists): ${tagNames.join(', ')}`);
82
+ }
83
+ }
84
+ else if (listData.space?.id) {
85
+ responseLines.push('No tags found in this space.');
86
+ }
87
+ return {
88
+ content: [
89
+ {
90
+ type: "text",
91
+ text: responseLines.join('\n')
92
+ }
93
+ ],
94
+ };
95
+ }
96
+ catch (error) {
97
+ console.error('Error getting list info:', error);
98
+ return {
99
+ content: [
100
+ {
101
+ type: "text",
102
+ text: `Error getting list info: ${error instanceof Error ? error.message : 'Unknown error'}`,
103
+ },
104
+ ],
105
+ };
106
+ }
107
+ });
108
+ }
109
+ function registerListToolsWrite(server) {
110
+ server.tool("updateListInfo", [
111
+ "Appends documentation or context to a list's description.",
112
+ "ALWAYS reference the list URL (https://app.clickup.com/v/l/LIST_ID) when updating or discussing lists.",
113
+ "SAFETY FEATURE: Description updates are APPEND-ONLY to prevent data loss - existing content is preserved.",
114
+ "Use this to add project context, requirements, or guidelines that LLMs should consider when working with tasks in this list.",
115
+ "Include links to related tasks, spaces, or external resources in the appended content.",
116
+ "Content is appended in markdown format with timestamp for tracking changes."
117
+ ].join("\n"), {
118
+ list_id: zod_1.z.string().min(1).describe("The list ID to update"),
119
+ append_description: zod_1.z.string().min(1).describe("Markdown content to APPEND to existing list description (preserves existing content for safety)")
120
+ }, {
121
+ readOnlyHint: false,
122
+ destructiveHint: false,
123
+ idempotentHint: false,
124
+ }, async ({ list_id, append_description }) => {
125
+ try {
126
+ // Get current list info including description (try to get markdown content)
127
+ const listResponse = await fetch(`https://api.clickup.com/api/v2/list/${list_id}?include_markdown_description=true`, {
128
+ headers: { Authorization: config_1.CONFIG.apiKey },
129
+ });
130
+ if (!listResponse.ok) {
131
+ throw new Error(`Error fetching list: ${listResponse.status} ${listResponse.statusText}`);
132
+ }
133
+ const listData = await listResponse.json();
134
+ // Handle append-only description update with markdown support
135
+ const currentDescription = listData.markdown_description || listData.markdown_content || listData.content || "";
136
+ const timestamp = new Date().toISOString().split('T')[0]; // YYYY-MM-DD format
137
+ const separator = currentDescription.trim() ? "\n\n---\n" : "";
138
+ const finalDescription = currentDescription + separator + `**Edit (${timestamp}):** ${append_description}`;
139
+ // Update the list description using markdown_content
140
+ const updateResponse = await fetch(`https://api.clickup.com/api/v2/list/${list_id}`, {
141
+ method: 'PUT',
142
+ headers: {
143
+ Authorization: config_1.CONFIG.apiKey,
144
+ 'Content-Type': 'application/json'
145
+ },
146
+ body: JSON.stringify({
147
+ markdown_content: finalDescription
148
+ })
149
+ });
150
+ if (!updateResponse.ok) {
151
+ const errorData = await updateResponse.json().catch(() => ({}));
152
+ throw new Error(`Error updating list: ${updateResponse.status} ${updateResponse.statusText} - ${JSON.stringify(errorData)}`);
153
+ }
154
+ return {
155
+ content: [
156
+ {
157
+ type: "text",
158
+ text: `Successfully appended content to list "${listData.name}". The new content has been added with timestamp (${timestamp}) while preserving existing description.`,
159
+ },
160
+ ],
161
+ };
162
+ }
163
+ catch (error) {
164
+ console.error('Error updating list info:', error);
165
+ return {
166
+ content: [
167
+ {
168
+ type: "text",
169
+ text: `Error updating list info: ${error instanceof Error ? error.message : String(error)}`,
170
+ },
171
+ ],
172
+ };
173
+ }
174
+ });
175
+ }
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerSearchTools(server: McpServer, userData: any): void;
3
+ //# sourceMappingURL=search-tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-tools.d.ts","sourceRoot":"","sources":["../../src/tools/search-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAC,MAAM,yCAAyC,CAAC;AAQlE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,QAmLnE"}