@hauptsache.net/clickup-mcp 1.5.0 → 1.6.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/README.md +16 -15
- package/dist/clickup-text.d.ts +37 -2
- package/dist/clickup-text.d.ts.map +1 -1
- package/dist/clickup-text.js +302 -5
- package/dist/resources/space-resources.js +2 -2
- package/dist/tools/doc-tools.d.ts.map +1 -1
- package/dist/tools/doc-tools.js +7 -2
- package/dist/tools/list-tools.d.ts.map +1 -1
- package/dist/tools/list-tools.js +3 -1
- package/dist/tools/task-tools.js +2 -2
- package/dist/tools/task-write-tools.d.ts.map +1 -1
- package/dist/tools/task-write-tools.js +15 -4
- package/dist/tools/time-tools.d.ts.map +1 -1
- package/dist/tools/time-tools.js +3 -1
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -155,21 +155,22 @@ The ClickUp MCP supports three operational modes to balance functionality, secur
|
|
|
155
155
|
- **📖 `read`**: Full read-only access for project exploration and workflow understanding
|
|
156
156
|
- **✏️ `write`** (Default): Complete functionality for task management and productivity workflows
|
|
157
157
|
|
|
158
|
-
| Tool
|
|
159
|
-
|
|
160
|
-
| `getTaskById`
|
|
161
|
-
| `addComment`
|
|
162
|
-
| `updateTask`
|
|
163
|
-
| `createTask`
|
|
164
|
-
| `searchTasks`
|
|
165
|
-
| `searchSpaces`
|
|
166
|
-
| `getListInfo`
|
|
167
|
-
| `updateListInfo`
|
|
168
|
-
| `getTimeEntries`
|
|
169
|
-
| `createTimeEntry`
|
|
170
|
-
| `readDocument`
|
|
171
|
-
| `searchDocuments`
|
|
172
|
-
| `
|
|
158
|
+
| Tool | read-minimal | read | write | Description |
|
|
159
|
+
|------------------------|:------------:|:----:|:-----:|-----------------------------------------------------------------------------------------|
|
|
160
|
+
| `getTaskById` | ✅ | ✅ | ✅ | Get complete task details including comments, images, and metadata |
|
|
161
|
+
| `addComment` | ❌ | ❌ | ✅ | Add comments to tasks for collaboration |
|
|
162
|
+
| `updateTask` | ❌ | ❌ | ✅ | Update tasks (status, priority, assignees, etc.) with **SAFE APPEND-ONLY** descriptions |
|
|
163
|
+
| `createTask` | ❌ | ❌ | ✅ | Create new tasks with full markdown support |
|
|
164
|
+
| `searchTasks` | ✅ | ✅ | ✅ | Find tasks by content, keywords, assignees, or project context |
|
|
165
|
+
| `searchSpaces` | ❌ | ✅ | ✅ | Browse workspace structure, project organization, and documents |
|
|
166
|
+
| `getListInfo` | ❌ | ✅ | ✅ | Get list details and available statuses for task creation |
|
|
167
|
+
| `updateListInfo` | ❌ | ❌ | ✅ | **SAFE APPEND-ONLY** updates to list descriptions (preserves existing content) |
|
|
168
|
+
| `getTimeEntries` | ❌ | ✅ | ✅ | View time entries and analyze time spent across projects |
|
|
169
|
+
| `createTimeEntry` | ❌ | ❌ | ✅ | Log time entries for task tracking |
|
|
170
|
+
| `readDocument` | ❌ | ✅ | ✅ | Get document details, page structure, and content with navigation |
|
|
171
|
+
| `searchDocuments` | ❌ | ✅ | ✅ | Search documents by name and space with fuzzy matching and space filtering |
|
|
172
|
+
| `updateDocumentPage` | ❌ | ❌ | ✅ | Update existing page content or name with replace/append modes |
|
|
173
|
+
| `createDocumentOrPage` | ❌ | ❌ | ✅ | Create new documents with first page, or add pages/sub-pages to existing documents |
|
|
173
174
|
|
|
174
175
|
### Setting the Mode
|
|
175
176
|
|
package/dist/clickup-text.d.ts
CHANGED
|
@@ -37,12 +37,47 @@ export interface ClickUpAttachment {
|
|
|
37
37
|
* @param textItems Array of text items from ClickUp API
|
|
38
38
|
* @returns Promise resolving to an array of content blocks (text and images)
|
|
39
39
|
*/
|
|
40
|
-
export declare function
|
|
40
|
+
export declare function convertClickUpTextItemsToToolCallResult(textItems: ClickUpTextItem[]): Promise<(CallToolResult["content"][number] | ImageMetadataBlock)[]>;
|
|
41
41
|
/**
|
|
42
42
|
* Splits markdown text at image references and converts them to image blocks
|
|
43
43
|
* @param markdownText The markdown text to process
|
|
44
44
|
* @param attachments Array of attachments from the Clickup API
|
|
45
45
|
* @returns Array of content blocks (text and images)
|
|
46
46
|
*/
|
|
47
|
-
export declare function
|
|
47
|
+
export declare function convertMarkdownToToolCallResult(markdownText: string, attachments: ClickUpAttachment[] | null | undefined): (CallToolResult["content"][number] | ImageMetadataBlock)[];
|
|
48
|
+
/**
|
|
49
|
+
* Represents a ClickUp comment block with formatting
|
|
50
|
+
*/
|
|
51
|
+
export interface ClickUpCommentBlock {
|
|
52
|
+
text?: string;
|
|
53
|
+
type?: string;
|
|
54
|
+
attributes?: {
|
|
55
|
+
bold?: boolean;
|
|
56
|
+
italic?: boolean;
|
|
57
|
+
code?: boolean;
|
|
58
|
+
link?: string;
|
|
59
|
+
'code-block'?: {
|
|
60
|
+
'code-block': string;
|
|
61
|
+
};
|
|
62
|
+
header?: number;
|
|
63
|
+
blockquote?: {};
|
|
64
|
+
'blockquote-size'?: 'large';
|
|
65
|
+
list?: {
|
|
66
|
+
list: 'bullet' | 'ordered' | 'unchecked' | 'checked';
|
|
67
|
+
};
|
|
68
|
+
indent?: number;
|
|
69
|
+
'block-id'?: string;
|
|
70
|
+
};
|
|
71
|
+
list?: {
|
|
72
|
+
list: 'bullet' | 'ordered' | 'unchecked' | 'checked';
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Convert markdown text to ClickUp comment blocks format using remark
|
|
77
|
+
* Supports: headers, bold, italic, code, links, lists, blockquotes, code blocks
|
|
78
|
+
*
|
|
79
|
+
* @param markdown The markdown text to convert
|
|
80
|
+
* @returns Array of ClickUp comment blocks
|
|
81
|
+
*/
|
|
82
|
+
export declare function convertMarkdownToClickUpBlocks(markdown: string): ClickUpCommentBlock[];
|
|
48
83
|
//# sourceMappingURL=clickup-text.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clickup-text.d.ts","sourceRoot":"","sources":["../src/clickup-text.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"clickup-text.d.ts","sourceRoot":"","sources":["../src/clickup-text.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAOpD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE;QACN,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,GAAG,EAAE,MAAM,CAAC;QACZ,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB,CAAC;IACF,UAAU,CAAC,EAAE,GAAG,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AA4BD;;;;;;GAMG;AACH,wBAAsB,uCAAuC,CAC3D,SAAS,EAAE,eAAe,EAAE,GAC3B,OAAO,CAAC,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,GAAG,kBAAkB,CAAC,EAAE,CAAC,CAsNrE;AAED;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC7C,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,iBAAiB,EAAE,GAAG,IAAI,GAAG,SAAS,GAClD,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,GAAG,kBAAkB,CAAC,EAAE,CA0I5D;AA8BD;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE;QACX,IAAI,CAAC,EAAE,OAAO,CAAC;QACf,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,IAAI,CAAC,EAAE,OAAO,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,YAAY,CAAC,EAAE;YACb,YAAY,EAAE,MAAM,CAAC;SACtB,CAAC;QACF,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,EAAE,CAAC;QAChB,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAC5B,IAAI,CAAC,EAAE;YACL,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,CAAC;SACtD,CAAC;QACF,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,IAAI,CAAC,EAAE;QACL,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,CAAC;KACtD,CAAC;CACH;AAED;;;;;;GAMG;AACH,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE,MAAM,GAAG,mBAAmB,EAAE,CAoBtF"}
|
package/dist/clickup-text.js
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.
|
|
6
|
+
exports.convertClickUpTextItemsToToolCallResult = convertClickUpTextItemsToToolCallResult;
|
|
7
|
+
exports.convertMarkdownToToolCallResult = convertMarkdownToToolCallResult;
|
|
8
|
+
exports.convertMarkdownToClickUpBlocks = convertMarkdownToClickUpBlocks;
|
|
5
9
|
const data_uri_1 = require("./shared/data-uri");
|
|
10
|
+
const unified_1 = require("unified");
|
|
11
|
+
const remark_parse_1 = __importDefault(require("remark-parse"));
|
|
12
|
+
const remark_gfm_1 = __importDefault(require("remark-gfm"));
|
|
6
13
|
/**
|
|
7
14
|
* Extract thumbnail URLs from data-attachment attribute JSON
|
|
8
15
|
* ClickUp API sometimes has broken thumbnail URLs, but data-attachment contains working ones
|
|
@@ -31,9 +38,14 @@ function extractThumbnailsFromDataAttachment(attributes) {
|
|
|
31
38
|
* @param textItems Array of text items from ClickUp API
|
|
32
39
|
* @returns Promise resolving to an array of content blocks (text and images)
|
|
33
40
|
*/
|
|
34
|
-
async function
|
|
41
|
+
async function convertClickUpTextItemsToToolCallResult(textItems) {
|
|
35
42
|
const contentBlocks = [];
|
|
36
43
|
let currentTextBlock = "";
|
|
44
|
+
let currentLine = ""; // Track current line separately for block formatting
|
|
45
|
+
// Track current formatting state to avoid unnecessary close/reopen
|
|
46
|
+
let activeBold = false;
|
|
47
|
+
let activeItalic = false;
|
|
48
|
+
let activeCode = false;
|
|
37
49
|
for (let i = 0; i < textItems.length; i++) {
|
|
38
50
|
const item = textItems[i];
|
|
39
51
|
// Handle image items
|
|
@@ -100,7 +112,108 @@ async function processClickUpText(textItems) {
|
|
|
100
112
|
}
|
|
101
113
|
// Handle text items
|
|
102
114
|
else if (typeof item.text === "string") {
|
|
103
|
-
|
|
115
|
+
// Check if this is a newline with block formatting (header, blockquote, list)
|
|
116
|
+
if (item.text === '\n' && item.attributes) {
|
|
117
|
+
// Header formatting
|
|
118
|
+
if (item.attributes.header) {
|
|
119
|
+
const level = item.attributes.header;
|
|
120
|
+
currentLine = '#'.repeat(level) + ' ' + currentLine;
|
|
121
|
+
}
|
|
122
|
+
// Blockquote formatting
|
|
123
|
+
else if (item.attributes.blockquote) {
|
|
124
|
+
currentLine = '> ' + currentLine;
|
|
125
|
+
}
|
|
126
|
+
// List formatting
|
|
127
|
+
else if (item.attributes.list) {
|
|
128
|
+
const listType = item.attributes.list.list;
|
|
129
|
+
const indent = item.attributes.indent || 0;
|
|
130
|
+
// Add indentation (2 spaces per level) for nested lists
|
|
131
|
+
const indentStr = ' '.repeat(indent);
|
|
132
|
+
switch (listType) {
|
|
133
|
+
case 'bullet':
|
|
134
|
+
currentLine = indentStr + '- ' + currentLine;
|
|
135
|
+
break;
|
|
136
|
+
case 'ordered':
|
|
137
|
+
currentLine = indentStr + '1. ' + currentLine;
|
|
138
|
+
break;
|
|
139
|
+
case 'checked':
|
|
140
|
+
currentLine = indentStr + '- [x] ' + currentLine;
|
|
141
|
+
break;
|
|
142
|
+
case 'unchecked':
|
|
143
|
+
currentLine = indentStr + '- [ ] ' + currentLine;
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Code block formatting
|
|
148
|
+
else if (item.attributes['code-block']) {
|
|
149
|
+
// Wrap the current line in code block markers
|
|
150
|
+
currentLine = '```\n' + currentLine + '\n```';
|
|
151
|
+
}
|
|
152
|
+
// Add formatted line to text block
|
|
153
|
+
currentTextBlock += currentLine;
|
|
154
|
+
// Add newline unless it's code block (already has newlines)
|
|
155
|
+
if (!item.attributes['code-block']) {
|
|
156
|
+
currentTextBlock += '\n';
|
|
157
|
+
}
|
|
158
|
+
currentLine = ""; // Reset for next line
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
// Regular text with inline formatting
|
|
162
|
+
let formattedText = item.text;
|
|
163
|
+
// Determine current and next formatting state
|
|
164
|
+
const hasBold = item.attributes?.bold === true;
|
|
165
|
+
const hasItalic = item.attributes?.italic === true;
|
|
166
|
+
const hasLink = item.attributes?.link;
|
|
167
|
+
// Look ahead to next non-newline block
|
|
168
|
+
let nextHasBold = false;
|
|
169
|
+
let nextHasItalic = false;
|
|
170
|
+
for (let j = i + 1; j < textItems.length; j++) {
|
|
171
|
+
const nextItem = textItems[j];
|
|
172
|
+
if (nextItem.text !== '\n' || !nextItem.attributes) {
|
|
173
|
+
nextHasBold = nextItem.attributes?.bold === true;
|
|
174
|
+
nextHasItalic = nextItem.attributes?.italic === true;
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// Build prefix (open new formatting)
|
|
179
|
+
let prefix = "";
|
|
180
|
+
if (hasBold && !activeBold)
|
|
181
|
+
prefix += "**";
|
|
182
|
+
if (hasItalic && !activeItalic)
|
|
183
|
+
prefix += "*";
|
|
184
|
+
// Build suffix (close formatting that won't continue)
|
|
185
|
+
let suffix = "";
|
|
186
|
+
if (hasItalic && !nextHasItalic)
|
|
187
|
+
suffix += "*";
|
|
188
|
+
if (hasBold && !nextHasBold)
|
|
189
|
+
suffix += "**";
|
|
190
|
+
// Close formatting that's active but not in this block
|
|
191
|
+
let closingPrefix = "";
|
|
192
|
+
if (activeBold && !hasBold)
|
|
193
|
+
closingPrefix += "**";
|
|
194
|
+
if (activeItalic && !hasItalic)
|
|
195
|
+
closingPrefix += "*";
|
|
196
|
+
formattedText = closingPrefix + prefix + formattedText + suffix;
|
|
197
|
+
// Update state
|
|
198
|
+
activeBold = hasBold && nextHasBold;
|
|
199
|
+
activeItalic = hasItalic && nextHasItalic;
|
|
200
|
+
// Link formatting (wraps everything)
|
|
201
|
+
if (hasLink) {
|
|
202
|
+
formattedText = `[${formattedText}](${hasLink})`;
|
|
203
|
+
}
|
|
204
|
+
// Code formatting
|
|
205
|
+
if (item.attributes?.code) {
|
|
206
|
+
formattedText = `\`${formattedText}\``;
|
|
207
|
+
}
|
|
208
|
+
// Add to current line (not text block yet)
|
|
209
|
+
if (item.text === '\n') {
|
|
210
|
+
// Plain newline without formatting
|
|
211
|
+
currentTextBlock += currentLine + '\n';
|
|
212
|
+
currentLine = "";
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
currentLine += formattedText;
|
|
216
|
+
}
|
|
104
217
|
}
|
|
105
218
|
// Handle other types of items like bookmarks or whatever clickup can think of
|
|
106
219
|
else {
|
|
@@ -108,6 +221,9 @@ async function processClickUpText(textItems) {
|
|
|
108
221
|
}
|
|
109
222
|
}
|
|
110
223
|
// Add any remaining text
|
|
224
|
+
if (currentLine) {
|
|
225
|
+
currentTextBlock += currentLine;
|
|
226
|
+
}
|
|
111
227
|
if (currentTextBlock.trim()) {
|
|
112
228
|
contentBlocks.push({
|
|
113
229
|
type: "text",
|
|
@@ -122,7 +238,7 @@ async function processClickUpText(textItems) {
|
|
|
122
238
|
* @param attachments Array of attachments from the Clickup API
|
|
123
239
|
* @returns Array of content blocks (text and images)
|
|
124
240
|
*/
|
|
125
|
-
function
|
|
241
|
+
function convertMarkdownToToolCallResult(markdownText, attachments) {
|
|
126
242
|
const contentBlocks = [];
|
|
127
243
|
let currentTextBlock = "";
|
|
128
244
|
// Create a map of attachment URLs to their full info for easy lookup
|
|
@@ -264,3 +380,184 @@ function extractFileTypeFromUrl(url) {
|
|
|
264
380
|
return null;
|
|
265
381
|
return filename.substring(lastDot + 1);
|
|
266
382
|
}
|
|
383
|
+
/**
|
|
384
|
+
* Convert markdown text to ClickUp comment blocks format using remark
|
|
385
|
+
* Supports: headers, bold, italic, code, links, lists, blockquotes, code blocks
|
|
386
|
+
*
|
|
387
|
+
* @param markdown The markdown text to convert
|
|
388
|
+
* @returns Array of ClickUp comment blocks
|
|
389
|
+
*/
|
|
390
|
+
function convertMarkdownToClickUpBlocks(markdown) {
|
|
391
|
+
const blocks = [];
|
|
392
|
+
try {
|
|
393
|
+
// Parse the entire markdown document using remark with GFM support (for task lists)
|
|
394
|
+
const tree = (0, unified_1.unified)()
|
|
395
|
+
.use(remark_parse_1.default)
|
|
396
|
+
.use(remark_gfm_1.default)
|
|
397
|
+
.parse(markdown);
|
|
398
|
+
// Walk the tree recursively
|
|
399
|
+
walkMdastNodes(tree.children, {}, blocks);
|
|
400
|
+
}
|
|
401
|
+
catch (error) {
|
|
402
|
+
console.error('Failed to parse markdown:', error);
|
|
403
|
+
// Fallback to plain text
|
|
404
|
+
return [{ text: markdown, attributes: {} }];
|
|
405
|
+
}
|
|
406
|
+
return blocks;
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Recursively walk mdast nodes and convert to ClickUp blocks
|
|
410
|
+
* @param nodes Array of mdast nodes to process
|
|
411
|
+
* @param inheritedAttrs Formatting attributes inherited from parent nodes
|
|
412
|
+
* @param blocks Output array to append ClickUp blocks to
|
|
413
|
+
* @param depth Nesting depth for lists (0 = top level, 1 = first nest, etc.)
|
|
414
|
+
*/
|
|
415
|
+
function walkMdastNodes(nodes, inheritedAttrs, blocks, depth = 0) {
|
|
416
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
417
|
+
const node = nodes[i];
|
|
418
|
+
const currentAttrs = { ...inheritedAttrs };
|
|
419
|
+
switch (node.type) {
|
|
420
|
+
case 'heading':
|
|
421
|
+
// Process heading content with inline formatting
|
|
422
|
+
walkPhrasingContent(node.children, currentAttrs, blocks);
|
|
423
|
+
// Add newline with header attribute
|
|
424
|
+
blocks.push({ text: '\n', attributes: { header: node.depth } });
|
|
425
|
+
break;
|
|
426
|
+
case 'paragraph':
|
|
427
|
+
// Process paragraph content with inline formatting
|
|
428
|
+
walkPhrasingContent(node.children, currentAttrs, blocks);
|
|
429
|
+
// Add newline unless it's the last node
|
|
430
|
+
if (i < nodes.length - 1) {
|
|
431
|
+
blocks.push({ text: '\n', attributes: {} });
|
|
432
|
+
}
|
|
433
|
+
break;
|
|
434
|
+
case 'blockquote':
|
|
435
|
+
// ClickUp limitation: Blockquotes only support paragraph content, not headers or lists
|
|
436
|
+
// For complex blockquote content (headers, lists), only paragraph text is preserved
|
|
437
|
+
const blockquoteChildren = node.children;
|
|
438
|
+
for (const child of blockquoteChildren) {
|
|
439
|
+
if (child.type === 'paragraph') {
|
|
440
|
+
walkPhrasingContent(child.children, currentAttrs, blocks);
|
|
441
|
+
blocks.push({ text: '\n', attributes: { blockquote: {} } });
|
|
442
|
+
}
|
|
443
|
+
// Note: Other child types (heading, list) are not supported by ClickUp blockquotes
|
|
444
|
+
// and will be silently skipped, preserving only inline paragraph content
|
|
445
|
+
}
|
|
446
|
+
break;
|
|
447
|
+
case 'list':
|
|
448
|
+
const listNode = node;
|
|
449
|
+
const listType = listNode.ordered ? 'ordered' : 'bullet';
|
|
450
|
+
for (const item of listNode.children) {
|
|
451
|
+
const listItem = item;
|
|
452
|
+
// Check if it's a checkbox item
|
|
453
|
+
const isChecked = listItem.checked === true;
|
|
454
|
+
const isUnchecked = listItem.checked === false;
|
|
455
|
+
const finalListType = isChecked ? 'checked' : isUnchecked ? 'unchecked' : listType;
|
|
456
|
+
// Process list item content
|
|
457
|
+
for (const itemChild of listItem.children) {
|
|
458
|
+
if (itemChild.type === 'paragraph') {
|
|
459
|
+
// Process paragraph content with inline formatting
|
|
460
|
+
walkPhrasingContent(itemChild.children, currentAttrs, blocks);
|
|
461
|
+
// Add newline with list formatting and optional indent
|
|
462
|
+
const listAttrs = {
|
|
463
|
+
list: { list: finalListType }
|
|
464
|
+
};
|
|
465
|
+
// Add indent for nested lists (depth 0 = no indent, depth 1+ = indented)
|
|
466
|
+
if (depth > 0) {
|
|
467
|
+
listAttrs.indent = depth;
|
|
468
|
+
}
|
|
469
|
+
blocks.push({ text: '\n', attributes: listAttrs });
|
|
470
|
+
}
|
|
471
|
+
else if (itemChild.type === 'list') {
|
|
472
|
+
// Nested list - recursively process with increased depth
|
|
473
|
+
walkMdastNodes([itemChild], currentAttrs, blocks, depth + 1);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
break;
|
|
478
|
+
case 'code':
|
|
479
|
+
// Code block
|
|
480
|
+
const codeNode = node;
|
|
481
|
+
if (codeNode.value) {
|
|
482
|
+
blocks.push({ text: codeNode.value, attributes: {} });
|
|
483
|
+
blocks.push({
|
|
484
|
+
text: '\n',
|
|
485
|
+
attributes: { 'code-block': { 'code-block': codeNode.lang || 'plain' } }
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
break;
|
|
489
|
+
case 'thematicBreak':
|
|
490
|
+
// Horizontal rule - just add a line break
|
|
491
|
+
blocks.push({ text: '\n', attributes: {} });
|
|
492
|
+
break;
|
|
493
|
+
default:
|
|
494
|
+
// For any other block-level nodes, try to process children
|
|
495
|
+
if ('children' in node && Array.isArray(node.children)) {
|
|
496
|
+
walkMdastNodes(node.children, currentAttrs, blocks, depth);
|
|
497
|
+
}
|
|
498
|
+
break;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Recursively walk phrasing content (inline nodes) and build ClickUp blocks
|
|
504
|
+
* Accumulates formatting attributes from parent nodes
|
|
505
|
+
*/
|
|
506
|
+
function walkPhrasingContent(nodes, inheritedAttrs, blocks) {
|
|
507
|
+
for (const node of nodes) {
|
|
508
|
+
const currentAttrs = { ...inheritedAttrs };
|
|
509
|
+
switch (node.type) {
|
|
510
|
+
case 'text':
|
|
511
|
+
// Plain text node
|
|
512
|
+
if (node.value) {
|
|
513
|
+
blocks.push({
|
|
514
|
+
text: node.value,
|
|
515
|
+
attributes: Object.keys(currentAttrs).length > 0 ? currentAttrs : {}
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
break;
|
|
519
|
+
case 'strong':
|
|
520
|
+
// Bold text - recurse with bold attribute
|
|
521
|
+
currentAttrs.bold = true;
|
|
522
|
+
walkPhrasingContent(node.children, currentAttrs, blocks);
|
|
523
|
+
break;
|
|
524
|
+
case 'emphasis':
|
|
525
|
+
// Italic text - recurse with italic attribute
|
|
526
|
+
currentAttrs.italic = true;
|
|
527
|
+
walkPhrasingContent(node.children, currentAttrs, blocks);
|
|
528
|
+
break;
|
|
529
|
+
case 'inlineCode':
|
|
530
|
+
// Inline code
|
|
531
|
+
if (node.value) {
|
|
532
|
+
currentAttrs.code = true;
|
|
533
|
+
blocks.push({
|
|
534
|
+
text: node.value,
|
|
535
|
+
attributes: currentAttrs
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
break;
|
|
539
|
+
case 'link':
|
|
540
|
+
// Link - recurse with link attribute
|
|
541
|
+
currentAttrs.link = node.url;
|
|
542
|
+
walkPhrasingContent(node.children, currentAttrs, blocks);
|
|
543
|
+
break;
|
|
544
|
+
case 'break':
|
|
545
|
+
// Line break - add as plain text
|
|
546
|
+
blocks.push({ text: '\n', attributes: {} });
|
|
547
|
+
break;
|
|
548
|
+
default:
|
|
549
|
+
// For any other node types, try to extract text if available
|
|
550
|
+
if ('value' in node && typeof node.value === 'string') {
|
|
551
|
+
blocks.push({
|
|
552
|
+
text: node.value,
|
|
553
|
+
attributes: Object.keys(currentAttrs).length > 0 ? currentAttrs : {}
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
else if ('children' in node && Array.isArray(node.children)) {
|
|
557
|
+
// Recurse into children for other container nodes
|
|
558
|
+
walkPhrasingContent(node.children, currentAttrs, blocks);
|
|
559
|
+
}
|
|
560
|
+
break;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
@@ -34,8 +34,8 @@ function registerSpaceResources(server) {
|
|
|
34
34
|
return {
|
|
35
35
|
resources: activeSpaces.map((space) => ({
|
|
36
36
|
uri: `clickup://space/${space.id}`,
|
|
37
|
-
name: space.name
|
|
38
|
-
title: space.name
|
|
37
|
+
name: `${space.name} ClickUp Space.txt`,
|
|
38
|
+
title: `${space.name} ClickUp Space`,
|
|
39
39
|
mimeType: "text/plain"
|
|
40
40
|
}))
|
|
41
41
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doc-tools.d.ts","sourceRoot":"","sources":["../../src/tools/doc-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAoDpE,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,QA0J1D;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"doc-tools.d.ts","sourceRoot":"","sources":["../../src/tools/doc-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAoDpE,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,QA0J1D;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,QAoS3D"}
|
package/dist/tools/doc-tools.js
CHANGED
|
@@ -209,7 +209,9 @@ function registerDocumentToolsWrite(server) {
|
|
|
209
209
|
.optional()
|
|
210
210
|
.describe("Whether to append content to existing page content (default: false - replaces content)")
|
|
211
211
|
}, {
|
|
212
|
-
readOnlyHint: false
|
|
212
|
+
readOnlyHint: false,
|
|
213
|
+
destructiveHint: true,
|
|
214
|
+
idempotentHint: false,
|
|
213
215
|
}, async ({ doc_id, page_id, name, content, append = false }) => {
|
|
214
216
|
try {
|
|
215
217
|
const requestBody = {};
|
|
@@ -306,7 +308,10 @@ function registerDocumentToolsWrite(server) {
|
|
|
306
308
|
.optional()
|
|
307
309
|
.describe("Optional: page content in markdown format")
|
|
308
310
|
}, {
|
|
309
|
-
readOnlyHint: false
|
|
311
|
+
readOnlyHint: false,
|
|
312
|
+
destructiveHint: false,
|
|
313
|
+
idempotentHint: false,
|
|
314
|
+
openWorldHint: true
|
|
310
315
|
}, async ({ space_id, list_id, doc_id, parent_page_id, name, content }) => {
|
|
311
316
|
try {
|
|
312
317
|
// Validate mutually exclusive parameters
|
|
@@ -1 +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,
|
|
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"}
|
package/dist/tools/list-tools.js
CHANGED
|
@@ -118,7 +118,9 @@ function registerListToolsWrite(server) {
|
|
|
118
118
|
list_id: zod_1.z.string().min(1).describe("The list ID to update"),
|
|
119
119
|
append_description: zod_1.z.string().min(1).describe("Markdown content to APPEND to existing list description (preserves existing content for safety)")
|
|
120
120
|
}, {
|
|
121
|
-
readOnlyHint: false
|
|
121
|
+
readOnlyHint: false,
|
|
122
|
+
destructiveHint: false,
|
|
123
|
+
idempotentHint: false,
|
|
122
124
|
}, async ({ list_id, append_description }) => {
|
|
123
125
|
try {
|
|
124
126
|
// Get current list info including description (try to get markdown content)
|
package/dist/tools/task-tools.js
CHANGED
|
@@ -93,7 +93,7 @@ async function loadTaskContent(taskId) {
|
|
|
93
93
|
return await generateTaskMetadata(task, timeEntries, true);
|
|
94
94
|
})(),
|
|
95
95
|
// process markdown and download images
|
|
96
|
-
(0, clickup_text_1.
|
|
96
|
+
(0, clickup_text_1.convertMarkdownToToolCallResult)(task.markdown_description || "", task.attachments || []),
|
|
97
97
|
]);
|
|
98
98
|
return [taskMetadata, ...content];
|
|
99
99
|
}
|
|
@@ -114,7 +114,7 @@ async function loadTaskComments(id) {
|
|
|
114
114
|
type: "text",
|
|
115
115
|
text: `Comment by ${comment.user.username} on ${timestampToIso(comment.date)}:`,
|
|
116
116
|
};
|
|
117
|
-
const commentBodyBlocks = await (0, clickup_text_1.
|
|
117
|
+
const commentBodyBlocks = await (0, clickup_text_1.convertClickUpTextItemsToToolCallResult)(comment.comment);
|
|
118
118
|
return {
|
|
119
119
|
date: comment.date, // String timestamp from ClickUp for sorting
|
|
120
120
|
contentBlocks: [headerBlock, ...commentBodyBlocks],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-write-tools.d.ts","sourceRoot":"","sources":["../../src/tools/task-write-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;
|
|
1
|
+
{"version":3,"file":"task-write-tools.d.ts","sourceRoot":"","sources":["../../src/tools/task-write-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAcpE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,QAsZtE"}
|
|
@@ -4,6 +4,7 @@ exports.registerTaskToolsWrite = registerTaskToolsWrite;
|
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const config_1 = require("../shared/config");
|
|
6
6
|
const utils_1 = require("../shared/utils");
|
|
7
|
+
const clickup_text_1 = require("../clickup-text");
|
|
7
8
|
// Shared schemas for task parameters
|
|
8
9
|
const taskNameSchema = zod_1.z.string().min(1).describe("The name/title of the task");
|
|
9
10
|
const taskPrioritySchema = zod_1.z.enum(["urgent", "high", "normal", "low"]).optional().describe("Optional priority level");
|
|
@@ -31,11 +32,15 @@ function registerTaskToolsWrite(server, userData) {
|
|
|
31
32
|
task_id: zod_1.z.string().min(6).max(9).describe("The 6-9 character task ID to comment on"),
|
|
32
33
|
comment: zod_1.z.string().min(1).describe("The comment text to add to the task"),
|
|
33
34
|
}, {
|
|
34
|
-
readOnlyHint: false
|
|
35
|
+
readOnlyHint: false,
|
|
36
|
+
destructiveHint: false,
|
|
37
|
+
idempotentHint: false,
|
|
35
38
|
}, async ({ task_id, comment }) => {
|
|
36
39
|
try {
|
|
40
|
+
// Convert markdown to ClickUp formatted blocks
|
|
41
|
+
const commentBlocks = (0, clickup_text_1.convertMarkdownToClickUpBlocks)(comment);
|
|
37
42
|
const requestBody = {
|
|
38
|
-
|
|
43
|
+
comment: commentBlocks,
|
|
39
44
|
notify_all: true
|
|
40
45
|
};
|
|
41
46
|
const response = await fetch(`https://api.clickup.com/api/v2/task/${task_id}/comment`, {
|
|
@@ -111,7 +116,10 @@ function registerTaskToolsWrite(server, userData) {
|
|
|
111
116
|
blocking: zod_1.z.array(zod_1.z.string()).optional().describe("Optional array of task IDs that this task should block. Note: This creates dependencies FROM those tasks TO this task (those tasks will wait on this one)"),
|
|
112
117
|
linked_tasks: zod_1.z.array(zod_1.z.string()).optional().describe("Optional array of task IDs to link as related tasks without blocking (will replace existing linked tasks)")
|
|
113
118
|
}, {
|
|
114
|
-
readOnlyHint: false
|
|
119
|
+
readOnlyHint: false,
|
|
120
|
+
destructiveHint: true,
|
|
121
|
+
idempotentHint: false,
|
|
122
|
+
openWorldHint: true
|
|
115
123
|
}, async ({ task_id, name, append_description, status, priority, due_date, start_date, time_estimate, tags, parent_task_id, assignees, blocking, waiting_on, linked_tasks }) => {
|
|
116
124
|
try {
|
|
117
125
|
const userData = await (0, utils_1.getCurrentUser)();
|
|
@@ -289,7 +297,10 @@ function registerTaskToolsWrite(server, userData) {
|
|
|
289
297
|
parent_task_id: zod_1.z.string().optional().describe("Optional parent task ID to create this as a subtask"),
|
|
290
298
|
assignees: zod_1.z.array(zod_1.z.string()).optional().describe(createAssigneeDescription(userData))
|
|
291
299
|
}, {
|
|
292
|
-
readOnlyHint: false
|
|
300
|
+
readOnlyHint: false,
|
|
301
|
+
destructiveHint: false,
|
|
302
|
+
idempotentHint: false,
|
|
303
|
+
openWorldHint: true
|
|
293
304
|
}, async ({ list_id, name, description, status, priority, due_date, start_date, time_estimate, tags, parent_task_id, assignees }) => {
|
|
294
305
|
try {
|
|
295
306
|
const userData = await (0, utils_1.getCurrentUser)();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"time-tools.d.ts","sourceRoot":"","sources":["../../src/tools/time-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAyDpE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,QAgFtD;AAwLD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"time-tools.d.ts","sourceRoot":"","sources":["../../src/tools/time-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAyDpE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,QAgFtD;AAwLD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,QAwFvD"}
|
package/dist/tools/time-tools.js
CHANGED
|
@@ -271,7 +271,9 @@ function registerTimeToolsWrite(server) {
|
|
|
271
271
|
description: zod_1.z.string().optional().describe("Optional description for the time entry"),
|
|
272
272
|
start_time: zod_1.z.string().optional().describe("Optional start time as ISO date string (e.g., '2024-10-06T09:00:00+02:00', defaults to current time)")
|
|
273
273
|
}, {
|
|
274
|
-
readOnlyHint: false
|
|
274
|
+
readOnlyHint: false,
|
|
275
|
+
destructiveHint: false,
|
|
276
|
+
idempotentHint: false,
|
|
275
277
|
}, async ({ task_id, hours, description, start_time }) => {
|
|
276
278
|
try {
|
|
277
279
|
// Convert hours to milliseconds (ClickUp API uses milliseconds)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hauptsache.net/clickup-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "Search, create, and retrieve tasks, add comments, and track time through natural language commands.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -42,10 +42,14 @@
|
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@modelcontextprotocol/sdk": "^1.15.1",
|
|
44
44
|
"fuse.js": "^7.1.0",
|
|
45
|
+
"remark-gfm": "^4.0.1",
|
|
46
|
+
"remark-parse": "^11.0.0",
|
|
47
|
+
"unified": "^11.0.5",
|
|
45
48
|
"zod": "^3.24.2"
|
|
46
49
|
},
|
|
47
50
|
"devDependencies": {
|
|
48
51
|
"@anthropic-ai/mcpb": "^1.1.1",
|
|
52
|
+
"@types/mdast": "^4.0.4",
|
|
49
53
|
"@types/node": "^22.14.1",
|
|
50
54
|
"dotenv": "^16.5.0",
|
|
51
55
|
"nodemon": "^3.1.9",
|