@taazkareem/clickup-mcp-server 0.4.48 → 0.4.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Dockerfile.smithery +36 -0
- package/LICENSE +8 -2
- package/README.md +74 -211
- package/build/config.js +4 -16
- package/build/index.js +529 -302
- package/build/services/clickup.js +637 -80
- package/package.json +10 -10
- package/smithery.yaml +23 -0
- package/build/handlers/prompts.js +0 -88
- package/build/handlers/tools.js +0 -38
- package/build/utils/resolvers.js +0 -48
package/build/index.js
CHANGED
|
@@ -1,136 +1,54 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ClickUp MCP Server - A Model Context Protocol server for ClickUp integration
|
|
4
|
+
*
|
|
5
|
+
* This server enables AI applications to interact with ClickUp through a standardized protocol.
|
|
6
|
+
* Key capabilities include:
|
|
7
|
+
*
|
|
8
|
+
* Tools:
|
|
9
|
+
* - Task Management: Create, update, move and duplicate tasks
|
|
10
|
+
* - Bulk task creation and management
|
|
11
|
+
* - Workspace Organization: Create lists, folders and manage hierarchy
|
|
12
|
+
* - Smart lookups by name or ID with case-insensitive matching
|
|
13
|
+
*
|
|
14
|
+
* Prompts:
|
|
15
|
+
* - Task summarization and status grouping
|
|
16
|
+
* - Priority analysis and optimization
|
|
17
|
+
* - Detailed task description generation
|
|
18
|
+
* - Task relationship insights
|
|
19
|
+
*
|
|
20
|
+
* Features markdown support, secure credential handling, and comprehensive error reporting.
|
|
21
|
+
*/
|
|
2
22
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
23
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
-
import { CallToolRequestSchema,
|
|
24
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
25
|
import { ClickUpService } from "./services/clickup.js";
|
|
6
26
|
import config from "./config.js";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
teamId: config.teamId || 'missing'
|
|
14
|
-
});
|
|
15
|
-
// Create and configure the server
|
|
27
|
+
// Initialize ClickUp service
|
|
28
|
+
const clickup = ClickUpService.initialize(config.clickupApiKey, config.clickupTeamId);
|
|
29
|
+
/**
|
|
30
|
+
* Create an MCP server with capabilities for tools and prompts.
|
|
31
|
+
* Resources have been removed as they are being replaced with direct tool calls.
|
|
32
|
+
*/
|
|
16
33
|
const server = new Server({
|
|
17
|
-
name: "clickup",
|
|
34
|
+
name: "clickup-mcp-server",
|
|
18
35
|
version: "0.1.0",
|
|
19
|
-
description: "ClickUp MCP Server"
|
|
20
36
|
}, {
|
|
21
37
|
capabilities: {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
schemes: ["clickup"]
|
|
26
|
-
},
|
|
27
|
-
tools: {
|
|
28
|
-
canExecute: true,
|
|
29
|
-
tools: {
|
|
30
|
-
workspace_hierarchy: {
|
|
31
|
-
description: "List complete hierarchy of the ClickUp workspace"
|
|
32
|
-
},
|
|
33
|
-
create_task: {
|
|
34
|
-
description: "Create a new task in ClickUp"
|
|
35
|
-
},
|
|
36
|
-
create_bulk_tasks: {
|
|
37
|
-
description: "Create multiple tasks in a ClickUp list"
|
|
38
|
-
},
|
|
39
|
-
create_list: {
|
|
40
|
-
description: "Create a new list in a ClickUp space"
|
|
41
|
-
},
|
|
42
|
-
create_folder: {
|
|
43
|
-
description: "Create a new folder in a ClickUp space"
|
|
44
|
-
},
|
|
45
|
-
create_list_in_folder: {
|
|
46
|
-
description: "Create a new list in a ClickUp folder"
|
|
47
|
-
},
|
|
48
|
-
move_task: {
|
|
49
|
-
description: "Move a task to a different list"
|
|
50
|
-
},
|
|
51
|
-
duplicate_task: {
|
|
52
|
-
description: "Duplicate a task to a list"
|
|
53
|
-
},
|
|
54
|
-
update_task: {
|
|
55
|
-
description: "Update an existing task in ClickUp"
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
|
-
prompts: {
|
|
60
|
-
canExecute: true,
|
|
61
|
-
prompts: {
|
|
62
|
-
summarize_tasks: {
|
|
63
|
-
description: "Summarize all ClickUp tasks"
|
|
64
|
-
},
|
|
65
|
-
analyze_task_priorities: {
|
|
66
|
-
description: "Analyze task priorities"
|
|
67
|
-
},
|
|
68
|
-
generate_description: {
|
|
69
|
-
description: "Generate a detailed task description"
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
// Initialize ClickUp service
|
|
76
|
-
console.log('Initializing ClickUp service...');
|
|
77
|
-
const clickup = ClickUpService.initialize(config.clickupApiKey);
|
|
78
|
-
console.log('Creating MCP server...');
|
|
79
|
-
console.log('MCP server created');
|
|
80
|
-
/**
|
|
81
|
-
* Handler for listing available ClickUp tasks as resources.
|
|
82
|
-
*/
|
|
83
|
-
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
84
|
-
console.log('Handling ListResources request');
|
|
85
|
-
try {
|
|
86
|
-
const { tasks, spaces } = await getAllTasks(clickup, config.teamId);
|
|
87
|
-
return {
|
|
88
|
-
resources: tasks.map(task => ({
|
|
89
|
-
uri: `clickup://task/${task.id}`,
|
|
90
|
-
mimeType: "application/json",
|
|
91
|
-
name: task.name,
|
|
92
|
-
description: task.description || `Task in ${task.list.name} (${task.space.name})`,
|
|
93
|
-
tags: []
|
|
94
|
-
}))
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
catch (error) {
|
|
98
|
-
console.error('Error in ListResources:', error);
|
|
99
|
-
throw error;
|
|
100
|
-
}
|
|
38
|
+
tools: {},
|
|
39
|
+
prompts: {},
|
|
40
|
+
},
|
|
101
41
|
});
|
|
102
42
|
/**
|
|
103
|
-
* Handler
|
|
104
|
-
|
|
105
|
-
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
106
|
-
try {
|
|
107
|
-
const url = new URL(request.params.uri);
|
|
108
|
-
const taskId = url.pathname.replace(/^\/task\//, '');
|
|
109
|
-
const task = await clickup.getTask(taskId);
|
|
110
|
-
return {
|
|
111
|
-
contents: [{
|
|
112
|
-
uri: request.params.uri,
|
|
113
|
-
mimeType: "application/json",
|
|
114
|
-
text: JSON.stringify(task, null, 2),
|
|
115
|
-
tags: []
|
|
116
|
-
}]
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
catch (error) {
|
|
120
|
-
console.error('Error reading resource:', error);
|
|
121
|
-
throw error;
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
/**
|
|
125
|
-
* Handler for listing available tools.
|
|
43
|
+
* Handler that lists available tools.
|
|
44
|
+
* Exposes tools for listing spaces, creating tasks, and updating tasks.
|
|
126
45
|
*/
|
|
127
46
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
128
|
-
console.log('Handling ListTools request');
|
|
129
47
|
return {
|
|
130
48
|
tools: [
|
|
131
49
|
{
|
|
132
|
-
name: "
|
|
133
|
-
description: "
|
|
50
|
+
name: "get_workspace_hierarchy",
|
|
51
|
+
description: "Get the complete hierarchy of spaces, folders, and lists in the workspace. Important: If looking up information, first check chat history for space, folder, and list names or ID matches. If not found, use this tool to get necessary information.",
|
|
134
52
|
inputSchema: {
|
|
135
53
|
type: "object",
|
|
136
54
|
properties: {},
|
|
@@ -139,37 +57,41 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
139
57
|
},
|
|
140
58
|
{
|
|
141
59
|
name: "create_task",
|
|
142
|
-
description: "Create a new task in ClickUp",
|
|
60
|
+
description: "Create a new task in ClickUp. Supports direct name-based lookup for lists - no need to know the list ID. Status will use ClickUp defaults if not specified. If the specified list doesn't exist, you can create it using create_list or create_list_in_folder.",
|
|
143
61
|
inputSchema: {
|
|
144
62
|
type: "object",
|
|
145
63
|
properties: {
|
|
146
64
|
listId: {
|
|
147
65
|
type: "string",
|
|
148
|
-
description: "ID of the list to create the task in (optional if listName
|
|
66
|
+
description: "ID of the list to create the task in (optional if using listName instead)"
|
|
149
67
|
},
|
|
150
68
|
listName: {
|
|
151
69
|
type: "string",
|
|
152
|
-
description: "Name of the list to create the task in (optional if listId
|
|
70
|
+
description: "Name of the list to create the task in - will automatically find the list by name (optional if using listId instead)"
|
|
153
71
|
},
|
|
154
72
|
name: {
|
|
155
73
|
type: "string",
|
|
156
|
-
description: "Name of the task"
|
|
74
|
+
description: "Name of the task. Put a relevant emoji followed by a blank space before the name."
|
|
157
75
|
},
|
|
158
76
|
description: {
|
|
159
77
|
type: "string",
|
|
160
|
-
description: "
|
|
78
|
+
description: "Plain text description for the task"
|
|
79
|
+
},
|
|
80
|
+
markdown_description: {
|
|
81
|
+
type: "string",
|
|
82
|
+
description: "Markdown formatted description for the task. If provided, this takes precedence over description"
|
|
161
83
|
},
|
|
162
84
|
status: {
|
|
163
85
|
type: "string",
|
|
164
|
-
description: "
|
|
86
|
+
description: "OPTIONAL: Override the default ClickUp status. In most cases, you should omit this to use ClickUp defaults"
|
|
165
87
|
},
|
|
166
88
|
priority: {
|
|
167
89
|
type: "number",
|
|
168
|
-
description: "Priority of the task (1-4)"
|
|
90
|
+
description: "Priority of the task (1-4), 1 is urgent/highest priority, 4 is lowest priority. Only set this if priority is explicitly mentioned in the user's request."
|
|
169
91
|
},
|
|
170
92
|
dueDate: {
|
|
171
93
|
type: "string",
|
|
172
|
-
description: "Due date of the task (
|
|
94
|
+
description: "Due date of the task (Unix timestamp in milliseconds)"
|
|
173
95
|
}
|
|
174
96
|
},
|
|
175
97
|
required: ["name"]
|
|
@@ -177,17 +99,17 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
177
99
|
},
|
|
178
100
|
{
|
|
179
101
|
name: "create_bulk_tasks",
|
|
180
|
-
description: "Create multiple tasks in a ClickUp list",
|
|
102
|
+
description: "Create multiple tasks in a ClickUp list. Supports direct name-based lookup for lists - no need to know the list ID. Tasks will use ClickUp default status if not specified. If the specified list doesn't exist, you can create it using create_list or create_list_in_folder.",
|
|
181
103
|
inputSchema: {
|
|
182
104
|
type: "object",
|
|
183
105
|
properties: {
|
|
184
106
|
listId: {
|
|
185
107
|
type: "string",
|
|
186
|
-
description: "ID of the list to create the tasks in (optional if listName
|
|
108
|
+
description: "ID of the list to create the tasks in (optional if using listName instead)"
|
|
187
109
|
},
|
|
188
110
|
listName: {
|
|
189
111
|
type: "string",
|
|
190
|
-
description: "Name of the list to create the tasks in (optional if listId
|
|
112
|
+
description: "Name of the list to create the tasks in - will automatically find the list by name (optional if using listId instead)"
|
|
191
113
|
},
|
|
192
114
|
tasks: {
|
|
193
115
|
type: "array",
|
|
@@ -201,19 +123,23 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
201
123
|
},
|
|
202
124
|
description: {
|
|
203
125
|
type: "string",
|
|
204
|
-
description: "
|
|
126
|
+
description: "Plain text description for the task"
|
|
127
|
+
},
|
|
128
|
+
markdown_description: {
|
|
129
|
+
type: "string",
|
|
130
|
+
description: "Markdown formatted description for the task. If provided, this takes precedence over description"
|
|
205
131
|
},
|
|
206
132
|
status: {
|
|
207
133
|
type: "string",
|
|
208
|
-
description: "
|
|
134
|
+
description: "OPTIONAL: Override the default ClickUp status. In most cases, you should omit this to use ClickUp defaults"
|
|
209
135
|
},
|
|
210
136
|
priority: {
|
|
211
137
|
type: "number",
|
|
212
|
-
description: "Priority level (1-4)"
|
|
138
|
+
description: "Priority level (1-4), 1 is urgent/highest priority, 4 is lowest priority. Only set this if priority is explicitly mentioned in the user's request."
|
|
213
139
|
},
|
|
214
140
|
dueDate: {
|
|
215
141
|
type: "string",
|
|
216
|
-
description: "Due date (
|
|
142
|
+
description: "Due date (Unix timestamp in milliseconds)"
|
|
217
143
|
},
|
|
218
144
|
assignees: {
|
|
219
145
|
type: "array",
|
|
@@ -227,22 +153,22 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
227
153
|
}
|
|
228
154
|
}
|
|
229
155
|
},
|
|
230
|
-
required: ["tasks"]
|
|
156
|
+
required: ["listId", "tasks"]
|
|
231
157
|
}
|
|
232
158
|
},
|
|
233
159
|
{
|
|
234
160
|
name: "create_list",
|
|
235
|
-
description: "Create a new list in a ClickUp space",
|
|
161
|
+
description: "Create a new list in a ClickUp space. Supports direct name-based lookup for spaces - no need to know the space ID. If the specified space doesn't exist, you can create it through the ClickUp web interface (space creation via API not supported).",
|
|
236
162
|
inputSchema: {
|
|
237
163
|
type: "object",
|
|
238
164
|
properties: {
|
|
239
165
|
spaceId: {
|
|
240
166
|
type: "string",
|
|
241
|
-
description: "ID of the space to create the list in (optional if spaceName
|
|
167
|
+
description: "ID of the space to create the list in (optional if using spaceName instead)"
|
|
242
168
|
},
|
|
243
169
|
spaceName: {
|
|
244
170
|
type: "string",
|
|
245
|
-
description: "Name of the space to create the list in (optional if spaceId
|
|
171
|
+
description: "Name of the space to create the list in - will automatically find the space by name (optional if using spaceId instead)"
|
|
246
172
|
},
|
|
247
173
|
name: {
|
|
248
174
|
type: "string",
|
|
@@ -258,7 +184,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
258
184
|
},
|
|
259
185
|
priority: {
|
|
260
186
|
type: "number",
|
|
261
|
-
description: "Priority of the list (1-4)"
|
|
187
|
+
description: "Priority of the list (1-4). Only set this if priority is explicitly mentioned in the user's request."
|
|
262
188
|
},
|
|
263
189
|
assignee: {
|
|
264
190
|
type: "number",
|
|
@@ -274,17 +200,17 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
274
200
|
},
|
|
275
201
|
{
|
|
276
202
|
name: "create_folder",
|
|
277
|
-
description: "Create a new folder in a ClickUp space",
|
|
203
|
+
description: "Create a new folder in a ClickUp space. Supports direct name-based lookup for spaces - no need to know the space ID. If the specified space doesn't exist, you can create it through the ClickUp web interface (space creation via API not supported).",
|
|
278
204
|
inputSchema: {
|
|
279
205
|
type: "object",
|
|
280
206
|
properties: {
|
|
281
207
|
spaceId: {
|
|
282
208
|
type: "string",
|
|
283
|
-
description: "ID of the space to create the folder in (optional if spaceName
|
|
209
|
+
description: "ID of the space to create the folder in (optional if using spaceName instead)"
|
|
284
210
|
},
|
|
285
211
|
spaceName: {
|
|
286
212
|
type: "string",
|
|
287
|
-
description: "Name of the space to create the folder in (optional if spaceId
|
|
213
|
+
description: "Name of the space to create the folder in - will automatically find the space by name (optional if using spaceId instead)"
|
|
288
214
|
},
|
|
289
215
|
name: {
|
|
290
216
|
type: "string",
|
|
@@ -300,25 +226,25 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
300
226
|
},
|
|
301
227
|
{
|
|
302
228
|
name: "create_list_in_folder",
|
|
303
|
-
description: "Create a new list in a ClickUp folder",
|
|
229
|
+
description: "Create a new list in a ClickUp folder. Supports direct name-based lookup for folders and spaces - no need to know IDs. If the specified folder doesn't exist, you can create it using create_folder. If the space doesn't exist, it must be created through the ClickUp web interface.",
|
|
304
230
|
inputSchema: {
|
|
305
231
|
type: "object",
|
|
306
232
|
properties: {
|
|
307
233
|
folderId: {
|
|
308
234
|
type: "string",
|
|
309
|
-
description: "ID of the folder to create the list in (optional if folderName
|
|
235
|
+
description: "ID of the folder to create the list in (optional if using folderName instead)"
|
|
310
236
|
},
|
|
311
237
|
folderName: {
|
|
312
238
|
type: "string",
|
|
313
|
-
description: "Name of the folder to create the list in"
|
|
239
|
+
description: "Name of the folder to create the list in - will automatically find the folder by name (optional if using folderId instead)"
|
|
314
240
|
},
|
|
315
241
|
spaceId: {
|
|
316
242
|
type: "string",
|
|
317
|
-
description: "ID of the space containing the folder (
|
|
243
|
+
description: "ID of the space containing the folder (optional if using spaceName instead)"
|
|
318
244
|
},
|
|
319
245
|
spaceName: {
|
|
320
246
|
type: "string",
|
|
321
|
-
description: "Name of the space containing the folder (
|
|
247
|
+
description: "Name of the space containing the folder - will automatically find the space by name (optional if using spaceId instead)"
|
|
322
248
|
},
|
|
323
249
|
name: {
|
|
324
250
|
type: "string",
|
|
@@ -338,57 +264,81 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
338
264
|
},
|
|
339
265
|
{
|
|
340
266
|
name: "move_task",
|
|
341
|
-
description: "Move a task to a different list",
|
|
267
|
+
description: "Move a task to a different list. Supports direct name-based lookup for lists and tasks - no need to know IDs.",
|
|
342
268
|
inputSchema: {
|
|
343
269
|
type: "object",
|
|
344
270
|
properties: {
|
|
345
271
|
taskId: {
|
|
346
272
|
type: "string",
|
|
347
|
-
description: "ID of the task to move"
|
|
273
|
+
description: "ID of the task to move (optional if using taskName instead)"
|
|
274
|
+
},
|
|
275
|
+
taskName: {
|
|
276
|
+
type: "string",
|
|
277
|
+
description: "Name of the task to move - will automatically find the task by name (optional if using taskId instead)"
|
|
278
|
+
},
|
|
279
|
+
sourceListName: {
|
|
280
|
+
type: "string",
|
|
281
|
+
description: "Optional: Name of the list to narrow down task search"
|
|
348
282
|
},
|
|
349
283
|
listId: {
|
|
350
284
|
type: "string",
|
|
351
|
-
description: "ID of the destination list (optional if listName
|
|
285
|
+
description: "ID of the destination list (optional if using listName instead)"
|
|
352
286
|
},
|
|
353
287
|
listName: {
|
|
354
288
|
type: "string",
|
|
355
|
-
description: "Name of the destination list (optional if listId
|
|
289
|
+
description: "Name of the destination list - will automatically find the list by name (optional if using listId instead)"
|
|
356
290
|
}
|
|
357
291
|
},
|
|
358
|
-
required: [
|
|
292
|
+
required: []
|
|
359
293
|
}
|
|
360
294
|
},
|
|
361
295
|
{
|
|
362
296
|
name: "duplicate_task",
|
|
363
|
-
description: "Duplicate a task to a list",
|
|
297
|
+
description: "Duplicate a task to a list. Supports direct name-based lookup for lists - no need to know the list ID. If the destination list doesn't exist, you can create it using create_list or create_list_in_folder.",
|
|
364
298
|
inputSchema: {
|
|
365
299
|
type: "object",
|
|
366
300
|
properties: {
|
|
367
301
|
taskId: {
|
|
368
302
|
type: "string",
|
|
369
|
-
description: "ID of the task to duplicate"
|
|
303
|
+
description: "ID of the task to duplicate (optional if using taskName instead)"
|
|
304
|
+
},
|
|
305
|
+
taskName: {
|
|
306
|
+
type: "string",
|
|
307
|
+
description: "Name of the task to duplicate - will automatically find the task by name (optional if using taskId instead)"
|
|
308
|
+
},
|
|
309
|
+
sourceListName: {
|
|
310
|
+
type: "string",
|
|
311
|
+
description: "Optional: Name of the list to narrow down task search"
|
|
370
312
|
},
|
|
371
313
|
listId: {
|
|
372
314
|
type: "string",
|
|
373
|
-
description: "ID of the
|
|
315
|
+
description: "ID of the list to create the duplicate in (optional if using listName instead)"
|
|
374
316
|
},
|
|
375
317
|
listName: {
|
|
376
318
|
type: "string",
|
|
377
|
-
description: "Name of the
|
|
319
|
+
description: "Name of the list to create the duplicate in - will automatically find the list by name (optional if using listId instead)"
|
|
378
320
|
}
|
|
379
321
|
},
|
|
380
|
-
required: [
|
|
322
|
+
required: []
|
|
381
323
|
}
|
|
382
324
|
},
|
|
383
325
|
{
|
|
384
326
|
name: "update_task",
|
|
385
|
-
description: "Update an existing task in ClickUp",
|
|
327
|
+
description: "Update an existing task in ClickUp. Supports direct name-based lookup for tasks - no need to know the task ID.",
|
|
386
328
|
inputSchema: {
|
|
387
329
|
type: "object",
|
|
388
330
|
properties: {
|
|
389
331
|
taskId: {
|
|
390
332
|
type: "string",
|
|
391
|
-
description: "ID of the task to update"
|
|
333
|
+
description: "ID of the task to update (optional if using taskName instead)"
|
|
334
|
+
},
|
|
335
|
+
taskName: {
|
|
336
|
+
type: "string",
|
|
337
|
+
description: "Name of the task to update - will automatically find the task by name (optional if using taskId instead)"
|
|
338
|
+
},
|
|
339
|
+
listName: {
|
|
340
|
+
type: "string",
|
|
341
|
+
description: "Optional: Name of the list to narrow down task search"
|
|
392
342
|
},
|
|
393
343
|
name: {
|
|
394
344
|
type: "string",
|
|
@@ -404,13 +354,129 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
404
354
|
},
|
|
405
355
|
priority: {
|
|
406
356
|
type: "number",
|
|
407
|
-
description: "New priority of the task (1-4)"
|
|
357
|
+
description: "New priority of the task (1-4). Only set this if priority is explicitly mentioned in the user's request."
|
|
408
358
|
},
|
|
409
359
|
dueDate: {
|
|
410
360
|
type: "string",
|
|
411
361
|
description: "New due date of the task (ISO string)"
|
|
412
362
|
}
|
|
413
363
|
},
|
|
364
|
+
required: []
|
|
365
|
+
}
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
name: "get_tasks",
|
|
369
|
+
description: "Get tasks from a ClickUp list with optional filters. Supports direct name-based lookup for lists - no need to know the list ID. If the list doesn't exist, you can create it using create_list or create_list_in_folder. ",
|
|
370
|
+
inputSchema: {
|
|
371
|
+
type: "object",
|
|
372
|
+
properties: {
|
|
373
|
+
listId: {
|
|
374
|
+
type: "string",
|
|
375
|
+
description: "ID of the list to get tasks from (optional if using listName instead)"
|
|
376
|
+
},
|
|
377
|
+
listName: {
|
|
378
|
+
type: "string",
|
|
379
|
+
description: "Name of the list to get tasks from - will automatically find the list by name (optional if using listId instead)"
|
|
380
|
+
},
|
|
381
|
+
archived: {
|
|
382
|
+
type: "boolean",
|
|
383
|
+
description: "Include archived tasks"
|
|
384
|
+
},
|
|
385
|
+
page: {
|
|
386
|
+
type: "number",
|
|
387
|
+
description: "Page number for pagination"
|
|
388
|
+
},
|
|
389
|
+
order_by: {
|
|
390
|
+
type: "string",
|
|
391
|
+
description: "Field to order tasks by"
|
|
392
|
+
},
|
|
393
|
+
reverse: {
|
|
394
|
+
type: "boolean",
|
|
395
|
+
description: "Reverse the order of tasks"
|
|
396
|
+
},
|
|
397
|
+
subtasks: {
|
|
398
|
+
type: "boolean",
|
|
399
|
+
description: "Include subtasks"
|
|
400
|
+
},
|
|
401
|
+
statuses: {
|
|
402
|
+
type: "array",
|
|
403
|
+
items: { type: "string" },
|
|
404
|
+
description: "Filter tasks by status"
|
|
405
|
+
},
|
|
406
|
+
include_closed: {
|
|
407
|
+
type: "boolean",
|
|
408
|
+
description: "Include closed tasks"
|
|
409
|
+
},
|
|
410
|
+
assignees: {
|
|
411
|
+
type: "array",
|
|
412
|
+
items: { type: "string" },
|
|
413
|
+
description: "Filter tasks by assignee IDs"
|
|
414
|
+
},
|
|
415
|
+
due_date_gt: {
|
|
416
|
+
type: "number",
|
|
417
|
+
description: "Filter tasks due after this timestamp"
|
|
418
|
+
},
|
|
419
|
+
due_date_lt: {
|
|
420
|
+
type: "number",
|
|
421
|
+
description: "Filter tasks due before this timestamp"
|
|
422
|
+
},
|
|
423
|
+
date_created_gt: {
|
|
424
|
+
type: "number",
|
|
425
|
+
description: "Filter tasks created after this timestamp"
|
|
426
|
+
},
|
|
427
|
+
date_created_lt: {
|
|
428
|
+
type: "number",
|
|
429
|
+
description: "Filter tasks created before this timestamp"
|
|
430
|
+
},
|
|
431
|
+
date_updated_gt: {
|
|
432
|
+
type: "number",
|
|
433
|
+
description: "Filter tasks updated after this timestamp"
|
|
434
|
+
},
|
|
435
|
+
date_updated_lt: {
|
|
436
|
+
type: "number",
|
|
437
|
+
description: "Filter tasks updated before this timestamp"
|
|
438
|
+
},
|
|
439
|
+
custom_fields: {
|
|
440
|
+
type: "object",
|
|
441
|
+
description: "Filter tasks by custom field values"
|
|
442
|
+
}
|
|
443
|
+
},
|
|
444
|
+
required: []
|
|
445
|
+
}
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
name: "get_task",
|
|
449
|
+
description: "Get detailed information about a specific ClickUp task, including attachments. Supports direct name-based lookup for tasks.",
|
|
450
|
+
inputSchema: {
|
|
451
|
+
type: "object",
|
|
452
|
+
properties: {
|
|
453
|
+
taskId: {
|
|
454
|
+
type: "string",
|
|
455
|
+
description: "ID of the task to retrieve (optional if using taskName instead)"
|
|
456
|
+
},
|
|
457
|
+
taskName: {
|
|
458
|
+
type: "string",
|
|
459
|
+
description: "Name of the task to retrieve - will automatically find the task by name (optional if using taskId instead)"
|
|
460
|
+
},
|
|
461
|
+
listName: {
|
|
462
|
+
type: "string",
|
|
463
|
+
description: "Optional: Name of the list to narrow down task search"
|
|
464
|
+
}
|
|
465
|
+
},
|
|
466
|
+
required: []
|
|
467
|
+
}
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
name: "delete_task",
|
|
471
|
+
description: "Delete a task from your workspace. This action cannot be undone.",
|
|
472
|
+
inputSchema: {
|
|
473
|
+
type: "object",
|
|
474
|
+
properties: {
|
|
475
|
+
taskId: {
|
|
476
|
+
type: "string",
|
|
477
|
+
description: "ID of the task to delete"
|
|
478
|
+
}
|
|
479
|
+
},
|
|
414
480
|
required: ["taskId"]
|
|
415
481
|
}
|
|
416
482
|
}
|
|
@@ -418,23 +484,68 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
418
484
|
};
|
|
419
485
|
});
|
|
420
486
|
/**
|
|
421
|
-
* Handler for
|
|
487
|
+
* Handler for the CallToolRequestSchema.
|
|
488
|
+
* Handles the execution of tools like listing spaces, creating tasks, and updating tasks.
|
|
422
489
|
*/
|
|
423
490
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
424
491
|
try {
|
|
425
492
|
switch (request.params.name) {
|
|
426
|
-
case "
|
|
427
|
-
const
|
|
493
|
+
case "get_workspace_hierarchy": {
|
|
494
|
+
const hierarchy = await clickup.getWorkspaceHierarchy();
|
|
428
495
|
return {
|
|
429
496
|
content: [{
|
|
430
497
|
type: "text",
|
|
431
|
-
text:
|
|
498
|
+
text: JSON.stringify({
|
|
499
|
+
workspace: {
|
|
500
|
+
id: hierarchy.root.id,
|
|
501
|
+
name: hierarchy.root.name,
|
|
502
|
+
spaces: hierarchy.root.children.map((space) => ({
|
|
503
|
+
id: space.id,
|
|
504
|
+
name: space.name,
|
|
505
|
+
lists: space.children
|
|
506
|
+
.filter((node) => node.type === 'list')
|
|
507
|
+
.map((list) => ({
|
|
508
|
+
id: list.id,
|
|
509
|
+
name: list.name,
|
|
510
|
+
path: `${space.name} > ${list.name}`
|
|
511
|
+
})),
|
|
512
|
+
folders: space.children
|
|
513
|
+
.filter((node) => node.type === 'folder')
|
|
514
|
+
.map((folder) => ({
|
|
515
|
+
id: folder.id,
|
|
516
|
+
name: folder.name,
|
|
517
|
+
path: `${space.name} > ${folder.name}`,
|
|
518
|
+
lists: folder.children.map((list) => ({
|
|
519
|
+
id: list.id,
|
|
520
|
+
name: list.name,
|
|
521
|
+
path: `${space.name} > ${folder.name} > ${list.name}`
|
|
522
|
+
}))
|
|
523
|
+
}))
|
|
524
|
+
}))
|
|
525
|
+
}
|
|
526
|
+
}, null, 2)
|
|
432
527
|
}]
|
|
433
528
|
};
|
|
434
529
|
}
|
|
435
530
|
case "create_task": {
|
|
436
531
|
const args = request.params.arguments;
|
|
437
|
-
|
|
532
|
+
if (!args.listId && !args.listName) {
|
|
533
|
+
throw new Error("Either listId or listName is required");
|
|
534
|
+
}
|
|
535
|
+
if (!args.name) {
|
|
536
|
+
throw new Error("name is required");
|
|
537
|
+
}
|
|
538
|
+
let listId = args.listId;
|
|
539
|
+
if (!listId && args.listName) {
|
|
540
|
+
const hierarchy = await clickup.getWorkspaceHierarchy();
|
|
541
|
+
const result = clickup.findIDByNameInHierarchy(hierarchy, args.listName, 'list');
|
|
542
|
+
if (!result) {
|
|
543
|
+
throw new Error(`List with name "${args.listName}" not found`);
|
|
544
|
+
}
|
|
545
|
+
listId = result.id;
|
|
546
|
+
}
|
|
547
|
+
const { listId: _, listName: __, ...taskData } = args;
|
|
548
|
+
const task = await clickup.createTask(listId, taskData);
|
|
438
549
|
return {
|
|
439
550
|
content: [{
|
|
440
551
|
type: "text",
|
|
@@ -444,26 +555,26 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
444
555
|
}
|
|
445
556
|
case "create_bulk_tasks": {
|
|
446
557
|
const args = request.params.arguments;
|
|
558
|
+
if (!args.listId && !args.listName) {
|
|
559
|
+
throw new Error("Either listId or listName is required");
|
|
560
|
+
}
|
|
561
|
+
if (!args.tasks || args.tasks.length === 0) {
|
|
562
|
+
throw new Error("tasks array is required and must not be empty");
|
|
563
|
+
}
|
|
447
564
|
let listId = args.listId;
|
|
448
565
|
if (!listId && args.listName) {
|
|
449
|
-
const result = await clickup.
|
|
566
|
+
const result = await clickup.findListIDByName(args.listName);
|
|
450
567
|
if (!result) {
|
|
451
568
|
throw new Error(`List with name "${args.listName}" not found`);
|
|
452
569
|
}
|
|
453
|
-
listId = result.
|
|
454
|
-
}
|
|
455
|
-
if (!listId) {
|
|
456
|
-
throw new Error("Either listId or listName is required");
|
|
457
|
-
}
|
|
458
|
-
if (!args.tasks || !args.tasks.length) {
|
|
459
|
-
throw new Error("At least one task is required");
|
|
570
|
+
listId = result.id;
|
|
460
571
|
}
|
|
461
572
|
const { listId: _, listName: __, tasks } = args;
|
|
462
573
|
const createdTasks = await clickup.createBulkTasks(listId, { tasks });
|
|
463
574
|
return {
|
|
464
575
|
content: [{
|
|
465
576
|
type: "text",
|
|
466
|
-
text: `Created ${createdTasks.length} tasks
|
|
577
|
+
text: `Created ${createdTasks.length} tasks`
|
|
467
578
|
}]
|
|
468
579
|
};
|
|
469
580
|
}
|
|
@@ -472,41 +583,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
472
583
|
if (!args.name) {
|
|
473
584
|
throw new Error("name is required");
|
|
474
585
|
}
|
|
475
|
-
// If folder is specified, create list in folder
|
|
476
|
-
if (args.folderName || args.folderId) {
|
|
477
|
-
let folderId = args.folderId;
|
|
478
|
-
if (!folderId && args.folderName) {
|
|
479
|
-
const result = await clickup.findFolderByNameGlobally(config.teamId, args.folderName);
|
|
480
|
-
if (!result) {
|
|
481
|
-
throw new Error(`Folder with name "${args.folderName}" not found`);
|
|
482
|
-
}
|
|
483
|
-
folderId = result.folder.id;
|
|
484
|
-
}
|
|
485
|
-
if (!folderId) {
|
|
486
|
-
throw new Error("Either folderId or folderName must be provided");
|
|
487
|
-
}
|
|
488
|
-
const { spaceId: _, spaceName: __, folderName: ___, folderId: ____, ...listData } = args;
|
|
489
|
-
const list = await clickup.createListInFolder(folderId, listData);
|
|
490
|
-
return {
|
|
491
|
-
content: [{
|
|
492
|
-
type: "text",
|
|
493
|
-
text: `Created list ${list.id}: ${list.name} in folder`
|
|
494
|
-
}]
|
|
495
|
-
};
|
|
496
|
-
}
|
|
497
|
-
// Otherwise, create list in space
|
|
498
586
|
let spaceId = args.spaceId;
|
|
499
587
|
if (!spaceId && args.spaceName) {
|
|
500
|
-
const
|
|
501
|
-
if (!
|
|
588
|
+
const foundId = await clickup.findSpaceIDByName(args.spaceName);
|
|
589
|
+
if (!foundId) {
|
|
502
590
|
throw new Error(`Space with name "${args.spaceName}" not found`);
|
|
503
591
|
}
|
|
504
|
-
spaceId =
|
|
592
|
+
spaceId = foundId;
|
|
505
593
|
}
|
|
506
594
|
if (!spaceId) {
|
|
507
595
|
throw new Error("Either spaceId or spaceName must be provided");
|
|
508
596
|
}
|
|
509
|
-
const { spaceId: _, spaceName: __,
|
|
597
|
+
const { spaceId: _, spaceName: __, ...listData } = args;
|
|
510
598
|
const list = await clickup.createList(spaceId, listData);
|
|
511
599
|
return {
|
|
512
600
|
content: [{
|
|
@@ -522,11 +610,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
522
610
|
}
|
|
523
611
|
let spaceId = args.spaceId;
|
|
524
612
|
if (!spaceId && args.spaceName) {
|
|
525
|
-
const
|
|
526
|
-
if (!
|
|
613
|
+
const foundId = await clickup.findSpaceIDByName(args.spaceName);
|
|
614
|
+
if (!foundId) {
|
|
527
615
|
throw new Error(`Space with name "${args.spaceName}" not found`);
|
|
528
616
|
}
|
|
529
|
-
spaceId =
|
|
617
|
+
spaceId = foundId;
|
|
530
618
|
}
|
|
531
619
|
if (!spaceId) {
|
|
532
620
|
throw new Error("Either spaceId or spaceName must be provided");
|
|
@@ -547,94 +635,111 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
547
635
|
}
|
|
548
636
|
let folderId = args.folderId;
|
|
549
637
|
if (!folderId && args.folderName) {
|
|
550
|
-
const result = await clickup.
|
|
638
|
+
const result = await clickup.findFolderIDByName(args.folderName);
|
|
551
639
|
if (!result) {
|
|
552
640
|
throw new Error(`Folder with name "${args.folderName}" not found`);
|
|
553
641
|
}
|
|
554
|
-
folderId = result.
|
|
642
|
+
folderId = result.id;
|
|
555
643
|
}
|
|
556
644
|
if (!folderId) {
|
|
557
|
-
throw new Error("Either folderId or folderName
|
|
645
|
+
throw new Error("Either folderId or folderName must be provided");
|
|
558
646
|
}
|
|
559
|
-
const listData =
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
647
|
+
const { folderId: _, folderName: __, spaceId: ___, spaceName: ____, ...listData } = args;
|
|
648
|
+
const list = await clickup.createListInFolder(folderId, listData);
|
|
649
|
+
return {
|
|
650
|
+
content: [{
|
|
651
|
+
type: "text",
|
|
652
|
+
text: `Created list ${list.id}: ${list.name} in folder`
|
|
653
|
+
}]
|
|
563
654
|
};
|
|
564
|
-
try {
|
|
565
|
-
const list = await clickup.createListInFolder(folderId, listData);
|
|
566
|
-
return {
|
|
567
|
-
content: [{
|
|
568
|
-
type: "text",
|
|
569
|
-
text: `Created list ${list.id}: ${list.name} in folder`
|
|
570
|
-
}]
|
|
571
|
-
};
|
|
572
|
-
}
|
|
573
|
-
catch (error) {
|
|
574
|
-
throw new Error(`Failed to create list: ${error.message}`);
|
|
575
|
-
}
|
|
576
655
|
}
|
|
577
656
|
case "move_task": {
|
|
578
657
|
const args = request.params.arguments;
|
|
579
|
-
if (!args.taskId) {
|
|
580
|
-
throw new Error("taskId is required");
|
|
658
|
+
if (!args.taskId && !args.taskName) {
|
|
659
|
+
throw new Error("Either taskId or taskName is required");
|
|
660
|
+
}
|
|
661
|
+
if (!args.listId && !args.listName) {
|
|
662
|
+
throw new Error("Either listId or listName is required");
|
|
663
|
+
}
|
|
664
|
+
let taskId = args.taskId;
|
|
665
|
+
if (!taskId && args.taskName) {
|
|
666
|
+
const result = await clickup.findTaskByName(args.taskName, undefined, args.sourceListName);
|
|
667
|
+
if (!result) {
|
|
668
|
+
throw new Error(`Task with name "${args.taskName}" not found${args.sourceListName ? ` in list "${args.sourceListName}"` : ''}`);
|
|
669
|
+
}
|
|
670
|
+
taskId = result.id;
|
|
581
671
|
}
|
|
582
672
|
let listId = args.listId;
|
|
583
673
|
if (!listId && args.listName) {
|
|
584
|
-
const result = await clickup.
|
|
674
|
+
const result = await clickup.findListIDByName(args.listName);
|
|
585
675
|
if (!result) {
|
|
586
676
|
throw new Error(`List with name "${args.listName}" not found`);
|
|
587
677
|
}
|
|
588
|
-
listId = result.
|
|
589
|
-
}
|
|
590
|
-
if (!listId) {
|
|
591
|
-
throw new Error("Either listId or listName is required");
|
|
678
|
+
listId = result.id;
|
|
592
679
|
}
|
|
593
|
-
|
|
680
|
+
// Get the original task details for the response message
|
|
681
|
+
const originalTask = await clickup.getTask(taskId);
|
|
682
|
+
const newTask = await clickup.moveTask(taskId, listId);
|
|
594
683
|
return {
|
|
595
684
|
content: [{
|
|
596
685
|
type: "text",
|
|
597
|
-
text: `Moved task ${
|
|
686
|
+
text: `Moved task "${originalTask.name}" from "${originalTask.list.name}" to "${newTask.list.name}"`
|
|
598
687
|
}]
|
|
599
688
|
};
|
|
600
689
|
}
|
|
601
690
|
case "duplicate_task": {
|
|
602
691
|
const args = request.params.arguments;
|
|
603
|
-
|
|
604
|
-
|
|
692
|
+
// Require either taskId or taskName
|
|
693
|
+
if (!args.taskId && !args.taskName) {
|
|
694
|
+
throw new Error("Either taskId or taskName is required");
|
|
695
|
+
}
|
|
696
|
+
// Require either listId or listName
|
|
697
|
+
if (!args.listId && !args.listName) {
|
|
698
|
+
throw new Error("Either listId or listName is required");
|
|
699
|
+
}
|
|
700
|
+
// Get taskId from taskName if needed
|
|
701
|
+
let taskId = args.taskId;
|
|
702
|
+
if (!taskId && args.taskName) {
|
|
703
|
+
const result = await clickup.findTaskByName(args.taskName, undefined, args.sourceListName);
|
|
704
|
+
if (!result) {
|
|
705
|
+
throw new Error(`Task with name "${args.taskName}" not found${args.sourceListName ? ` in list "${args.sourceListName}"` : ''}`);
|
|
706
|
+
}
|
|
707
|
+
taskId = result.id;
|
|
605
708
|
}
|
|
709
|
+
// Get listId from listName if needed
|
|
606
710
|
let listId = args.listId;
|
|
607
711
|
if (!listId && args.listName) {
|
|
608
|
-
const result = await clickup.
|
|
712
|
+
const result = await clickup.findListIDByName(args.listName);
|
|
609
713
|
if (!result) {
|
|
610
714
|
throw new Error(`List with name "${args.listName}" not found`);
|
|
611
715
|
}
|
|
612
|
-
listId = result.
|
|
716
|
+
listId = result.id;
|
|
613
717
|
}
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
const task = await clickup.duplicateTask(args.taskId, listId);
|
|
718
|
+
// Get the original task details for the response message
|
|
719
|
+
const originalTask = await clickup.getTask(taskId);
|
|
720
|
+
const newTask = await clickup.duplicateTask(taskId, listId);
|
|
618
721
|
return {
|
|
619
722
|
content: [{
|
|
620
723
|
type: "text",
|
|
621
|
-
text: `Duplicated task ${
|
|
724
|
+
text: `Duplicated task "${originalTask.name}" from "${originalTask.list.name}" to "${newTask.list.name}"`
|
|
622
725
|
}]
|
|
623
726
|
};
|
|
624
727
|
}
|
|
625
728
|
case "update_task": {
|
|
626
729
|
const args = request.params.arguments;
|
|
627
|
-
if (!args.taskId) {
|
|
628
|
-
throw new Error("taskId is required");
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
}
|
|
730
|
+
if (!args.taskId && !args.taskName) {
|
|
731
|
+
throw new Error("Either taskId or taskName is required");
|
|
732
|
+
}
|
|
733
|
+
let taskId = args.taskId;
|
|
734
|
+
if (!taskId && args.taskName) {
|
|
735
|
+
const result = await clickup.findTaskByName(args.taskName, undefined, args.listName);
|
|
736
|
+
if (!result) {
|
|
737
|
+
throw new Error(`Task with name "${args.taskName}" not found${args.listName ? ` in list "${args.listName}"` : ''}`);
|
|
738
|
+
}
|
|
739
|
+
taskId = result.id;
|
|
740
|
+
}
|
|
741
|
+
const { taskId: _, taskName: __, listName: ___, ...updateData } = args;
|
|
742
|
+
const task = await clickup.updateTask(taskId, updateData);
|
|
638
743
|
return {
|
|
639
744
|
content: [{
|
|
640
745
|
type: "text",
|
|
@@ -642,100 +747,222 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
642
747
|
}]
|
|
643
748
|
};
|
|
644
749
|
}
|
|
750
|
+
case "get_tasks": {
|
|
751
|
+
const args = request.params.arguments;
|
|
752
|
+
if (!args.listId && !args.listName) {
|
|
753
|
+
throw new Error("Either listId or listName is required");
|
|
754
|
+
}
|
|
755
|
+
let listId = args.listId;
|
|
756
|
+
if (!listId && args.listName) {
|
|
757
|
+
const result = await clickup.findListIDByName(args.listName);
|
|
758
|
+
if (!result) {
|
|
759
|
+
throw new Error(`List with name "${args.listName}" not found`);
|
|
760
|
+
}
|
|
761
|
+
listId = result.id;
|
|
762
|
+
}
|
|
763
|
+
// Remove listId and listName from filters
|
|
764
|
+
const { listId: _, listName: __, ...filters } = args;
|
|
765
|
+
const { tasks, statuses } = await clickup.getTasks(listId, filters);
|
|
766
|
+
return {
|
|
767
|
+
content: [{
|
|
768
|
+
type: "text",
|
|
769
|
+
text: JSON.stringify({ tasks, available_statuses: statuses }, null, 2)
|
|
770
|
+
}]
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
case "get_task": {
|
|
774
|
+
const args = request.params.arguments;
|
|
775
|
+
if (!args.taskId && !args.taskName) {
|
|
776
|
+
throw new Error("Either taskId or taskName is required");
|
|
777
|
+
}
|
|
778
|
+
let taskId = args.taskId;
|
|
779
|
+
if (!taskId && args.taskName) {
|
|
780
|
+
const result = await clickup.findTaskByName(args.taskName, undefined, args.listName);
|
|
781
|
+
if (!result) {
|
|
782
|
+
throw new Error(`Task with name "${args.taskName}" not found${args.listName ? ` in list "${args.listName}"` : ''}`);
|
|
783
|
+
}
|
|
784
|
+
taskId = result.id;
|
|
785
|
+
}
|
|
786
|
+
const task = await clickup.getTask(taskId);
|
|
787
|
+
return {
|
|
788
|
+
content: [{
|
|
789
|
+
type: "text",
|
|
790
|
+
text: JSON.stringify(task, null, 2)
|
|
791
|
+
}]
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
case "delete_task": {
|
|
795
|
+
const args = request.params.arguments;
|
|
796
|
+
if (!args.taskId && !args.taskName) {
|
|
797
|
+
throw new Error("Either taskId or taskName is required");
|
|
798
|
+
}
|
|
799
|
+
let taskId = args.taskId;
|
|
800
|
+
if (!taskId && args.taskName) {
|
|
801
|
+
const result = await clickup.findTaskByName(args.taskName, undefined, args.listName);
|
|
802
|
+
if (!result) {
|
|
803
|
+
throw new Error(`Task with name "${args.taskName}" not found${args.listName ? ` in list "${args.listName}"` : ''}`);
|
|
804
|
+
}
|
|
805
|
+
taskId = result.id;
|
|
806
|
+
}
|
|
807
|
+
await clickup.deleteTask(taskId);
|
|
808
|
+
return {
|
|
809
|
+
content: [{
|
|
810
|
+
type: "text",
|
|
811
|
+
text: `Successfully deleted task ${args.taskName || taskId}`
|
|
812
|
+
}]
|
|
813
|
+
};
|
|
814
|
+
}
|
|
645
815
|
default:
|
|
646
|
-
throw new Error(
|
|
816
|
+
throw new Error(`Unknown tool: ${request.params.name}`);
|
|
647
817
|
}
|
|
648
818
|
}
|
|
649
819
|
catch (error) {
|
|
650
|
-
console.error('Error
|
|
651
|
-
|
|
820
|
+
console.error('Error in tool call:', error);
|
|
821
|
+
return {
|
|
822
|
+
content: [{
|
|
823
|
+
type: "text",
|
|
824
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
825
|
+
}]
|
|
826
|
+
};
|
|
652
827
|
}
|
|
653
828
|
});
|
|
654
829
|
/**
|
|
655
|
-
*
|
|
830
|
+
* Add handlers for listing and getting prompts.
|
|
831
|
+
* Prompts include summarizing tasks, analyzing priorities, and generating task descriptions.
|
|
656
832
|
*/
|
|
657
833
|
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
658
834
|
return {
|
|
659
835
|
prompts: [
|
|
660
836
|
{
|
|
661
837
|
name: "summarize_tasks",
|
|
662
|
-
description: "Summarize all
|
|
838
|
+
description: "Summarize all tasks in a list",
|
|
663
839
|
},
|
|
664
840
|
{
|
|
665
|
-
name: "
|
|
666
|
-
description: "Analyze task priorities"
|
|
841
|
+
name: "analyze_priorities",
|
|
842
|
+
description: "Analyze task priorities and suggest optimizations",
|
|
667
843
|
},
|
|
668
844
|
{
|
|
669
845
|
name: "generate_description",
|
|
670
|
-
description: "Generate a detailed
|
|
846
|
+
description: "Generate a detailed description for a task",
|
|
671
847
|
}
|
|
672
848
|
]
|
|
673
849
|
};
|
|
674
850
|
});
|
|
675
|
-
/**
|
|
676
|
-
* Handler for getting a specific prompt.
|
|
677
|
-
*/
|
|
678
851
|
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
679
852
|
try {
|
|
680
853
|
switch (request.params.name) {
|
|
681
854
|
case "summarize_tasks": {
|
|
682
|
-
const
|
|
855
|
+
const spaces = await clickup.getSpaces(config.clickupTeamId);
|
|
856
|
+
const tasks = [];
|
|
857
|
+
// Gather all tasks
|
|
858
|
+
for (const space of spaces) {
|
|
859
|
+
const lists = await clickup.getLists(space.id);
|
|
860
|
+
for (const list of lists) {
|
|
861
|
+
const { tasks: listTasks } = await clickup.getTasks(list.id);
|
|
862
|
+
tasks.push(...listTasks.map((task) => ({
|
|
863
|
+
type: "resource",
|
|
864
|
+
resource: {
|
|
865
|
+
uri: `clickup://task/${task.id}`,
|
|
866
|
+
mimeType: "application/json",
|
|
867
|
+
text: JSON.stringify(task, null, 2)
|
|
868
|
+
}
|
|
869
|
+
})));
|
|
870
|
+
}
|
|
871
|
+
}
|
|
683
872
|
return {
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
873
|
+
messages: [
|
|
874
|
+
{
|
|
875
|
+
role: "user",
|
|
876
|
+
content: {
|
|
877
|
+
type: "text",
|
|
878
|
+
text: "Please provide a summary of the following ClickUp tasks:"
|
|
879
|
+
}
|
|
880
|
+
},
|
|
881
|
+
...tasks.map(task => ({
|
|
882
|
+
role: "user",
|
|
883
|
+
content: task
|
|
884
|
+
})),
|
|
885
|
+
{
|
|
886
|
+
role: "user",
|
|
887
|
+
content: {
|
|
888
|
+
type: "text",
|
|
889
|
+
text: "Please provide:\n1. A high-level overview of all tasks\n2. Group them by status\n3. Highlight any urgent or high-priority items\n4. Suggest any task dependencies or relationships"
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
]
|
|
688
893
|
};
|
|
689
894
|
}
|
|
690
|
-
case "
|
|
691
|
-
const
|
|
895
|
+
case "analyze_priorities": {
|
|
896
|
+
const spaces = await clickup.getSpaces(config.clickupTeamId);
|
|
897
|
+
const tasks = [];
|
|
898
|
+
for (const space of spaces) {
|
|
899
|
+
const lists = await clickup.getLists(space.id);
|
|
900
|
+
for (const list of lists) {
|
|
901
|
+
const { tasks: listTasks } = await clickup.getTasks(list.id);
|
|
902
|
+
tasks.push(...listTasks.map((task) => ({
|
|
903
|
+
type: "resource",
|
|
904
|
+
resource: {
|
|
905
|
+
uri: `clickup://task/${task.id}`,
|
|
906
|
+
mimeType: "application/json",
|
|
907
|
+
text: JSON.stringify(task, null, 2)
|
|
908
|
+
}
|
|
909
|
+
})));
|
|
910
|
+
}
|
|
911
|
+
}
|
|
692
912
|
return {
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
913
|
+
messages: [
|
|
914
|
+
{
|
|
915
|
+
role: "user",
|
|
916
|
+
content: {
|
|
917
|
+
type: "text",
|
|
918
|
+
text: "Analyze the priorities of the following ClickUp tasks:"
|
|
919
|
+
}
|
|
920
|
+
},
|
|
921
|
+
...tasks.map(task => ({
|
|
922
|
+
role: "user",
|
|
923
|
+
content: task
|
|
924
|
+
})),
|
|
925
|
+
{
|
|
926
|
+
role: "user",
|
|
927
|
+
content: {
|
|
928
|
+
type: "text",
|
|
929
|
+
text: "Please provide:\n1. Analysis of current priority distribution\n2. Identify any misaligned priorities\n3. Suggest priority adjustments\n4. Recommend task sequencing based on priorities"
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
]
|
|
697
933
|
};
|
|
698
934
|
}
|
|
699
935
|
case "generate_description": {
|
|
700
|
-
if (!request.params.arguments?.taskId) {
|
|
701
|
-
throw new Error("taskId is required for generate_description prompt");
|
|
702
|
-
}
|
|
703
|
-
const output = await handleGenerateDescription(clickup, request.params.arguments.taskId);
|
|
704
936
|
return {
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
937
|
+
messages: [
|
|
938
|
+
{
|
|
939
|
+
role: "user",
|
|
940
|
+
content: {
|
|
941
|
+
type: "text",
|
|
942
|
+
text: "Please help me generate a detailed description for a ClickUp task. The description should include:\n1. Clear objective\n2. Success criteria\n3. Required resources\n4. Dependencies\n5. Potential risks\n\nPlease ask me about the task details."
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
]
|
|
709
946
|
};
|
|
710
947
|
}
|
|
711
948
|
default:
|
|
712
|
-
throw new Error("
|
|
949
|
+
throw new Error("Unknown prompt");
|
|
713
950
|
}
|
|
714
951
|
}
|
|
715
952
|
catch (error) {
|
|
716
|
-
console.error('Error
|
|
953
|
+
console.error('Error handling prompt:', error);
|
|
717
954
|
throw error;
|
|
718
955
|
}
|
|
719
956
|
});
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
957
|
+
/**
|
|
958
|
+
* Start the server using stdio transport.
|
|
959
|
+
* This allows the server to communicate via standard input/output streams.
|
|
960
|
+
*/
|
|
961
|
+
async function main() {
|
|
962
|
+
const transport = new StdioServerTransport();
|
|
963
|
+
await server.connect(transport);
|
|
964
|
+
}
|
|
965
|
+
main().catch((error) => {
|
|
966
|
+
console.error("Server error:", error);
|
|
727
967
|
process.exit(1);
|
|
728
968
|
});
|
|
729
|
-
// Handle process signals
|
|
730
|
-
process.on('SIGINT', () => {
|
|
731
|
-
console.log('Received SIGINT. Shutting down...');
|
|
732
|
-
transport.close();
|
|
733
|
-
});
|
|
734
|
-
process.on('SIGTERM', () => {
|
|
735
|
-
console.log('Received SIGTERM. Shutting down...');
|
|
736
|
-
transport.close();
|
|
737
|
-
});
|
|
738
|
-
// Prevent unhandled promise rejections from crashing the server
|
|
739
|
-
process.on('unhandledRejection', (error) => {
|
|
740
|
-
console.error('Unhandled promise rejection:', error);
|
|
741
|
-
});
|