@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.
- package/LICENSE +22 -0
- package/README.md +295 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +184 -0
- package/dist/clickup-text.d.ts +83 -0
- package/dist/clickup-text.d.ts.map +1 -0
- package/dist/clickup-text.js +563 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +135 -0
- package/dist/resources/space-resources.d.ts +6 -0
- package/dist/resources/space-resources.d.ts.map +1 -0
- package/dist/resources/space-resources.js +95 -0
- package/dist/shared/config.d.ts +11 -0
- package/dist/shared/config.d.ts.map +1 -0
- package/dist/shared/config.js +61 -0
- package/dist/shared/data-uri.d.ts +14 -0
- package/dist/shared/data-uri.d.ts.map +1 -0
- package/dist/shared/data-uri.js +34 -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 +199 -0
- package/dist/shared/types.d.ts +21 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/types.js +2 -0
- package/dist/shared/utils.d.ts +71 -0
- package/dist/shared/utils.d.ts.map +1 -0
- package/dist/shared/utils.js +508 -0
- package/dist/test-utils.d.ts +23 -0
- package/dist/test-utils.d.ts.map +1 -0
- package/dist/test-utils.js +44 -0
- package/dist/tools/admin-tools.d.ts +3 -0
- package/dist/tools/admin-tools.d.ts.map +1 -0
- package/dist/tools/admin-tools.js +288 -0
- 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 +436 -0
- package/dist/tools/list-tools.d.ts +4 -0
- package/dist/tools/list-tools.d.ts.map +1 -0
- package/dist/tools/list-tools.js +175 -0
- package/dist/tools/search-tools.d.ts +3 -0
- package/dist/tools/search-tools.d.ts.map +1 -0
- package/dist/tools/search-tools.js +161 -0
- package/dist/tools/space-tools.d.ts +3 -0
- package/dist/tools/space-tools.d.ts.map +1 -0
- package/dist/tools/space-tools.js +128 -0
- package/dist/tools/task-tools.d.ts +8 -0
- package/dist/tools/task-tools.d.ts.map +1 -0
- package/dist/tools/task-tools.js +329 -0
- 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 +567 -0
- package/dist/tools/time-tools.d.ts +4 -0
- package/dist/tools/time-tools.d.ts.map +1 -0
- package/dist/tools/time-tools.js +338 -0
- 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 @@
|
|
|
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 @@
|
|
|
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"}
|