1use crate::client::ClickUpClient;
2use crate::config::Config;
3use crate::output::compact_items;
4use serde_json::{json, Value};
5use tokio::io::{AsyncBufReadExt, BufReader};
6
7fn ok_response(id: &Value, result: Value) -> Value {
10 json!({"jsonrpc":"2.0","id":id,"result":result})
11}
12
13fn error_response(id: &Value, code: i64, message: &str) -> Value {
14 json!({"jsonrpc":"2.0","id":id,"error":{"code":code,"message":message}})
15}
16
17fn tool_result(text: String) -> Value {
18 json!({"content":[{"type":"text","text":text}]})
19}
20
21fn tool_error(msg: String) -> Value {
22 json!({"content":[{"type":"text","text":msg}],"isError":true})
23}
24
25fn tool_list() -> Value {
28 json!([
29 {
30 "name": "clickup_whoami",
31 "description": "Get the currently authenticated ClickUp user",
32 "inputSchema": {
33 "type": "object",
34 "properties": {},
35 "required": []
36 }
37 },
38 {
39 "name": "clickup_workspace_list",
40 "description": "List all ClickUp workspaces (teams) accessible to the current user",
41 "inputSchema": {
42 "type": "object",
43 "properties": {},
44 "required": []
45 }
46 },
47 {
48 "name": "clickup_space_list",
49 "description": "List spaces in a workspace",
50 "inputSchema": {
51 "type": "object",
52 "properties": {
53 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
54 "archived": {"type": "boolean", "description": "Include archived spaces"}
55 },
56 "required": []
57 }
58 },
59 {
60 "name": "clickup_folder_list",
61 "description": "List folders in a space",
62 "inputSchema": {
63 "type": "object",
64 "properties": {
65 "space_id": {"type": "string", "description": "Space ID"},
66 "archived": {"type": "boolean", "description": "Include archived folders"}
67 },
68 "required": ["space_id"]
69 }
70 },
71 {
72 "name": "clickup_list_list",
73 "description": "List ClickUp lists in a folder or space (folderless lists)",
74 "inputSchema": {
75 "type": "object",
76 "properties": {
77 "folder_id": {"type": "string", "description": "Folder ID (mutually exclusive with space_id)"},
78 "space_id": {"type": "string", "description": "Space ID for folderless lists (mutually exclusive with folder_id)"},
79 "archived": {"type": "boolean", "description": "Include archived lists"}
80 },
81 "required": []
82 }
83 },
84 {
85 "name": "clickup_task_list",
86 "description": "List tasks in a ClickUp list",
87 "inputSchema": {
88 "type": "object",
89 "properties": {
90 "list_id": {"type": "string", "description": "List ID"},
91 "statuses": {
92 "type": "array",
93 "items": {"type": "string"},
94 "description": "Filter by status names"
95 },
96 "assignees": {
97 "type": "array",
98 "items": {"type": "string"},
99 "description": "Filter by assignee user IDs"
100 },
101 "include_closed": {"type": "boolean", "description": "Include closed tasks"}
102 },
103 "required": ["list_id"]
104 }
105 },
106 {
107 "name": "clickup_task_get",
108 "description": "Get details of a specific ClickUp task",
109 "inputSchema": {
110 "type": "object",
111 "properties": {
112 "task_id": {"type": "string", "description": "Task ID"},
113 "include_subtasks": {"type": "boolean", "description": "Include subtasks in the response"}
114 },
115 "required": ["task_id"]
116 }
117 },
118 {
119 "name": "clickup_task_create",
120 "description": "Create a new task in a ClickUp list",
121 "inputSchema": {
122 "type": "object",
123 "properties": {
124 "list_id": {"type": "string", "description": "List ID to create the task in"},
125 "name": {"type": "string", "description": "Task name"},
126 "description": {"type": "string", "description": "Task description (markdown supported)"},
127 "status": {"type": "string", "description": "Task status"},
128 "priority": {"type": "integer", "description": "Priority (1=urgent, 2=high, 3=normal, 4=low)"},
129 "assignees": {
130 "type": "array",
131 "items": {"type": "integer"},
132 "description": "List of assignee user IDs"
133 },
134 "tags": {
135 "type": "array",
136 "items": {"type": "string"},
137 "description": "List of tag names"
138 },
139 "due_date": {"type": "integer", "description": "Due date as Unix timestamp (milliseconds)"}
140 },
141 "required": ["list_id", "name"]
142 }
143 },
144 {
145 "name": "clickup_task_update",
146 "description": "Update an existing ClickUp task",
147 "inputSchema": {
148 "type": "object",
149 "properties": {
150 "task_id": {"type": "string", "description": "Task ID"},
151 "name": {"type": "string", "description": "New task name"},
152 "status": {"type": "string", "description": "New status"},
153 "priority": {"type": "integer", "description": "New priority (1=urgent, 2=high, 3=normal, 4=low)"},
154 "description": {"type": "string", "description": "New description"},
155 "add_assignees": {
156 "type": "array",
157 "items": {"type": "integer"},
158 "description": "User IDs to add as assignees"
159 },
160 "rem_assignees": {
161 "type": "array",
162 "items": {"type": "integer"},
163 "description": "User IDs to remove from assignees"
164 }
165 },
166 "required": ["task_id"]
167 }
168 },
169 {
170 "name": "clickup_task_delete",
171 "description": "Delete a ClickUp task",
172 "inputSchema": {
173 "type": "object",
174 "properties": {
175 "task_id": {"type": "string", "description": "Task ID"}
176 },
177 "required": ["task_id"]
178 }
179 },
180 {
181 "name": "clickup_task_search",
182 "description": "Search tasks across a workspace with optional filters",
183 "inputSchema": {
184 "type": "object",
185 "properties": {
186 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
187 "space_ids": {
188 "type": "array",
189 "items": {"type": "string"},
190 "description": "Filter by space IDs"
191 },
192 "list_ids": {
193 "type": "array",
194 "items": {"type": "string"},
195 "description": "Filter by list IDs"
196 },
197 "statuses": {
198 "type": "array",
199 "items": {"type": "string"},
200 "description": "Filter by status names"
201 },
202 "assignees": {
203 "type": "array",
204 "items": {"type": "string"},
205 "description": "Filter by assignee user IDs"
206 }
207 },
208 "required": []
209 }
210 },
211 {
212 "name": "clickup_comment_list",
213 "description": "List comments on a ClickUp task",
214 "inputSchema": {
215 "type": "object",
216 "properties": {
217 "task_id": {"type": "string", "description": "Task ID"}
218 },
219 "required": ["task_id"]
220 }
221 },
222 {
223 "name": "clickup_comment_create",
224 "description": "Create a comment on a ClickUp task",
225 "inputSchema": {
226 "type": "object",
227 "properties": {
228 "task_id": {"type": "string", "description": "Task ID"},
229 "text": {"type": "string", "description": "Comment text"},
230 "assignee": {"type": "integer", "description": "Assign the comment to a user ID"},
231 "notify_all": {"type": "boolean", "description": "Notify all assignees"}
232 },
233 "required": ["task_id", "text"]
234 }
235 },
236 {
237 "name": "clickup_field_list",
238 "description": "List custom fields for a ClickUp list",
239 "inputSchema": {
240 "type": "object",
241 "properties": {
242 "list_id": {"type": "string", "description": "List ID"}
243 },
244 "required": ["list_id"]
245 }
246 },
247 {
248 "name": "clickup_field_set",
249 "description": "Set a custom field value on a ClickUp task",
250 "inputSchema": {
251 "type": "object",
252 "properties": {
253 "task_id": {"type": "string", "description": "Task ID"},
254 "field_id": {"type": "string", "description": "Custom field ID"},
255 "value": {"description": "Value to set (type depends on the custom field type)"}
256 },
257 "required": ["task_id", "field_id", "value"]
258 }
259 },
260 {
261 "name": "clickup_time_start",
262 "description": "Start a time tracking entry for a task",
263 "inputSchema": {
264 "type": "object",
265 "properties": {
266 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
267 "task_id": {"type": "string", "description": "Task ID to track time against"},
268 "description": {"type": "string", "description": "Description of the time entry"},
269 "billable": {"type": "boolean", "description": "Whether this entry is billable"}
270 },
271 "required": []
272 }
273 },
274 {
275 "name": "clickup_time_stop",
276 "description": "Stop the currently running time tracking entry",
277 "inputSchema": {
278 "type": "object",
279 "properties": {
280 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."}
281 },
282 "required": []
283 }
284 },
285 {
286 "name": "clickup_time_list",
287 "description": "List time tracking entries for a workspace",
288 "inputSchema": {
289 "type": "object",
290 "properties": {
291 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
292 "start_date": {"type": "integer", "description": "Start date as Unix timestamp (milliseconds)"},
293 "end_date": {"type": "integer", "description": "End date as Unix timestamp (milliseconds)"},
294 "task_id": {"type": "string", "description": "Filter by task ID"}
295 },
296 "required": []
297 }
298 },
299 {
300 "name": "clickup_checklist_create",
301 "description": "Create a checklist on a ClickUp task",
302 "inputSchema": {
303 "type": "object",
304 "properties": {
305 "task_id": {"type": "string", "description": "Task ID"},
306 "name": {"type": "string", "description": "Checklist name"}
307 },
308 "required": ["task_id", "name"]
309 }
310 },
311 {
312 "name": "clickup_checklist_delete",
313 "description": "Delete a checklist from a ClickUp task",
314 "inputSchema": {
315 "type": "object",
316 "properties": {
317 "checklist_id": {"type": "string", "description": "Checklist ID"}
318 },
319 "required": ["checklist_id"]
320 }
321 },
322 {
323 "name": "clickup_goal_list",
324 "description": "List goals in a workspace",
325 "inputSchema": {
326 "type": "object",
327 "properties": {
328 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."}
329 },
330 "required": []
331 }
332 },
333 {
334 "name": "clickup_goal_get",
335 "description": "Get details of a specific goal",
336 "inputSchema": {
337 "type": "object",
338 "properties": {
339 "goal_id": {"type": "string", "description": "Goal ID"}
340 },
341 "required": ["goal_id"]
342 }
343 },
344 {
345 "name": "clickup_goal_create",
346 "description": "Create a new goal in a workspace",
347 "inputSchema": {
348 "type": "object",
349 "properties": {
350 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
351 "name": {"type": "string", "description": "Goal name"},
352 "due_date": {"type": "integer", "description": "Due date as Unix timestamp (milliseconds)"},
353 "description": {"type": "string", "description": "Goal description"},
354 "owner_ids": {
355 "type": "array",
356 "items": {"type": "integer"},
357 "description": "List of owner user IDs"
358 }
359 },
360 "required": ["name"]
361 }
362 },
363 {
364 "name": "clickup_goal_update",
365 "description": "Update an existing goal",
366 "inputSchema": {
367 "type": "object",
368 "properties": {
369 "goal_id": {"type": "string", "description": "Goal ID"},
370 "name": {"type": "string", "description": "New goal name"},
371 "due_date": {"type": "integer", "description": "New due date as Unix timestamp (milliseconds)"},
372 "description": {"type": "string", "description": "New goal description"}
373 },
374 "required": ["goal_id"]
375 }
376 },
377 {
378 "name": "clickup_view_list",
379 "description": "List views for a space, folder, list, or workspace",
380 "inputSchema": {
381 "type": "object",
382 "properties": {
383 "space_id": {"type": "string", "description": "Space ID (mutually exclusive with other IDs)"},
384 "folder_id": {"type": "string", "description": "Folder ID (mutually exclusive with other IDs)"},
385 "list_id": {"type": "string", "description": "List ID (mutually exclusive with other IDs)"},
386 "team_id": {"type": "string", "description": "Workspace (team) ID for workspace-level views. Omit to use the default workspace from config."}
387 },
388 "required": []
389 }
390 },
391 {
392 "name": "clickup_view_tasks",
393 "description": "Get tasks in a specific view",
394 "inputSchema": {
395 "type": "object",
396 "properties": {
397 "view_id": {"type": "string", "description": "View ID"},
398 "page": {"type": "integer", "description": "Page number (0-indexed, default 0)"}
399 },
400 "required": ["view_id"]
401 }
402 },
403 {
404 "name": "clickup_doc_list",
405 "description": "List docs in a workspace",
406 "inputSchema": {
407 "type": "object",
408 "properties": {
409 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."}
410 },
411 "required": []
412 }
413 },
414 {
415 "name": "clickup_doc_get",
416 "description": "Get a specific doc from a workspace",
417 "inputSchema": {
418 "type": "object",
419 "properties": {
420 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
421 "doc_id": {"type": "string", "description": "Doc ID"}
422 },
423 "required": ["doc_id"]
424 }
425 },
426 {
427 "name": "clickup_doc_pages",
428 "description": "List pages in a doc",
429 "inputSchema": {
430 "type": "object",
431 "properties": {
432 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
433 "doc_id": {"type": "string", "description": "Doc ID"},
434 "content": {"type": "boolean", "description": "Include page content in the response"}
435 },
436 "required": ["doc_id"]
437 }
438 },
439 {
440 "name": "clickup_tag_list",
441 "description": "List tags for a space",
442 "inputSchema": {
443 "type": "object",
444 "properties": {
445 "space_id": {"type": "string", "description": "Space ID"}
446 },
447 "required": ["space_id"]
448 }
449 },
450 {
451 "name": "clickup_task_add_tag",
452 "description": "Add a tag to a ClickUp task",
453 "inputSchema": {
454 "type": "object",
455 "properties": {
456 "task_id": {"type": "string", "description": "Task ID"},
457 "tag_name": {"type": "string", "description": "Tag name to add"}
458 },
459 "required": ["task_id", "tag_name"]
460 }
461 },
462 {
463 "name": "clickup_task_remove_tag",
464 "description": "Remove a tag from a ClickUp task",
465 "inputSchema": {
466 "type": "object",
467 "properties": {
468 "task_id": {"type": "string", "description": "Task ID"},
469 "tag_name": {"type": "string", "description": "Tag name to remove"}
470 },
471 "required": ["task_id", "tag_name"]
472 }
473 },
474 {
475 "name": "clickup_webhook_list",
476 "description": "List webhooks for a workspace",
477 "inputSchema": {
478 "type": "object",
479 "properties": {
480 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."}
481 },
482 "required": []
483 }
484 },
485 {
486 "name": "clickup_member_list",
487 "description": "List members of a task or list",
488 "inputSchema": {
489 "type": "object",
490 "properties": {
491 "task_id": {"type": "string", "description": "Task ID (mutually exclusive with list_id)"},
492 "list_id": {"type": "string", "description": "List ID (mutually exclusive with task_id)"}
493 },
494 "required": []
495 }
496 },
497 {
498 "name": "clickup_template_list",
499 "description": "List task templates for a workspace",
500 "inputSchema": {
501 "type": "object",
502 "properties": {
503 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
504 "page": {"type": "integer", "description": "Page number (0-indexed, default 0)"}
505 },
506 "required": []
507 }
508 },
509 {
510 "name": "clickup_space_get",
511 "description": "Get details of a specific space",
512 "inputSchema": {
513 "type": "object",
514 "properties": {
515 "space_id": {"type": "string", "description": "Space ID"}
516 },
517 "required": ["space_id"]
518 }
519 },
520 {
521 "name": "clickup_space_create",
522 "description": "Create a new space in a workspace",
523 "inputSchema": {
524 "type": "object",
525 "properties": {
526 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
527 "name": {"type": "string", "description": "Space name"},
528 "private": {"type": "boolean", "description": "Whether the space is private"}
529 },
530 "required": ["name"]
531 }
532 },
533 {
534 "name": "clickup_space_update",
535 "description": "Update an existing space",
536 "inputSchema": {
537 "type": "object",
538 "properties": {
539 "space_id": {"type": "string", "description": "Space ID"},
540 "name": {"type": "string", "description": "New space name"},
541 "private": {"type": "boolean", "description": "Whether the space is private"},
542 "archived": {"type": "boolean", "description": "Archive/unarchive the space"}
543 },
544 "required": ["space_id"]
545 }
546 },
547 {
548 "name": "clickup_space_delete",
549 "description": "Delete a space",
550 "inputSchema": {
551 "type": "object",
552 "properties": {
553 "space_id": {"type": "string", "description": "Space ID"}
554 },
555 "required": ["space_id"]
556 }
557 },
558 {
559 "name": "clickup_folder_get",
560 "description": "Get details of a specific folder",
561 "inputSchema": {
562 "type": "object",
563 "properties": {
564 "folder_id": {"type": "string", "description": "Folder ID"}
565 },
566 "required": ["folder_id"]
567 }
568 },
569 {
570 "name": "clickup_folder_create",
571 "description": "Create a new folder in a space",
572 "inputSchema": {
573 "type": "object",
574 "properties": {
575 "space_id": {"type": "string", "description": "Space ID"},
576 "name": {"type": "string", "description": "Folder name"}
577 },
578 "required": ["space_id", "name"]
579 }
580 },
581 {
582 "name": "clickup_folder_update",
583 "description": "Update an existing folder",
584 "inputSchema": {
585 "type": "object",
586 "properties": {
587 "folder_id": {"type": "string", "description": "Folder ID"},
588 "name": {"type": "string", "description": "New folder name"}
589 },
590 "required": ["folder_id", "name"]
591 }
592 },
593 {
594 "name": "clickup_folder_delete",
595 "description": "Delete a folder",
596 "inputSchema": {
597 "type": "object",
598 "properties": {
599 "folder_id": {"type": "string", "description": "Folder ID"}
600 },
601 "required": ["folder_id"]
602 }
603 },
604 {
605 "name": "clickup_list_get",
606 "description": "Get details of a specific list",
607 "inputSchema": {
608 "type": "object",
609 "properties": {
610 "list_id": {"type": "string", "description": "List ID"}
611 },
612 "required": ["list_id"]
613 }
614 },
615 {
616 "name": "clickup_list_create",
617 "description": "Create a new list in a folder or space (folderless)",
618 "inputSchema": {
619 "type": "object",
620 "properties": {
621 "folder_id": {"type": "string", "description": "Folder ID (mutually exclusive with space_id)"},
622 "space_id": {"type": "string", "description": "Space ID for a folderless list (mutually exclusive with folder_id)"},
623 "name": {"type": "string", "description": "List name"},
624 "content": {"type": "string", "description": "List description"},
625 "due_date": {"type": "integer", "description": "Due date as Unix timestamp (milliseconds)"},
626 "status": {"type": "string", "description": "List status"}
627 },
628 "required": ["name"]
629 }
630 },
631 {
632 "name": "clickup_list_update",
633 "description": "Update an existing list",
634 "inputSchema": {
635 "type": "object",
636 "properties": {
637 "list_id": {"type": "string", "description": "List ID"},
638 "name": {"type": "string", "description": "New list name"},
639 "content": {"type": "string", "description": "New description"},
640 "due_date": {"type": "integer", "description": "Due date as Unix timestamp (milliseconds)"},
641 "status": {"type": "string", "description": "List status"}
642 },
643 "required": ["list_id"]
644 }
645 },
646 {
647 "name": "clickup_list_delete",
648 "description": "Delete a list",
649 "inputSchema": {
650 "type": "object",
651 "properties": {
652 "list_id": {"type": "string", "description": "List ID"}
653 },
654 "required": ["list_id"]
655 }
656 },
657 {
658 "name": "clickup_list_add_task",
659 "description": "Add a task to an additional list",
660 "inputSchema": {
661 "type": "object",
662 "properties": {
663 "list_id": {"type": "string", "description": "List ID"},
664 "task_id": {"type": "string", "description": "Task ID"}
665 },
666 "required": ["list_id", "task_id"]
667 }
668 },
669 {
670 "name": "clickup_list_remove_task",
671 "description": "Remove a task from an additional list",
672 "inputSchema": {
673 "type": "object",
674 "properties": {
675 "list_id": {"type": "string", "description": "List ID"},
676 "task_id": {"type": "string", "description": "Task ID"}
677 },
678 "required": ["list_id", "task_id"]
679 }
680 },
681 {
682 "name": "clickup_comment_update",
683 "description": "Update a comment",
684 "inputSchema": {
685 "type": "object",
686 "properties": {
687 "comment_id": {"type": "string", "description": "Comment ID"},
688 "text": {"type": "string", "description": "New comment text"},
689 "assignee": {"type": "integer", "description": "Reassign comment to user ID"},
690 "resolved": {"type": "boolean", "description": "Mark comment as resolved"}
691 },
692 "required": ["comment_id", "text"]
693 }
694 },
695 {
696 "name": "clickup_comment_delete",
697 "description": "Delete a comment",
698 "inputSchema": {
699 "type": "object",
700 "properties": {
701 "comment_id": {"type": "string", "description": "Comment ID"}
702 },
703 "required": ["comment_id"]
704 }
705 },
706 {
707 "name": "clickup_task_add_dep",
708 "description": "Add a dependency to a task",
709 "inputSchema": {
710 "type": "object",
711 "properties": {
712 "task_id": {"type": "string", "description": "Task ID"},
713 "depends_on": {"type": "string", "description": "Task ID that this task depends on"},
714 "dependency_of": {"type": "string", "description": "Task ID that depends on this task"}
715 },
716 "required": ["task_id"]
717 }
718 },
719 {
720 "name": "clickup_task_remove_dep",
721 "description": "Remove a dependency from a task",
722 "inputSchema": {
723 "type": "object",
724 "properties": {
725 "task_id": {"type": "string", "description": "Task ID"},
726 "depends_on": {"type": "string", "description": "Task ID to remove as a depends_on dependency"},
727 "dependency_of": {"type": "string", "description": "Task ID to remove as a dependency_of dependency"}
728 },
729 "required": ["task_id"]
730 }
731 },
732 {
733 "name": "clickup_task_link",
734 "description": "Link two tasks together",
735 "inputSchema": {
736 "type": "object",
737 "properties": {
738 "task_id": {"type": "string", "description": "Task ID"},
739 "links_to": {"type": "string", "description": "Task ID to link to"}
740 },
741 "required": ["task_id", "links_to"]
742 }
743 },
744 {
745 "name": "clickup_task_unlink",
746 "description": "Remove a link between two tasks",
747 "inputSchema": {
748 "type": "object",
749 "properties": {
750 "task_id": {"type": "string", "description": "Task ID"},
751 "links_to": {"type": "string", "description": "Task ID to unlink"}
752 },
753 "required": ["task_id", "links_to"]
754 }
755 },
756 {
757 "name": "clickup_goal_delete",
758 "description": "Delete a goal",
759 "inputSchema": {
760 "type": "object",
761 "properties": {
762 "goal_id": {"type": "string", "description": "Goal ID"}
763 },
764 "required": ["goal_id"]
765 }
766 },
767 {
768 "name": "clickup_goal_add_kr",
769 "description": "Add a key result to a goal",
770 "inputSchema": {
771 "type": "object",
772 "properties": {
773 "goal_id": {"type": "string", "description": "Goal ID"},
774 "name": {"type": "string", "description": "Key result name"},
775 "type": {"type": "string", "description": "Key result type (number, currency, boolean, percentage, automatic)"},
776 "steps_start": {"type": "number", "description": "Starting value"},
777 "steps_end": {"type": "number", "description": "Target value"},
778 "unit": {"type": "string", "description": "Unit label"},
779 "owner_ids": {"type": "array", "items": {"type": "integer"}, "description": "Owner user IDs"},
780 "task_ids": {"type": "array", "items": {"type": "string"}, "description": "Task IDs to link (for automatic type)"},
781 "list_ids": {"type": "array", "items": {"type": "string"}, "description": "List IDs to link (for automatic type)"}
782 },
783 "required": ["goal_id", "name", "type", "steps_start", "steps_end"]
784 }
785 },
786 {
787 "name": "clickup_goal_update_kr",
788 "description": "Update a key result",
789 "inputSchema": {
790 "type": "object",
791 "properties": {
792 "kr_id": {"type": "string", "description": "Key result ID"},
793 "steps_current": {"type": "number", "description": "Current progress value"},
794 "name": {"type": "string", "description": "New key result name"},
795 "unit": {"type": "string", "description": "Unit label"}
796 },
797 "required": ["kr_id"]
798 }
799 },
800 {
801 "name": "clickup_goal_delete_kr",
802 "description": "Delete a key result",
803 "inputSchema": {
804 "type": "object",
805 "properties": {
806 "kr_id": {"type": "string", "description": "Key result ID"}
807 },
808 "required": ["kr_id"]
809 }
810 },
811 {
812 "name": "clickup_time_get",
813 "description": "Get a specific time tracking entry",
814 "inputSchema": {
815 "type": "object",
816 "properties": {
817 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
818 "timer_id": {"type": "string", "description": "Time entry ID"}
819 },
820 "required": ["timer_id"]
821 }
822 },
823 {
824 "name": "clickup_time_create",
825 "description": "Create a time tracking entry",
826 "inputSchema": {
827 "type": "object",
828 "properties": {
829 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
830 "task_id": {"type": "string", "description": "Task ID to log time against"},
831 "start": {"type": "integer", "description": "Start time as Unix timestamp (milliseconds)"},
832 "duration": {"type": "integer", "description": "Duration in milliseconds"},
833 "description": {"type": "string", "description": "Time entry description"},
834 "billable": {"type": "boolean", "description": "Whether this entry is billable"}
835 },
836 "required": ["start", "duration"]
837 }
838 },
839 {
840 "name": "clickup_time_update",
841 "description": "Update a time tracking entry",
842 "inputSchema": {
843 "type": "object",
844 "properties": {
845 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
846 "timer_id": {"type": "string", "description": "Time entry ID"},
847 "start": {"type": "integer", "description": "New start time as Unix timestamp (milliseconds)"},
848 "duration": {"type": "integer", "description": "New duration in milliseconds"},
849 "description": {"type": "string", "description": "New description"},
850 "billable": {"type": "boolean", "description": "Whether this entry is billable"}
851 },
852 "required": ["timer_id"]
853 }
854 },
855 {
856 "name": "clickup_time_delete",
857 "description": "Delete a time tracking entry",
858 "inputSchema": {
859 "type": "object",
860 "properties": {
861 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
862 "timer_id": {"type": "string", "description": "Time entry ID"}
863 },
864 "required": ["timer_id"]
865 }
866 },
867 {
868 "name": "clickup_view_get",
869 "description": "Get details of a specific view",
870 "inputSchema": {
871 "type": "object",
872 "properties": {
873 "view_id": {"type": "string", "description": "View ID"}
874 },
875 "required": ["view_id"]
876 }
877 },
878 {
879 "name": "clickup_view_create",
880 "description": "Create a view for a space, folder, list, or workspace",
881 "inputSchema": {
882 "type": "object",
883 "properties": {
884 "scope": {"type": "string", "description": "Scope type: space, folder, list, or team"},
885 "scope_id": {"type": "string", "description": "ID of the scope object"},
886 "name": {"type": "string", "description": "View name"},
887 "type": {"type": "string", "description": "View type (list, board, calendar, table, timeline, etc.)"}
888 },
889 "required": ["scope", "scope_id", "name", "type"]
890 }
891 },
892 {
893 "name": "clickup_view_update",
894 "description": "Update an existing view",
895 "inputSchema": {
896 "type": "object",
897 "properties": {
898 "view_id": {"type": "string", "description": "View ID"},
899 "name": {"type": "string", "description": "New view name"},
900 "type": {"type": "string", "description": "New view type"}
901 },
902 "required": ["view_id", "name", "type"]
903 }
904 },
905 {
906 "name": "clickup_view_delete",
907 "description": "Delete a view",
908 "inputSchema": {
909 "type": "object",
910 "properties": {
911 "view_id": {"type": "string", "description": "View ID"}
912 },
913 "required": ["view_id"]
914 }
915 },
916 {
917 "name": "clickup_doc_create",
918 "description": "Create a new doc in a workspace",
919 "inputSchema": {
920 "type": "object",
921 "properties": {
922 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
923 "name": {"type": "string", "description": "Doc name"},
924 "parent": {"type": "object", "description": "Parent object with id and type fields"}
925 },
926 "required": ["name"]
927 }
928 },
929 {
930 "name": "clickup_doc_add_page",
931 "description": "Add a page to a doc",
932 "inputSchema": {
933 "type": "object",
934 "properties": {
935 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
936 "doc_id": {"type": "string", "description": "Doc ID"},
937 "name": {"type": "string", "description": "Page name"},
938 "content": {"type": "string", "description": "Page content (markdown)"},
939 "sub_title": {"type": "string", "description": "Page subtitle"},
940 "parent_page_id": {"type": "string", "description": "Parent page ID for nested pages"}
941 },
942 "required": ["doc_id", "name"]
943 }
944 },
945 {
946 "name": "clickup_doc_edit_page",
947 "description": "Edit an existing page in a doc",
948 "inputSchema": {
949 "type": "object",
950 "properties": {
951 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
952 "doc_id": {"type": "string", "description": "Doc ID"},
953 "page_id": {"type": "string", "description": "Page ID"},
954 "name": {"type": "string", "description": "New page name"},
955 "content": {"type": "string", "description": "New page content (markdown)"}
956 },
957 "required": ["doc_id", "page_id"]
958 }
959 },
960 {
961 "name": "clickup_chat_channel_create",
962 "description": "Create a chat channel in a workspace",
963 "inputSchema": {
964 "type": "object",
965 "properties": {
966 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
967 "name": {"type": "string", "description": "Channel name"},
968 "description": {"type": "string", "description": "Channel description"},
969 "visibility": {"type": "string", "description": "Channel visibility (public or private)"}
970 },
971 "required": ["name"]
972 }
973 },
974 {
975 "name": "clickup_chat_channel_get",
976 "description": "Get a chat channel",
977 "inputSchema": {
978 "type": "object",
979 "properties": {
980 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
981 "channel_id": {"type": "string", "description": "Channel ID"}
982 },
983 "required": ["channel_id"]
984 }
985 },
986 {
987 "name": "clickup_chat_channel_update",
988 "description": "Update a chat channel",
989 "inputSchema": {
990 "type": "object",
991 "properties": {
992 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
993 "channel_id": {"type": "string", "description": "Channel ID"},
994 "name": {"type": "string", "description": "New channel name"},
995 "description": {"type": "string", "description": "New channel description"}
996 },
997 "required": ["channel_id"]
998 }
999 },
1000 {
1001 "name": "clickup_chat_channel_delete",
1002 "description": "Delete a chat channel",
1003 "inputSchema": {
1004 "type": "object",
1005 "properties": {
1006 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1007 "channel_id": {"type": "string", "description": "Channel ID"}
1008 },
1009 "required": ["channel_id"]
1010 }
1011 },
1012 {
1013 "name": "clickup_chat_message_list",
1014 "description": "List messages in a chat channel",
1015 "inputSchema": {
1016 "type": "object",
1017 "properties": {
1018 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1019 "channel_id": {"type": "string", "description": "Channel ID"},
1020 "cursor": {"type": "string", "description": "Pagination cursor"}
1021 },
1022 "required": ["channel_id"]
1023 }
1024 },
1025 {
1026 "name": "clickup_chat_message_send",
1027 "description": "Send a message to a chat channel",
1028 "inputSchema": {
1029 "type": "object",
1030 "properties": {
1031 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1032 "channel_id": {"type": "string", "description": "Channel ID"},
1033 "content": {"type": "string", "description": "Message content"}
1034 },
1035 "required": ["channel_id", "content"]
1036 }
1037 },
1038 {
1039 "name": "clickup_chat_message_delete",
1040 "description": "Delete a chat message",
1041 "inputSchema": {
1042 "type": "object",
1043 "properties": {
1044 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1045 "message_id": {"type": "string", "description": "Message ID"}
1046 },
1047 "required": ["message_id"]
1048 }
1049 },
1050 {
1051 "name": "clickup_chat_dm",
1052 "description": "Send a direct message to a user",
1053 "inputSchema": {
1054 "type": "object",
1055 "properties": {
1056 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1057 "user_id": {"type": "integer", "description": "User ID to DM"},
1058 "content": {"type": "string", "description": "Message content"}
1059 },
1060 "required": ["user_id", "content"]
1061 }
1062 },
1063 {
1064 "name": "clickup_webhook_create",
1065 "description": "Create a webhook for a workspace",
1066 "inputSchema": {
1067 "type": "object",
1068 "properties": {
1069 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1070 "endpoint": {"type": "string", "description": "Webhook URL endpoint"},
1071 "events": {
1072 "type": "array",
1073 "items": {"type": "string"},
1074 "description": "List of events to subscribe to (use ['*'] for all events)"
1075 },
1076 "space_id": {"type": "string", "description": "Filter events to a specific space"},
1077 "folder_id": {"type": "string", "description": "Filter events to a specific folder"},
1078 "list_id": {"type": "string", "description": "Filter events to a specific list"},
1079 "task_id": {"type": "string", "description": "Filter events to a specific task"}
1080 },
1081 "required": ["endpoint", "events"]
1082 }
1083 },
1084 {
1085 "name": "clickup_webhook_update",
1086 "description": "Update a webhook",
1087 "inputSchema": {
1088 "type": "object",
1089 "properties": {
1090 "webhook_id": {"type": "string", "description": "Webhook ID"},
1091 "endpoint": {"type": "string", "description": "New webhook URL endpoint"},
1092 "events": {
1093 "type": "array",
1094 "items": {"type": "string"},
1095 "description": "New list of events to subscribe to"
1096 },
1097 "status": {"type": "string", "description": "Webhook status (active or suspended)"}
1098 },
1099 "required": ["webhook_id"]
1100 }
1101 },
1102 {
1103 "name": "clickup_webhook_delete",
1104 "description": "Delete a webhook",
1105 "inputSchema": {
1106 "type": "object",
1107 "properties": {
1108 "webhook_id": {"type": "string", "description": "Webhook ID"}
1109 },
1110 "required": ["webhook_id"]
1111 }
1112 },
1113 {
1114 "name": "clickup_checklist_add_item",
1115 "description": "Add an item to a checklist",
1116 "inputSchema": {
1117 "type": "object",
1118 "properties": {
1119 "checklist_id": {"type": "string", "description": "Checklist ID"},
1120 "name": {"type": "string", "description": "Item name"},
1121 "assignee": {"type": "integer", "description": "Assign item to user ID"}
1122 },
1123 "required": ["checklist_id", "name"]
1124 }
1125 },
1126 {
1127 "name": "clickup_checklist_update_item",
1128 "description": "Update a checklist item",
1129 "inputSchema": {
1130 "type": "object",
1131 "properties": {
1132 "checklist_id": {"type": "string", "description": "Checklist ID"},
1133 "item_id": {"type": "string", "description": "Checklist item ID"},
1134 "name": {"type": "string", "description": "New item name"},
1135 "resolved": {"type": "boolean", "description": "Mark item as resolved"},
1136 "assignee": {"type": "integer", "description": "Reassign item to user ID"}
1137 },
1138 "required": ["checklist_id", "item_id"]
1139 }
1140 },
1141 {
1142 "name": "clickup_checklist_delete_item",
1143 "description": "Delete a checklist item",
1144 "inputSchema": {
1145 "type": "object",
1146 "properties": {
1147 "checklist_id": {"type": "string", "description": "Checklist ID"},
1148 "item_id": {"type": "string", "description": "Checklist item ID"}
1149 },
1150 "required": ["checklist_id", "item_id"]
1151 }
1152 },
1153 {
1154 "name": "clickup_user_get",
1155 "description": "Get a user in a workspace",
1156 "inputSchema": {
1157 "type": "object",
1158 "properties": {
1159 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1160 "user_id": {"type": "integer", "description": "User ID"}
1161 },
1162 "required": ["user_id"]
1163 }
1164 },
1165 {
1166 "name": "clickup_workspace_seats",
1167 "description": "Get seat usage for a workspace",
1168 "inputSchema": {
1169 "type": "object",
1170 "properties": {
1171 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."}
1172 },
1173 "required": []
1174 }
1175 },
1176 {
1177 "name": "clickup_workspace_plan",
1178 "description": "Get the plan for a workspace",
1179 "inputSchema": {
1180 "type": "object",
1181 "properties": {
1182 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."}
1183 },
1184 "required": []
1185 }
1186 },
1187 {
1188 "name": "clickup_tag_create",
1189 "description": "Create a tag in a space",
1190 "inputSchema": {
1191 "type": "object",
1192 "properties": {
1193 "space_id": {"type": "string", "description": "Space ID"},
1194 "name": {"type": "string", "description": "Tag name"},
1195 "tag_fg": {"type": "string", "description": "Foreground color (hex)"},
1196 "tag_bg": {"type": "string", "description": "Background color (hex)"}
1197 },
1198 "required": ["space_id", "name"]
1199 }
1200 },
1201 {
1202 "name": "clickup_tag_update",
1203 "description": "Update a tag in a space",
1204 "inputSchema": {
1205 "type": "object",
1206 "properties": {
1207 "space_id": {"type": "string", "description": "Space ID"},
1208 "tag_name": {"type": "string", "description": "Current tag name"},
1209 "name": {"type": "string", "description": "New tag name"},
1210 "tag_fg": {"type": "string", "description": "New foreground color (hex)"},
1211 "tag_bg": {"type": "string", "description": "New background color (hex)"}
1212 },
1213 "required": ["space_id", "tag_name"]
1214 }
1215 },
1216 {
1217 "name": "clickup_tag_delete",
1218 "description": "Delete a tag from a space",
1219 "inputSchema": {
1220 "type": "object",
1221 "properties": {
1222 "space_id": {"type": "string", "description": "Space ID"},
1223 "tag_name": {"type": "string", "description": "Tag name"}
1224 },
1225 "required": ["space_id", "tag_name"]
1226 }
1227 },
1228 {
1229 "name": "clickup_field_unset",
1230 "description": "Remove a custom field value from a task",
1231 "inputSchema": {
1232 "type": "object",
1233 "properties": {
1234 "task_id": {"type": "string", "description": "Task ID"},
1235 "field_id": {"type": "string", "description": "Custom field ID"}
1236 },
1237 "required": ["task_id", "field_id"]
1238 }
1239 },
1240 {
1241 "name": "clickup_attachment_list",
1242 "description": "List attachments on a task",
1243 "inputSchema": {
1244 "type": "object",
1245 "properties": {
1246 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1247 "task_id": {"type": "string", "description": "Task ID"}
1248 },
1249 "required": ["task_id"]
1250 }
1251 },
1252 {
1253 "name": "clickup_shared_list",
1254 "description": "Get shared hierarchy (tasks, lists, folders) for a workspace",
1255 "inputSchema": {
1256 "type": "object",
1257 "properties": {
1258 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."}
1259 },
1260 "required": []
1261 }
1262 },
1263 {
1264 "name": "clickup_group_list",
1265 "description": "List user groups (teams) in a workspace",
1266 "inputSchema": {
1267 "type": "object",
1268 "properties": {
1269 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1270 "group_ids": {
1271 "type": "array",
1272 "items": {"type": "string"},
1273 "description": "Filter by specific group IDs"
1274 }
1275 },
1276 "required": []
1277 }
1278 },
1279 {
1280 "name": "clickup_group_create",
1281 "description": "Create a user group in a workspace",
1282 "inputSchema": {
1283 "type": "object",
1284 "properties": {
1285 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1286 "name": {"type": "string", "description": "Group name"},
1287 "member_ids": {
1288 "type": "array",
1289 "items": {"type": "integer"},
1290 "description": "User IDs to add as members"
1291 }
1292 },
1293 "required": ["name"]
1294 }
1295 },
1296 {
1297 "name": "clickup_group_update",
1298 "description": "Update a user group",
1299 "inputSchema": {
1300 "type": "object",
1301 "properties": {
1302 "group_id": {"type": "string", "description": "Group ID"},
1303 "name": {"type": "string", "description": "New group name"},
1304 "add_members": {
1305 "type": "array",
1306 "items": {"type": "integer"},
1307 "description": "User IDs to add"
1308 },
1309 "rem_members": {
1310 "type": "array",
1311 "items": {"type": "integer"},
1312 "description": "User IDs to remove"
1313 }
1314 },
1315 "required": ["group_id"]
1316 }
1317 },
1318 {
1319 "name": "clickup_group_delete",
1320 "description": "Delete a user group",
1321 "inputSchema": {
1322 "type": "object",
1323 "properties": {
1324 "group_id": {"type": "string", "description": "Group ID"}
1325 },
1326 "required": ["group_id"]
1327 }
1328 },
1329 {
1330 "name": "clickup_role_list",
1331 "description": "List custom roles in a workspace",
1332 "inputSchema": {
1333 "type": "object",
1334 "properties": {
1335 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."}
1336 },
1337 "required": []
1338 }
1339 },
1340 {
1341 "name": "clickup_guest_get",
1342 "description": "Get a guest in a workspace",
1343 "inputSchema": {
1344 "type": "object",
1345 "properties": {
1346 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1347 "guest_id": {"type": "integer", "description": "Guest user ID"}
1348 },
1349 "required": ["guest_id"]
1350 }
1351 },
1352 {
1353 "name": "clickup_task_time_in_status",
1354 "description": "Get time a task has spent in each status",
1355 "inputSchema": {
1356 "type": "object",
1357 "properties": {
1358 "task_id": {"type": "string", "description": "Task ID"}
1359 },
1360 "required": ["task_id"]
1361 }
1362 },
1363 {
1364 "name": "clickup_task_move",
1365 "description": "Move a task to a different list (change home list)",
1366 "inputSchema": {
1367 "type": "object",
1368 "properties": {
1369 "task_id": {"type": "string", "description": "Task ID"},
1370 "list_id": {"type": "string", "description": "Destination list ID"},
1371 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."}
1372 },
1373 "required": ["task_id", "list_id"]
1374 }
1375 },
1376 {
1377 "name": "clickup_task_set_estimate",
1378 "description": "Set a time estimate for a specific user on a task",
1379 "inputSchema": {
1380 "type": "object",
1381 "properties": {
1382 "task_id": {"type": "string", "description": "Task ID"},
1383 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1384 "user_id": {"type": "integer", "description": "User ID to set estimate for"},
1385 "time_estimate": {"type": "integer", "description": "Time estimate in milliseconds"}
1386 },
1387 "required": ["task_id", "user_id", "time_estimate"]
1388 }
1389 },
1390 {
1391 "name": "clickup_task_replace_estimates",
1392 "description": "Replace all time estimates for a task (PUT replaces all user estimates)",
1393 "inputSchema": {
1394 "type": "object",
1395 "properties": {
1396 "task_id": {"type": "string", "description": "Task ID"},
1397 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1398 "user_id": {"type": "integer", "description": "User ID"},
1399 "time_estimate": {"type": "integer", "description": "Time estimate in milliseconds"}
1400 },
1401 "required": ["task_id", "user_id", "time_estimate"]
1402 }
1403 },
1404 {
1405 "name": "clickup_auth_check",
1406 "description": "Check if the current API token is valid",
1407 "inputSchema": {
1408 "type": "object",
1409 "properties": {},
1410 "required": []
1411 }
1412 },
1413 {
1414 "name": "clickup_checklist_update",
1415 "description": "Update a checklist (rename or reorder)",
1416 "inputSchema": {
1417 "type": "object",
1418 "properties": {
1419 "checklist_id": {"type": "string", "description": "Checklist ID"},
1420 "name": {"type": "string", "description": "New checklist name"},
1421 "position": {"type": "integer", "description": "New position index"}
1422 },
1423 "required": ["checklist_id"]
1424 }
1425 },
1426 {
1427 "name": "clickup_comment_replies",
1428 "description": "Get replies to a comment",
1429 "inputSchema": {
1430 "type": "object",
1431 "properties": {
1432 "comment_id": {"type": "string", "description": "Comment ID"}
1433 },
1434 "required": ["comment_id"]
1435 }
1436 },
1437 {
1438 "name": "clickup_comment_reply",
1439 "description": "Post a reply to a comment",
1440 "inputSchema": {
1441 "type": "object",
1442 "properties": {
1443 "comment_id": {"type": "string", "description": "Comment ID"},
1444 "text": {"type": "string", "description": "Reply text"},
1445 "assignee": {"type": "integer", "description": "Assign the reply to a user ID"}
1446 },
1447 "required": ["comment_id", "text"]
1448 }
1449 },
1450 {
1451 "name": "clickup_chat_channel_list",
1452 "description": "List chat channels in a workspace",
1453 "inputSchema": {
1454 "type": "object",
1455 "properties": {
1456 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1457 "include_closed": {"type": "boolean", "description": "Include closed channels"}
1458 },
1459 "required": []
1460 }
1461 },
1462 {
1463 "name": "clickup_chat_channel_followers",
1464 "description": "Get followers of a chat channel",
1465 "inputSchema": {
1466 "type": "object",
1467 "properties": {
1468 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1469 "channel_id": {"type": "string", "description": "Channel ID"}
1470 },
1471 "required": ["channel_id"]
1472 }
1473 },
1474 {
1475 "name": "clickup_chat_channel_members",
1476 "description": "Get members of a chat channel",
1477 "inputSchema": {
1478 "type": "object",
1479 "properties": {
1480 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1481 "channel_id": {"type": "string", "description": "Channel ID"}
1482 },
1483 "required": ["channel_id"]
1484 }
1485 },
1486 {
1487 "name": "clickup_chat_message_update",
1488 "description": "Update (edit) a chat message",
1489 "inputSchema": {
1490 "type": "object",
1491 "properties": {
1492 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1493 "message_id": {"type": "string", "description": "Message ID"},
1494 "text": {"type": "string", "description": "New message content"}
1495 },
1496 "required": ["message_id", "text"]
1497 }
1498 },
1499 {
1500 "name": "clickup_chat_reaction_list",
1501 "description": "List reactions on a chat message",
1502 "inputSchema": {
1503 "type": "object",
1504 "properties": {
1505 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1506 "message_id": {"type": "string", "description": "Message ID"}
1507 },
1508 "required": ["message_id"]
1509 }
1510 },
1511 {
1512 "name": "clickup_chat_reaction_add",
1513 "description": "Add a reaction to a chat message",
1514 "inputSchema": {
1515 "type": "object",
1516 "properties": {
1517 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1518 "message_id": {"type": "string", "description": "Message ID"},
1519 "emoji": {"type": "string", "description": "Emoji reaction (e.g. '👍')"}
1520 },
1521 "required": ["message_id", "emoji"]
1522 }
1523 },
1524 {
1525 "name": "clickup_chat_reaction_remove",
1526 "description": "Remove a reaction from a chat message",
1527 "inputSchema": {
1528 "type": "object",
1529 "properties": {
1530 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1531 "message_id": {"type": "string", "description": "Message ID"},
1532 "emoji": {"type": "string", "description": "Emoji to remove"}
1533 },
1534 "required": ["message_id", "emoji"]
1535 }
1536 },
1537 {
1538 "name": "clickup_chat_reply_list",
1539 "description": "List replies to a chat message",
1540 "inputSchema": {
1541 "type": "object",
1542 "properties": {
1543 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1544 "message_id": {"type": "string", "description": "Message ID"}
1545 },
1546 "required": ["message_id"]
1547 }
1548 },
1549 {
1550 "name": "clickup_chat_reply_send",
1551 "description": "Send a reply to a chat message",
1552 "inputSchema": {
1553 "type": "object",
1554 "properties": {
1555 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1556 "message_id": {"type": "string", "description": "Message ID"},
1557 "text": {"type": "string", "description": "Reply content"}
1558 },
1559 "required": ["message_id", "text"]
1560 }
1561 },
1562 {
1563 "name": "clickup_chat_tagged_users",
1564 "description": "Get users tagged in a chat message",
1565 "inputSchema": {
1566 "type": "object",
1567 "properties": {
1568 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1569 "message_id": {"type": "string", "description": "Message ID"}
1570 },
1571 "required": ["message_id"]
1572 }
1573 },
1574 {
1575 "name": "clickup_time_current",
1576 "description": "Get the currently running time tracking entry",
1577 "inputSchema": {
1578 "type": "object",
1579 "properties": {
1580 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."}
1581 },
1582 "required": []
1583 }
1584 },
1585 {
1586 "name": "clickup_time_tags",
1587 "description": "List all time entry tags for a workspace",
1588 "inputSchema": {
1589 "type": "object",
1590 "properties": {
1591 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."}
1592 },
1593 "required": []
1594 }
1595 },
1596 {
1597 "name": "clickup_time_add_tags",
1598 "description": "Add tags to time entries",
1599 "inputSchema": {
1600 "type": "object",
1601 "properties": {
1602 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1603 "entry_ids": {
1604 "type": "array",
1605 "items": {"type": "string"},
1606 "description": "Time entry IDs to tag"
1607 },
1608 "tag_names": {
1609 "type": "array",
1610 "items": {"type": "string"},
1611 "description": "Tag names to add"
1612 }
1613 },
1614 "required": ["entry_ids", "tag_names"]
1615 }
1616 },
1617 {
1618 "name": "clickup_time_remove_tags",
1619 "description": "Remove tags from time entries",
1620 "inputSchema": {
1621 "type": "object",
1622 "properties": {
1623 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1624 "entry_ids": {
1625 "type": "array",
1626 "items": {"type": "string"},
1627 "description": "Time entry IDs to untag"
1628 },
1629 "tag_names": {
1630 "type": "array",
1631 "items": {"type": "string"},
1632 "description": "Tag names to remove"
1633 }
1634 },
1635 "required": ["entry_ids", "tag_names"]
1636 }
1637 },
1638 {
1639 "name": "clickup_time_rename_tag",
1640 "description": "Rename a time entry tag",
1641 "inputSchema": {
1642 "type": "object",
1643 "properties": {
1644 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1645 "name": {"type": "string", "description": "Current tag name"},
1646 "new_name": {"type": "string", "description": "New tag name"}
1647 },
1648 "required": ["name", "new_name"]
1649 }
1650 },
1651 {
1652 "name": "clickup_time_history",
1653 "description": "Get the history of changes for a time entry",
1654 "inputSchema": {
1655 "type": "object",
1656 "properties": {
1657 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1658 "timer_id": {"type": "string", "description": "Time entry ID"}
1659 },
1660 "required": ["timer_id"]
1661 }
1662 },
1663 {
1664 "name": "clickup_guest_invite",
1665 "description": "Invite a guest to a workspace",
1666 "inputSchema": {
1667 "type": "object",
1668 "properties": {
1669 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1670 "email": {"type": "string", "description": "Guest email address"},
1671 "can_edit_tags": {"type": "boolean", "description": "Allow guest to edit tags"},
1672 "can_see_time_spent": {"type": "boolean", "description": "Allow guest to see time spent"},
1673 "can_create_views": {"type": "boolean", "description": "Allow guest to create views"}
1674 },
1675 "required": ["email"]
1676 }
1677 },
1678 {
1679 "name": "clickup_guest_update",
1680 "description": "Update a guest's permissions in a workspace",
1681 "inputSchema": {
1682 "type": "object",
1683 "properties": {
1684 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1685 "guest_id": {"type": "integer", "description": "Guest user ID"},
1686 "can_edit_tags": {"type": "boolean", "description": "Allow guest to edit tags"},
1687 "can_see_time_spent": {"type": "boolean", "description": "Allow guest to see time spent"},
1688 "can_create_views": {"type": "boolean", "description": "Allow guest to create views"}
1689 },
1690 "required": ["guest_id"]
1691 }
1692 },
1693 {
1694 "name": "clickup_guest_remove",
1695 "description": "Remove a guest from a workspace",
1696 "inputSchema": {
1697 "type": "object",
1698 "properties": {
1699 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1700 "guest_id": {"type": "integer", "description": "Guest user ID"}
1701 },
1702 "required": ["guest_id"]
1703 }
1704 },
1705 {
1706 "name": "clickup_guest_share_task",
1707 "description": "Share a task with a guest",
1708 "inputSchema": {
1709 "type": "object",
1710 "properties": {
1711 "task_id": {"type": "string", "description": "Task ID"},
1712 "guest_id": {"type": "integer", "description": "Guest user ID"},
1713 "permission": {"type": "string", "description": "Permission level (read, comment, create, edit)"}
1714 },
1715 "required": ["task_id", "guest_id", "permission"]
1716 }
1717 },
1718 {
1719 "name": "clickup_guest_unshare_task",
1720 "description": "Revoke a guest's access to a task",
1721 "inputSchema": {
1722 "type": "object",
1723 "properties": {
1724 "task_id": {"type": "string", "description": "Task ID"},
1725 "guest_id": {"type": "integer", "description": "Guest user ID"}
1726 },
1727 "required": ["task_id", "guest_id"]
1728 }
1729 },
1730 {
1731 "name": "clickup_guest_share_list",
1732 "description": "Share a list with a guest",
1733 "inputSchema": {
1734 "type": "object",
1735 "properties": {
1736 "list_id": {"type": "string", "description": "List ID"},
1737 "guest_id": {"type": "integer", "description": "Guest user ID"},
1738 "permission": {"type": "string", "description": "Permission level (read, comment, create, edit)"}
1739 },
1740 "required": ["list_id", "guest_id", "permission"]
1741 }
1742 },
1743 {
1744 "name": "clickup_guest_unshare_list",
1745 "description": "Revoke a guest's access to a list",
1746 "inputSchema": {
1747 "type": "object",
1748 "properties": {
1749 "list_id": {"type": "string", "description": "List ID"},
1750 "guest_id": {"type": "integer", "description": "Guest user ID"}
1751 },
1752 "required": ["list_id", "guest_id"]
1753 }
1754 },
1755 {
1756 "name": "clickup_guest_share_folder",
1757 "description": "Share a folder with a guest",
1758 "inputSchema": {
1759 "type": "object",
1760 "properties": {
1761 "folder_id": {"type": "string", "description": "Folder ID"},
1762 "guest_id": {"type": "integer", "description": "Guest user ID"},
1763 "permission": {"type": "string", "description": "Permission level (read, comment, create, edit)"}
1764 },
1765 "required": ["folder_id", "guest_id", "permission"]
1766 }
1767 },
1768 {
1769 "name": "clickup_guest_unshare_folder",
1770 "description": "Revoke a guest's access to a folder",
1771 "inputSchema": {
1772 "type": "object",
1773 "properties": {
1774 "folder_id": {"type": "string", "description": "Folder ID"},
1775 "guest_id": {"type": "integer", "description": "Guest user ID"}
1776 },
1777 "required": ["folder_id", "guest_id"]
1778 }
1779 },
1780 {
1781 "name": "clickup_user_invite",
1782 "description": "Invite a user to a workspace",
1783 "inputSchema": {
1784 "type": "object",
1785 "properties": {
1786 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1787 "email": {"type": "string", "description": "User email address"},
1788 "admin": {"type": "boolean", "description": "Grant admin role"}
1789 },
1790 "required": ["email"]
1791 }
1792 },
1793 {
1794 "name": "clickup_user_update",
1795 "description": "Update a workspace member's role or username",
1796 "inputSchema": {
1797 "type": "object",
1798 "properties": {
1799 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1800 "user_id": {"type": "integer", "description": "User ID"},
1801 "username": {"type": "string", "description": "New username"},
1802 "admin": {"type": "boolean", "description": "Grant or revoke admin role"}
1803 },
1804 "required": ["user_id"]
1805 }
1806 },
1807 {
1808 "name": "clickup_user_remove",
1809 "description": "Remove a user from a workspace",
1810 "inputSchema": {
1811 "type": "object",
1812 "properties": {
1813 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1814 "user_id": {"type": "integer", "description": "User ID"}
1815 },
1816 "required": ["user_id"]
1817 }
1818 },
1819 {
1820 "name": "clickup_template_apply_task",
1821 "description": "Create a task from a task template",
1822 "inputSchema": {
1823 "type": "object",
1824 "properties": {
1825 "list_id": {"type": "string", "description": "List ID to create the task in"},
1826 "template_id": {"type": "string", "description": "Template ID"},
1827 "name": {"type": "string", "description": "Task name"}
1828 },
1829 "required": ["list_id", "template_id", "name"]
1830 }
1831 },
1832 {
1833 "name": "clickup_template_apply_list",
1834 "description": "Create a list from a list template in a folder or space",
1835 "inputSchema": {
1836 "type": "object",
1837 "properties": {
1838 "template_id": {"type": "string", "description": "Template ID"},
1839 "name": {"type": "string", "description": "New list name"},
1840 "folder_id": {"type": "string", "description": "Folder ID (mutually exclusive with space_id)"},
1841 "space_id": {"type": "string", "description": "Space ID (mutually exclusive with folder_id)"}
1842 },
1843 "required": ["template_id", "name"]
1844 }
1845 },
1846 {
1847 "name": "clickup_template_apply_folder",
1848 "description": "Create a folder from a folder template in a space",
1849 "inputSchema": {
1850 "type": "object",
1851 "properties": {
1852 "space_id": {"type": "string", "description": "Space ID"},
1853 "template_id": {"type": "string", "description": "Template ID"},
1854 "name": {"type": "string", "description": "New folder name"}
1855 },
1856 "required": ["space_id", "template_id", "name"]
1857 }
1858 },
1859 {
1860 "name": "clickup_attachment_upload",
1861 "description": "Upload a file as an attachment to a task",
1862 "inputSchema": {
1863 "type": "object",
1864 "properties": {
1865 "task_id": {"type": "string", "description": "Task ID"},
1866 "file_path": {"type": "string", "description": "Absolute path to the file to upload"}
1867 },
1868 "required": ["task_id", "file_path"]
1869 }
1870 },
1871 {
1872 "name": "clickup_task_type_list",
1873 "description": "List custom task types (custom items) for a workspace",
1874 "inputSchema": {
1875 "type": "object",
1876 "properties": {
1877 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."}
1878 },
1879 "required": []
1880 }
1881 },
1882 {
1883 "name": "clickup_doc_get_page",
1884 "description": "Get a specific page from a doc",
1885 "inputSchema": {
1886 "type": "object",
1887 "properties": {
1888 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1889 "doc_id": {"type": "string", "description": "Doc ID"},
1890 "page_id": {"type": "string", "description": "Page ID"}
1891 },
1892 "required": ["doc_id", "page_id"]
1893 }
1894 },
1895 {
1896 "name": "clickup_audit_log_query",
1897 "description": "Query the audit log for a workspace",
1898 "inputSchema": {
1899 "type": "object",
1900 "properties": {
1901 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1902 "type": {"type": "string", "description": "Audit log event type"},
1903 "user_id": {"type": "integer", "description": "Filter by user ID"},
1904 "start_date": {"type": "integer", "description": "Start date as Unix timestamp (milliseconds)"},
1905 "end_date": {"type": "integer", "description": "End date as Unix timestamp (milliseconds)"}
1906 },
1907 "required": ["type"]
1908 }
1909 },
1910 {
1911 "name": "clickup_acl_update",
1912 "description": "Update access control (privacy) for a workspace object",
1913 "inputSchema": {
1914 "type": "object",
1915 "properties": {
1916 "team_id": {"type": "string", "description": "Workspace (team) ID. Omit to use the default workspace from config."},
1917 "object_type": {"type": "string", "description": "Object type (e.g. space, folder, list)"},
1918 "object_id": {"type": "string", "description": "Object ID"},
1919 "private": {"type": "boolean", "description": "Set to true to make private, false to make public"}
1920 },
1921 "required": ["object_type", "object_id"]
1922 }
1923 }
1924 ])
1925}
1926
1927async fn call_tool(
1930 name: &str,
1931 args: &Value,
1932 client: &ClickUpClient,
1933 workspace_id: &Option<String>,
1934) -> Value {
1935 let result = dispatch_tool(name, args, client, workspace_id).await;
1936 match result {
1937 Ok(v) => tool_result(v.to_string()),
1938 Err(e) => tool_error(format!("Error: {}", e)),
1939 }
1940}
1941
1942async fn dispatch_tool(
1943 name: &str,
1944 args: &Value,
1945 client: &ClickUpClient,
1946 workspace_id: &Option<String>,
1947) -> Result<Value, String> {
1948 let empty = json!({});
1949 let args = if args.is_null() { &empty } else { args };
1950
1951 let resolve_workspace = |args: &Value| -> Result<String, String> {
1953 if let Some(id) = args.get("team_id").and_then(|v| v.as_str()) {
1954 return Ok(id.to_string());
1955 }
1956 workspace_id
1957 .clone()
1958 .ok_or_else(|| "No workspace_id found in config. Please run `clickup setup` or provide team_id in the tool arguments.".to_string())
1959 };
1960
1961 match name {
1962 "clickup_whoami" => {
1963 let resp = client.get("/v2/user").await.map_err(|e| e.to_string())?;
1964 let user = resp.get("user").cloned().unwrap_or(resp);
1965 Ok(compact_items(&[user], &["id", "username", "email"]))
1966 }
1967
1968 "clickup_workspace_list" => {
1969 let resp = client.get("/v2/team").await.map_err(|e| e.to_string())?;
1970 let teams = resp.get("teams").and_then(|t| t.as_array()).cloned().unwrap_or_default();
1971 let items: Vec<Value> = teams.iter().map(|ws| {
1972 json!({
1973 "id": ws.get("id"),
1974 "name": ws.get("name"),
1975 "members": ws.get("members").and_then(|m| m.as_array()).map(|a| a.len()).unwrap_or(0),
1976 })
1977 }).collect();
1978 Ok(compact_items(&items, &["id", "name", "members"]))
1979 }
1980
1981 "clickup_space_list" => {
1982 let team_id = resolve_workspace(args)?;
1983 let archived = args.get("archived").and_then(|v| v.as_bool()).unwrap_or(false);
1984 let path = format!("/v2/team/{}/space?archived={}", team_id, archived);
1985 let resp = client.get(&path).await.map_err(|e| e.to_string())?;
1986 let spaces = resp.get("spaces").and_then(|s| s.as_array()).cloned().unwrap_or_default();
1987 Ok(compact_items(&spaces, &["id", "name", "private", "archived"]))
1988 }
1989
1990 "clickup_folder_list" => {
1991 let space_id = args
1992 .get("space_id")
1993 .and_then(|v| v.as_str())
1994 .ok_or("Missing required parameter: space_id")?;
1995 let archived = args.get("archived").and_then(|v| v.as_bool()).unwrap_or(false);
1996 let path = format!("/v2/space/{}/folder?archived={}", space_id, archived);
1997 let resp = client.get(&path).await.map_err(|e| e.to_string())?;
1998 let folders = resp.get("folders").and_then(|f| f.as_array()).cloned().unwrap_or_default();
1999 let items: Vec<Value> = folders.iter().map(|f| {
2000 let list_count = f.get("lists").and_then(|l| l.as_array()).map(|a| a.len()).unwrap_or(0);
2001 json!({
2002 "id": f.get("id"),
2003 "name": f.get("name"),
2004 "task_count": f.get("task_count"),
2005 "list_count": list_count,
2006 })
2007 }).collect();
2008 Ok(compact_items(&items, &["id", "name", "task_count", "list_count"]))
2009 }
2010
2011 "clickup_list_list" => {
2012 let archived = args.get("archived").and_then(|v| v.as_bool()).unwrap_or(false);
2013 let path = if let Some(folder_id) = args.get("folder_id").and_then(|v| v.as_str()) {
2014 format!("/v2/folder/{}/list?archived={}", folder_id, archived)
2015 } else if let Some(space_id) = args.get("space_id").and_then(|v| v.as_str()) {
2016 format!("/v2/space/{}/list?archived={}", space_id, archived)
2017 } else {
2018 return Err("Provide either folder_id or space_id".to_string());
2019 };
2020 let resp = client.get(&path).await.map_err(|e| e.to_string())?;
2021 let lists = resp.get("lists").and_then(|l| l.as_array()).cloned().unwrap_or_default();
2022 Ok(compact_items(&lists, &["id", "name", "task_count", "status", "due_date"]))
2023 }
2024
2025 "clickup_task_list" => {
2026 let list_id = args
2027 .get("list_id")
2028 .and_then(|v| v.as_str())
2029 .ok_or("Missing required parameter: list_id")?;
2030 let mut qs = String::new();
2031 if let Some(include_closed) = args.get("include_closed").and_then(|v| v.as_bool()) {
2032 qs.push_str(&format!("&include_closed={}", include_closed));
2033 }
2034 if let Some(statuses) = args.get("statuses").and_then(|v| v.as_array()) {
2035 for s in statuses {
2036 if let Some(s) = s.as_str() {
2037 qs.push_str(&format!("&statuses[]={}", s));
2038 }
2039 }
2040 }
2041 if let Some(assignees) = args.get("assignees").and_then(|v| v.as_array()) {
2042 for a in assignees {
2043 if let Some(a) = a.as_str() {
2044 qs.push_str(&format!("&assignees[]={}", a));
2045 }
2046 }
2047 }
2048 let path = format!("/v2/list/{}/task?{}", list_id, qs.trim_start_matches('&'));
2049 let resp = client.get(&path).await.map_err(|e| e.to_string())?;
2050 let tasks = resp.get("tasks").and_then(|t| t.as_array()).cloned().unwrap_or_default();
2051 Ok(compact_items(&tasks, &["id", "name", "status", "priority", "assignees", "due_date"]))
2052 }
2053
2054 "clickup_task_get" => {
2055 let task_id = args
2056 .get("task_id")
2057 .and_then(|v| v.as_str())
2058 .ok_or("Missing required parameter: task_id")?;
2059 let include_subtasks = args
2060 .get("include_subtasks")
2061 .and_then(|v| v.as_bool())
2062 .unwrap_or(false);
2063 let path = format!(
2064 "/v2/task/{}?include_subtasks={}",
2065 task_id, include_subtasks
2066 );
2067 let resp = client.get(&path).await.map_err(|e| e.to_string())?;
2068 Ok(compact_items(&[resp], &["id", "name", "status", "priority", "assignees", "due_date", "description"]))
2069 }
2070
2071 "clickup_task_create" => {
2072 let list_id = args
2073 .get("list_id")
2074 .and_then(|v| v.as_str())
2075 .ok_or("Missing required parameter: list_id")?;
2076 let name = args
2077 .get("name")
2078 .and_then(|v| v.as_str())
2079 .ok_or("Missing required parameter: name")?;
2080 let mut body = json!({"name": name});
2081 if let Some(desc) = args.get("description").and_then(|v| v.as_str()) {
2082 body["description"] = json!(desc);
2083 }
2084 if let Some(status) = args.get("status").and_then(|v| v.as_str()) {
2085 body["status"] = json!(status);
2086 }
2087 if let Some(priority) = args.get("priority").and_then(|v| v.as_i64()) {
2088 body["priority"] = json!(priority);
2089 }
2090 if let Some(assignees) = args.get("assignees") {
2091 body["assignees"] = assignees.clone();
2092 }
2093 if let Some(tags) = args.get("tags") {
2094 body["tags"] = tags.clone();
2095 }
2096 if let Some(due_date) = args.get("due_date").and_then(|v| v.as_i64()) {
2097 body["due_date"] = json!(due_date);
2098 }
2099 let path = format!("/v2/list/{}/task", list_id);
2100 let resp = client.post(&path, &body).await.map_err(|e| e.to_string())?;
2101 Ok(compact_items(&[resp], &["id", "name", "status", "priority", "assignees", "due_date"]))
2102 }
2103
2104 "clickup_task_update" => {
2105 let task_id = args
2106 .get("task_id")
2107 .and_then(|v| v.as_str())
2108 .ok_or("Missing required parameter: task_id")?;
2109 let mut body = json!({});
2110 if let Some(name) = args.get("name").and_then(|v| v.as_str()) {
2111 body["name"] = json!(name);
2112 }
2113 if let Some(status) = args.get("status").and_then(|v| v.as_str()) {
2114 body["status"] = json!(status);
2115 }
2116 if let Some(priority) = args.get("priority").and_then(|v| v.as_i64()) {
2117 body["priority"] = json!(priority);
2118 }
2119 if let Some(desc) = args.get("description").and_then(|v| v.as_str()) {
2120 body["description"] = json!(desc);
2121 }
2122 if let Some(add) = args.get("add_assignees") {
2123 body["assignees"] = json!({"add": add, "rem": args.get("rem_assignees").cloned().unwrap_or(json!([]))});
2124 } else if let Some(rem) = args.get("rem_assignees") {
2125 body["assignees"] = json!({"add": [], "rem": rem});
2126 }
2127 let path = format!("/v2/task/{}", task_id);
2128 let resp = client.put(&path, &body).await.map_err(|e| e.to_string())?;
2129 Ok(compact_items(&[resp], &["id", "name", "status", "priority", "assignees", "due_date"]))
2130 }
2131
2132 "clickup_task_delete" => {
2133 let task_id = args
2134 .get("task_id")
2135 .and_then(|v| v.as_str())
2136 .ok_or("Missing required parameter: task_id")?;
2137 let path = format!("/v2/task/{}", task_id);
2138 client.delete(&path).await.map_err(|e| e.to_string())?;
2139 Ok(json!({"message": format!("Task {} deleted", task_id)}))
2140 }
2141
2142 "clickup_task_search" => {
2143 let team_id = resolve_workspace(args)?;
2144 let mut qs = String::new();
2145 if let Some(space_ids) = args.get("space_ids").and_then(|v| v.as_array()) {
2146 for id in space_ids {
2147 if let Some(id) = id.as_str() {
2148 qs.push_str(&format!("&space_ids[]={}", id));
2149 }
2150 }
2151 }
2152 if let Some(list_ids) = args.get("list_ids").and_then(|v| v.as_array()) {
2153 for id in list_ids {
2154 if let Some(id) = id.as_str() {
2155 qs.push_str(&format!("&list_ids[]={}", id));
2156 }
2157 }
2158 }
2159 if let Some(statuses) = args.get("statuses").and_then(|v| v.as_array()) {
2160 for s in statuses {
2161 if let Some(s) = s.as_str() {
2162 qs.push_str(&format!("&statuses[]={}", s));
2163 }
2164 }
2165 }
2166 if let Some(assignees) = args.get("assignees").and_then(|v| v.as_array()) {
2167 for a in assignees {
2168 if let Some(a) = a.as_str() {
2169 qs.push_str(&format!("&assignees[]={}", a));
2170 }
2171 }
2172 }
2173 let path = format!(
2174 "/v2/team/{}/task?{}",
2175 team_id,
2176 qs.trim_start_matches('&')
2177 );
2178 let resp = client.get(&path).await.map_err(|e| e.to_string())?;
2179 let tasks = resp.get("tasks").and_then(|t| t.as_array()).cloned().unwrap_or_default();
2180 Ok(compact_items(&tasks, &["id", "name", "status", "priority", "assignees", "due_date"]))
2181 }
2182
2183 "clickup_comment_list" => {
2184 let task_id = args
2185 .get("task_id")
2186 .and_then(|v| v.as_str())
2187 .ok_or("Missing required parameter: task_id")?;
2188 let path = format!("/v2/task/{}/comment", task_id);
2189 let resp = client.get(&path).await.map_err(|e| e.to_string())?;
2190 let comments = resp.get("comments").and_then(|c| c.as_array()).cloned().unwrap_or_default();
2191 Ok(compact_items(&comments, &["id", "user", "date", "comment_text"]))
2192 }
2193
2194 "clickup_comment_create" => {
2195 let task_id = args
2196 .get("task_id")
2197 .and_then(|v| v.as_str())
2198 .ok_or("Missing required parameter: task_id")?;
2199 let text = args
2200 .get("text")
2201 .and_then(|v| v.as_str())
2202 .ok_or("Missing required parameter: text")?;
2203 let mut body = json!({"comment_text": text});
2204 if let Some(assignee) = args.get("assignee").and_then(|v| v.as_i64()) {
2205 body["assignee"] = json!(assignee);
2206 }
2207 if let Some(notify_all) = args.get("notify_all").and_then(|v| v.as_bool()) {
2208 body["notify_all"] = json!(notify_all);
2209 }
2210 let path = format!("/v2/task/{}/comment", task_id);
2211 let resp = client.post(&path, &body).await.map_err(|e| e.to_string())?;
2212 Ok(json!({"message": "Comment created", "id": resp.get("id")}))
2213 }
2214
2215 "clickup_field_list" => {
2216 let list_id = args
2217 .get("list_id")
2218 .and_then(|v| v.as_str())
2219 .ok_or("Missing required parameter: list_id")?;
2220 let path = format!("/v2/list/{}/field", list_id);
2221 let resp = client.get(&path).await.map_err(|e| e.to_string())?;
2222 let fields = resp.get("fields").and_then(|f| f.as_array()).cloned().unwrap_or_default();
2223 Ok(compact_items(&fields, &["id", "name", "type", "required"]))
2224 }
2225
2226 "clickup_field_set" => {
2227 let task_id = args
2228 .get("task_id")
2229 .and_then(|v| v.as_str())
2230 .ok_or("Missing required parameter: task_id")?;
2231 let field_id = args
2232 .get("field_id")
2233 .and_then(|v| v.as_str())
2234 .ok_or("Missing required parameter: field_id")?;
2235 let value = args.get("value").ok_or("Missing required parameter: value")?;
2236 let body = json!({"value": value});
2237 let path = format!("/v2/task/{}/field/{}", task_id, field_id);
2238 client.post(&path, &body).await.map_err(|e| e.to_string())?;
2239 Ok(json!({"message": format!("Field {} set on task {}", field_id, task_id)}))
2240 }
2241
2242 "clickup_time_start" => {
2243 let team_id = resolve_workspace(args)?;
2244 let mut body = json!({});
2245 if let Some(task_id) = args.get("task_id").and_then(|v| v.as_str()) {
2246 body["tid"] = json!(task_id);
2247 }
2248 if let Some(desc) = args.get("description").and_then(|v| v.as_str()) {
2249 body["description"] = json!(desc);
2250 }
2251 if let Some(billable) = args.get("billable").and_then(|v| v.as_bool()) {
2252 body["billable"] = json!(billable);
2253 }
2254 let path = format!("/v2/team/{}/time_entries/start", team_id);
2255 let resp = client.post(&path, &body).await.map_err(|e| e.to_string())?;
2256 let data = resp.get("data").cloned().unwrap_or(resp);
2257 Ok(compact_items(&[data], &["id", "task", "duration", "start", "billable"]))
2258 }
2259
2260 "clickup_time_stop" => {
2261 let team_id = resolve_workspace(args)?;
2262 let path = format!("/v2/team/{}/time_entries/stop", team_id);
2263 let resp = client.post(&path, &json!({})).await.map_err(|e| e.to_string())?;
2264 let data = resp.get("data").cloned().unwrap_or(resp);
2265 Ok(compact_items(&[data], &["id", "task", "duration", "start", "end", "billable"]))
2266 }
2267
2268 "clickup_time_list" => {
2269 let team_id = resolve_workspace(args)?;
2270 let mut qs = String::new();
2271 if let Some(start_date) = args.get("start_date").and_then(|v| v.as_i64()) {
2272 qs.push_str(&format!("&start_date={}", start_date));
2273 }
2274 if let Some(end_date) = args.get("end_date").and_then(|v| v.as_i64()) {
2275 qs.push_str(&format!("&end_date={}", end_date));
2276 }
2277 if let Some(task_id) = args.get("task_id").and_then(|v| v.as_str()) {
2278 qs.push_str(&format!("&task_id={}", task_id));
2279 }
2280 let path = format!(
2281 "/v2/team/{}/time_entries?{}",
2282 team_id,
2283 qs.trim_start_matches('&')
2284 );
2285 let resp = client.get(&path).await.map_err(|e| e.to_string())?;
2286 let entries = resp.get("data").and_then(|d| d.as_array()).cloned().unwrap_or_default();
2287 Ok(compact_items(&entries, &["id", "task", "duration", "start", "billable"]))
2288 }
2289
2290 "clickup_checklist_create" => {
2291 let task_id = args
2292 .get("task_id")
2293 .and_then(|v| v.as_str())
2294 .ok_or("Missing required parameter: task_id")?;
2295 let name = args
2296 .get("name")
2297 .and_then(|v| v.as_str())
2298 .ok_or("Missing required parameter: name")?;
2299 let path = format!("/v2/task/{}/checklist", task_id);
2300 let body = json!({"name": name});
2301 let resp = client.post(&path, &body).await.map_err(|e| e.to_string())?;
2302 let checklist = resp.get("checklist").cloned().unwrap_or(resp);
2303 Ok(compact_items(&[checklist], &["id", "name"]))
2304 }
2305
2306 "clickup_checklist_delete" => {
2307 let checklist_id = args
2308 .get("checklist_id")
2309 .and_then(|v| v.as_str())
2310 .ok_or("Missing required parameter: checklist_id")?;
2311 let path = format!("/v2/checklist/{}", checklist_id);
2312 client.delete(&path).await.map_err(|e| e.to_string())?;
2313 Ok(json!({"message": format!("Checklist {} deleted", checklist_id)}))
2314 }
2315
2316 "clickup_goal_list" => {
2317 let team_id = resolve_workspace(args)?;
2318 let path = format!("/v2/team/{}/goal", team_id);
2319 let resp = client.get(&path).await.map_err(|e| e.to_string())?;
2320 let goals = resp.get("goals").and_then(|g| g.as_array()).cloned().unwrap_or_default();
2321 Ok(compact_items(&goals, &["id", "name", "percent_completed", "due_date"]))
2322 }
2323
2324 "clickup_goal_get" => {
2325 let goal_id = args
2326 .get("goal_id")
2327 .and_then(|v| v.as_str())
2328 .ok_or("Missing required parameter: goal_id")?;
2329 let path = format!("/v2/goal/{}", goal_id);
2330 let resp = client.get(&path).await.map_err(|e| e.to_string())?;
2331 let goal = resp.get("goal").cloned().unwrap_or(resp);
2332 Ok(compact_items(&[goal], &["id", "name", "percent_completed", "due_date", "description"]))
2333 }
2334
2335 "clickup_goal_create" => {
2336 let team_id = resolve_workspace(args)?;
2337 let name = args
2338 .get("name")
2339 .and_then(|v| v.as_str())
2340 .ok_or("Missing required parameter: name")?;
2341 let mut body = json!({"name": name});
2342 if let Some(due_date) = args.get("due_date").and_then(|v| v.as_i64()) {
2343 body["due_date"] = json!(due_date);
2344 }
2345 if let Some(desc) = args.get("description").and_then(|v| v.as_str()) {
2346 body["description"] = json!(desc);
2347 }
2348 if let Some(owner_ids) = args.get("owner_ids") {
2349 body["owners"] = owner_ids.clone();
2350 }
2351 let path = format!("/v2/team/{}/goal", team_id);
2352 let resp = client.post(&path, &body).await.map_err(|e| e.to_string())?;
2353 let goal = resp.get("goal").cloned().unwrap_or(resp);
2354 Ok(compact_items(&[goal], &["id", "name"]))
2355 }
2356
2357 "clickup_goal_update" => {
2358 let goal_id = args
2359 .get("goal_id")
2360 .and_then(|v| v.as_str())
2361 .ok_or("Missing required parameter: goal_id")?;
2362 let mut body = json!({});
2363 if let Some(name) = args.get("name").and_then(|v| v.as_str()) {
2364 body["name"] = json!(name);
2365 }
2366 if let Some(due_date) = args.get("due_date").and_then(|v| v.as_i64()) {
2367 body["due_date"] = json!(due_date);
2368 }
2369 if let Some(desc) = args.get("description").and_then(|v| v.as_str()) {
2370 body["description"] = json!(desc);
2371 }
2372 let path = format!("/v2/goal/{}", goal_id);
2373 let resp = client.put(&path, &body).await.map_err(|e| e.to_string())?;
2374 let goal = resp.get("goal").cloned().unwrap_or(resp);
2375 Ok(compact_items(&[goal], &["id", "name"]))
2376 }
2377
2378 "clickup_view_list" => {
2379 let path = if let Some(space_id) = args.get("space_id").and_then(|v| v.as_str()) {
2380 format!("/v2/space/{}/view", space_id)
2381 } else if let Some(folder_id) = args.get("folder_id").and_then(|v| v.as_str()) {
2382 format!("/v2/folder/{}/view", folder_id)
2383 } else if let Some(list_id) = args.get("list_id").and_then(|v| v.as_str()) {
2384 format!("/v2/list/{}/view", list_id)
2385 } else {
2386 let team_id = resolve_workspace(args)?;
2387 format!("/v2/team/{}/view", team_id)
2388 };
2389 let resp = client.get(&path).await.map_err(|e| e.to_string())?;
2390 let views = resp.get("views").and_then(|v| v.as_array()).cloned().unwrap_or_default();
2391 Ok(compact_items(&views, &["id", "name", "type"]))
2392 }
2393
2394 "clickup_view_tasks" => {
2395 let view_id = args
2396 .get("view_id")
2397 .and_then(|v| v.as_str())
2398 .ok_or("Missing required parameter: view_id")?;
2399 let page = args.get("page").and_then(|v| v.as_i64()).unwrap_or(0);
2400 let path = format!("/v2/view/{}/task?page={}", view_id, page);
2401 let resp = client.get(&path).await.map_err(|e| e.to_string())?;
2402 let tasks = resp.get("tasks").and_then(|t| t.as_array()).cloned().unwrap_or_default();
2403 Ok(compact_items(&tasks, &["id", "name", "status", "priority", "assignees", "due_date"]))
2404 }
2405
2406 "clickup_doc_list" => {
2407 let team_id = resolve_workspace(args)?;
2408 let path = format!("/v3/workspaces/{}/docs", team_id);
2409 let resp = client.get(&path).await.map_err(|e| e.to_string())?;
2410 let docs = resp.get("docs").and_then(|d| d.as_array()).cloned().unwrap_or_default();
2411 Ok(compact_items(&docs, &["id", "name", "date_created"]))
2412 }
2413
2414 "clickup_doc_get" => {
2415 let team_id = resolve_workspace(args)?;
2416 let doc_id = args
2417 .get("doc_id")
2418 .and_then(|v| v.as_str())
2419 .ok_or("Missing required parameter: doc_id")?;
2420 let path = format!("/v3/workspaces/{}/docs/{}", team_id, doc_id);
2421 let resp = client.get(&path).await.map_err(|e| e.to_string())?;
2422 Ok(compact_items(&[resp], &["id", "name", "date_created"]))
2423 }
2424
2425 "clickup_doc_pages" => {
2426 let team_id = resolve_workspace(args)?;
2427 let doc_id = args
2428 .get("doc_id")
2429 .and_then(|v| v.as_str())
2430 .ok_or("Missing required parameter: doc_id")?;
2431 let content = args.get("content").and_then(|v| v.as_bool()).unwrap_or(false);
2432 let path = format!("/v3/workspaces/{}/docs/{}/pages?content_format=text/md&max_page_depth=-1&include_content={}", team_id, doc_id, content);
2433 let resp = client.get(&path).await.map_err(|e| e.to_string())?;
2434 let pages = resp.get("pages").and_then(|p| p.as_array()).cloned().unwrap_or_default();
2435 Ok(compact_items(&pages, &["id", "name"]))
2436 }
2437
2438 "clickup_tag_list" => {
2439 let space_id = args
2440 .get("space_id")
2441 .and_then(|v| v.as_str())
2442 .ok_or("Missing required parameter: space_id")?;
2443 let path = format!("/v2/space/{}/tag", space_id);
2444 let resp = client.get(&path).await.map_err(|e| e.to_string())?;
2445 let tags = resp.get("tags").and_then(|t| t.as_array()).cloned().unwrap_or_default();
2446 Ok(compact_items(&tags, &["name", "tag_fg", "tag_bg"]))
2447 }
2448
2449 "clickup_task_add_tag" => {
2450 let task_id = args
2451 .get("task_id")
2452 .and_then(|v| v.as_str())
2453 .ok_or("Missing required parameter: task_id")?;
2454 let tag_name = args
2455 .get("tag_name")
2456 .and_then(|v| v.as_str())
2457 .ok_or("Missing required parameter: tag_name")?;
2458 let path = format!("/v2/task/{}/tag/{}", task_id, tag_name);
2459 client.post(&path, &json!({})).await.map_err(|e| e.to_string())?;
2460 Ok(json!({"message": format!("Tag '{}' added to task {}", tag_name, task_id)}))
2461 }
2462
2463 "clickup_task_remove_tag" => {
2464 let task_id = args
2465 .get("task_id")
2466 .and_then(|v| v.as_str())
2467 .ok_or("Missing required parameter: task_id")?;
2468 let tag_name = args
2469 .get("tag_name")
2470 .and_then(|v| v.as_str())
2471 .ok_or("Missing required parameter: tag_name")?;
2472 let path = format!("/v2/task/{}/tag/{}", task_id, tag_name);
2473 client.delete(&path).await.map_err(|e| e.to_string())?;
2474 Ok(json!({"message": format!("Tag '{}' removed from task {}", tag_name, task_id)}))
2475 }
2476
2477 "clickup_webhook_list" => {
2478 let team_id = resolve_workspace(args)?;
2479 let path = format!("/v2/team/{}/webhook", team_id);
2480 let resp = client.get(&path).await.map_err(|e| e.to_string())?;
2481 let webhooks = resp.get("webhooks").and_then(|w| w.as_array()).cloned().unwrap_or_default();
2482 Ok(compact_items(&webhooks, &["id", "endpoint", "events", "status"]))
2483 }
2484
2485 "clickup_member_list" => {
2486 let path = if let Some(task_id) = args.get("task_id").and_then(|v| v.as_str()) {
2487 format!("/v2/task/{}/member", task_id)
2488 } else if let Some(list_id) = args.get("list_id").and_then(|v| v.as_str()) {
2489 format!("/v2/list/{}/member", list_id)
2490 } else {
2491 return Err("Provide either task_id or list_id".to_string());
2492 };
2493 let resp = client.get(&path).await.map_err(|e| e.to_string())?;
2494 let members = resp.get("members").and_then(|m| m.as_array()).cloned().unwrap_or_default();
2495 Ok(compact_items(&members, &["id", "username", "email"]))
2496 }
2497
2498 "clickup_template_list" => {
2499 let team_id = resolve_workspace(args)?;
2500 let page = args.get("page").and_then(|v| v.as_i64()).unwrap_or(0);
2501 let path = format!("/v2/team/{}/taskTemplate?page={}", team_id, page);
2502 let resp = client.get(&path).await.map_err(|e| e.to_string())?;
2503 let templates = resp.get("templates").and_then(|t| t.as_array()).cloned().unwrap_or_default();
2504 Ok(compact_items(&templates, &["id", "name"]))
2505 }
2506
2507 "clickup_space_get" => {
2508 let space_id = args.get("space_id").and_then(|v| v.as_str())
2509 .ok_or("Missing required parameter: space_id")?;
2510 let resp = client.get(&format!("/v2/space/{}", space_id)).await.map_err(|e| e.to_string())?;
2511 Ok(compact_items(&[resp], &["id", "name", "private", "archived"]))
2512 }
2513
2514 "clickup_space_create" => {
2515 let team_id = resolve_workspace(args)?;
2516 let name = args.get("name").and_then(|v| v.as_str())
2517 .ok_or("Missing required parameter: name")?;
2518 let mut body = json!({"name": name});
2519 if let Some(private) = args.get("private").and_then(|v| v.as_bool()) {
2520 body["private"] = json!(private);
2521 }
2522 let resp = client.post(&format!("/v2/team/{}/space", team_id), &body).await.map_err(|e| e.to_string())?;
2523 Ok(compact_items(&[resp], &["id", "name", "private"]))
2524 }
2525
2526 "clickup_space_update" => {
2527 let space_id = args.get("space_id").and_then(|v| v.as_str())
2528 .ok_or("Missing required parameter: space_id")?;
2529 let mut body = json!({});
2530 if let Some(name) = args.get("name").and_then(|v| v.as_str()) { body["name"] = json!(name); }
2531 if let Some(private) = args.get("private").and_then(|v| v.as_bool()) { body["private"] = json!(private); }
2532 if let Some(archived) = args.get("archived").and_then(|v| v.as_bool()) { body["archived"] = json!(archived); }
2533 let resp = client.put(&format!("/v2/space/{}", space_id), &body).await.map_err(|e| e.to_string())?;
2534 Ok(compact_items(&[resp], &["id", "name", "private", "archived"]))
2535 }
2536
2537 "clickup_space_delete" => {
2538 let space_id = args.get("space_id").and_then(|v| v.as_str())
2539 .ok_or("Missing required parameter: space_id")?;
2540 client.delete(&format!("/v2/space/{}", space_id)).await.map_err(|e| e.to_string())?;
2541 Ok(json!({"message": format!("Space {} deleted", space_id)}))
2542 }
2543
2544 "clickup_folder_get" => {
2545 let folder_id = args.get("folder_id").and_then(|v| v.as_str())
2546 .ok_or("Missing required parameter: folder_id")?;
2547 let resp = client.get(&format!("/v2/folder/{}", folder_id)).await.map_err(|e| e.to_string())?;
2548 Ok(compact_items(&[resp], &["id", "name", "task_count"]))
2549 }
2550
2551 "clickup_folder_create" => {
2552 let space_id = args.get("space_id").and_then(|v| v.as_str())
2553 .ok_or("Missing required parameter: space_id")?;
2554 let name = args.get("name").and_then(|v| v.as_str())
2555 .ok_or("Missing required parameter: name")?;
2556 let body = json!({"name": name});
2557 let resp = client.post(&format!("/v2/space/{}/folder", space_id), &body).await.map_err(|e| e.to_string())?;
2558 Ok(compact_items(&[resp], &["id", "name"]))
2559 }
2560
2561 "clickup_folder_update" => {
2562 let folder_id = args.get("folder_id").and_then(|v| v.as_str())
2563 .ok_or("Missing required parameter: folder_id")?;
2564 let name = args.get("name").and_then(|v| v.as_str())
2565 .ok_or("Missing required parameter: name")?;
2566 let body = json!({"name": name});
2567 let resp = client.put(&format!("/v2/folder/{}", folder_id), &body).await.map_err(|e| e.to_string())?;
2568 Ok(compact_items(&[resp], &["id", "name"]))
2569 }
2570
2571 "clickup_folder_delete" => {
2572 let folder_id = args.get("folder_id").and_then(|v| v.as_str())
2573 .ok_or("Missing required parameter: folder_id")?;
2574 client.delete(&format!("/v2/folder/{}", folder_id)).await.map_err(|e| e.to_string())?;
2575 Ok(json!({"message": format!("Folder {} deleted", folder_id)}))
2576 }
2577
2578 "clickup_list_get" => {
2579 let list_id = args.get("list_id").and_then(|v| v.as_str())
2580 .ok_or("Missing required parameter: list_id")?;
2581 let resp = client.get(&format!("/v2/list/{}", list_id)).await.map_err(|e| e.to_string())?;
2582 Ok(compact_items(&[resp], &["id", "name", "task_count", "status", "due_date"]))
2583 }
2584
2585 "clickup_list_create" => {
2586 let name = args.get("name").and_then(|v| v.as_str())
2587 .ok_or("Missing required parameter: name")?;
2588 let mut body = json!({"name": name});
2589 if let Some(content) = args.get("content").and_then(|v| v.as_str()) { body["content"] = json!(content); }
2590 if let Some(due_date) = args.get("due_date").and_then(|v| v.as_i64()) { body["due_date"] = json!(due_date); }
2591 if let Some(status) = args.get("status").and_then(|v| v.as_str()) { body["status"] = json!(status); }
2592 let path = if let Some(folder_id) = args.get("folder_id").and_then(|v| v.as_str()) {
2593 format!("/v2/folder/{}/list", folder_id)
2594 } else if let Some(space_id) = args.get("space_id").and_then(|v| v.as_str()) {
2595 format!("/v2/space/{}/list", space_id)
2596 } else {
2597 return Err("Provide either folder_id or space_id".to_string());
2598 };
2599 let resp = client.post(&path, &body).await.map_err(|e| e.to_string())?;
2600 Ok(compact_items(&[resp], &["id", "name"]))
2601 }
2602
2603 "clickup_list_update" => {
2604 let list_id = args.get("list_id").and_then(|v| v.as_str())
2605 .ok_or("Missing required parameter: list_id")?;
2606 let mut body = json!({});
2607 if let Some(name) = args.get("name").and_then(|v| v.as_str()) { body["name"] = json!(name); }
2608 if let Some(content) = args.get("content").and_then(|v| v.as_str()) { body["content"] = json!(content); }
2609 if let Some(due_date) = args.get("due_date").and_then(|v| v.as_i64()) { body["due_date"] = json!(due_date); }
2610 if let Some(status) = args.get("status").and_then(|v| v.as_str()) { body["status"] = json!(status); }
2611 let resp = client.put(&format!("/v2/list/{}", list_id), &body).await.map_err(|e| e.to_string())?;
2612 Ok(compact_items(&[resp], &["id", "name", "task_count", "status"]))
2613 }
2614
2615 "clickup_list_delete" => {
2616 let list_id = args.get("list_id").and_then(|v| v.as_str())
2617 .ok_or("Missing required parameter: list_id")?;
2618 client.delete(&format!("/v2/list/{}", list_id)).await.map_err(|e| e.to_string())?;
2619 Ok(json!({"message": format!("List {} deleted", list_id)}))
2620 }
2621
2622 "clickup_list_add_task" => {
2623 let list_id = args.get("list_id").and_then(|v| v.as_str())
2624 .ok_or("Missing required parameter: list_id")?;
2625 let task_id = args.get("task_id").and_then(|v| v.as_str())
2626 .ok_or("Missing required parameter: task_id")?;
2627 client.post(&format!("/v2/list/{}/task/{}", list_id, task_id), &json!({})).await.map_err(|e| e.to_string())?;
2628 Ok(json!({"message": format!("Task {} added to list {}", task_id, list_id)}))
2629 }
2630
2631 "clickup_list_remove_task" => {
2632 let list_id = args.get("list_id").and_then(|v| v.as_str())
2633 .ok_or("Missing required parameter: list_id")?;
2634 let task_id = args.get("task_id").and_then(|v| v.as_str())
2635 .ok_or("Missing required parameter: task_id")?;
2636 client.delete(&format!("/v2/list/{}/task/{}", list_id, task_id)).await.map_err(|e| e.to_string())?;
2637 Ok(json!({"message": format!("Task {} removed from list {}", task_id, list_id)}))
2638 }
2639
2640 "clickup_comment_update" => {
2641 let comment_id = args.get("comment_id").and_then(|v| v.as_str())
2642 .ok_or("Missing required parameter: comment_id")?;
2643 let text = args.get("text").and_then(|v| v.as_str())
2644 .ok_or("Missing required parameter: text")?;
2645 let mut body = json!({"comment_text": text});
2646 if let Some(assignee) = args.get("assignee").and_then(|v| v.as_i64()) { body["assignee"] = json!(assignee); }
2647 if let Some(resolved) = args.get("resolved").and_then(|v| v.as_bool()) { body["resolved"] = json!(resolved); }
2648 client.put(&format!("/v2/comment/{}", comment_id), &body).await.map_err(|e| e.to_string())?;
2649 Ok(json!({"message": format!("Comment {} updated", comment_id)}))
2650 }
2651
2652 "clickup_comment_delete" => {
2653 let comment_id = args.get("comment_id").and_then(|v| v.as_str())
2654 .ok_or("Missing required parameter: comment_id")?;
2655 client.delete(&format!("/v2/comment/{}", comment_id)).await.map_err(|e| e.to_string())?;
2656 Ok(json!({"message": format!("Comment {} deleted", comment_id)}))
2657 }
2658
2659 "clickup_task_add_dep" => {
2660 let task_id = args.get("task_id").and_then(|v| v.as_str())
2661 .ok_or("Missing required parameter: task_id")?;
2662 let mut body = json!({});
2663 if let Some(dep) = args.get("depends_on").and_then(|v| v.as_str()) { body["depends_on"] = json!(dep); }
2664 if let Some(dep) = args.get("dependency_of").and_then(|v| v.as_str()) { body["dependency_of"] = json!(dep); }
2665 client.post(&format!("/v2/task/{}/dependency", task_id), &body).await.map_err(|e| e.to_string())?;
2666 Ok(json!({"message": format!("Dependency added to task {}", task_id)}))
2667 }
2668
2669 "clickup_task_remove_dep" => {
2670 let task_id = args.get("task_id").and_then(|v| v.as_str())
2671 .ok_or("Missing required parameter: task_id")?;
2672 let mut body = json!({});
2673 if let Some(dep) = args.get("depends_on").and_then(|v| v.as_str()) { body["depends_on"] = json!(dep); }
2674 if let Some(dep) = args.get("dependency_of").and_then(|v| v.as_str()) { body["dependency_of"] = json!(dep); }
2675 client.delete_with_body(&format!("/v2/task/{}/dependency", task_id), &body).await.map_err(|e| e.to_string())?;
2676 Ok(json!({"message": format!("Dependency removed from task {}", task_id)}))
2677 }
2678
2679 "clickup_task_link" => {
2680 let task_id = args.get("task_id").and_then(|v| v.as_str())
2681 .ok_or("Missing required parameter: task_id")?;
2682 let links_to = args.get("links_to").and_then(|v| v.as_str())
2683 .ok_or("Missing required parameter: links_to")?;
2684 let resp = client.post(&format!("/v2/task/{}/link/{}", task_id, links_to), &json!({})).await.map_err(|e| e.to_string())?;
2685 Ok(json!({"message": format!("Task {} linked to {}", task_id, links_to), "data": resp}))
2686 }
2687
2688 "clickup_task_unlink" => {
2689 let task_id = args.get("task_id").and_then(|v| v.as_str())
2690 .ok_or("Missing required parameter: task_id")?;
2691 let links_to = args.get("links_to").and_then(|v| v.as_str())
2692 .ok_or("Missing required parameter: links_to")?;
2693 client.delete(&format!("/v2/task/{}/link/{}", task_id, links_to)).await.map_err(|e| e.to_string())?;
2694 Ok(json!({"message": format!("Task {} unlinked from {}", task_id, links_to)}))
2695 }
2696
2697 "clickup_goal_delete" => {
2698 let goal_id = args.get("goal_id").and_then(|v| v.as_str())
2699 .ok_or("Missing required parameter: goal_id")?;
2700 client.delete(&format!("/v2/goal/{}", goal_id)).await.map_err(|e| e.to_string())?;
2701 Ok(json!({"message": format!("Goal {} deleted", goal_id)}))
2702 }
2703
2704 "clickup_goal_add_kr" => {
2705 let goal_id = args.get("goal_id").and_then(|v| v.as_str())
2706 .ok_or("Missing required parameter: goal_id")?;
2707 let name = args.get("name").and_then(|v| v.as_str())
2708 .ok_or("Missing required parameter: name")?;
2709 let kr_type = args.get("type").and_then(|v| v.as_str())
2710 .ok_or("Missing required parameter: type")?;
2711 let steps_start = args.get("steps_start").and_then(|v| v.as_f64())
2712 .ok_or("Missing required parameter: steps_start")?;
2713 let steps_end = args.get("steps_end").and_then(|v| v.as_f64())
2714 .ok_or("Missing required parameter: steps_end")?;
2715 let mut body = json!({"name": name, "type": kr_type, "steps_start": steps_start, "steps_end": steps_end});
2716 if let Some(unit) = args.get("unit").and_then(|v| v.as_str()) { body["unit"] = json!(unit); }
2717 if let Some(owners) = args.get("owner_ids") { body["owners"] = owners.clone(); }
2718 if let Some(task_ids) = args.get("task_ids") { body["task_ids"] = task_ids.clone(); }
2719 if let Some(list_ids) = args.get("list_ids") { body["list_ids"] = list_ids.clone(); }
2720 let resp = client.post(&format!("/v2/goal/{}/key_result", goal_id), &body).await.map_err(|e| e.to_string())?;
2721 let kr = resp.get("key_result").cloned().unwrap_or(resp);
2722 Ok(compact_items(&[kr], &["id", "name", "type", "steps_start", "steps_end", "steps_current"]))
2723 }
2724
2725 "clickup_goal_update_kr" => {
2726 let kr_id = args.get("kr_id").and_then(|v| v.as_str())
2727 .ok_or("Missing required parameter: kr_id")?;
2728 let mut body = json!({});
2729 if let Some(v) = args.get("steps_current").and_then(|v| v.as_f64()) { body["steps_current"] = json!(v); }
2730 if let Some(v) = args.get("name").and_then(|v| v.as_str()) { body["name"] = json!(v); }
2731 if let Some(v) = args.get("unit").and_then(|v| v.as_str()) { body["unit"] = json!(v); }
2732 let resp = client.put(&format!("/v2/key_result/{}", kr_id), &body).await.map_err(|e| e.to_string())?;
2733 let kr = resp.get("key_result").cloned().unwrap_or(resp);
2734 Ok(compact_items(&[kr], &["id", "name", "steps_current", "steps_end"]))
2735 }
2736
2737 "clickup_goal_delete_kr" => {
2738 let kr_id = args.get("kr_id").and_then(|v| v.as_str())
2739 .ok_or("Missing required parameter: kr_id")?;
2740 client.delete(&format!("/v2/key_result/{}", kr_id)).await.map_err(|e| e.to_string())?;
2741 Ok(json!({"message": format!("Key result {} deleted", kr_id)}))
2742 }
2743
2744 "clickup_time_get" => {
2745 let team_id = resolve_workspace(args)?;
2746 let timer_id = args.get("timer_id").and_then(|v| v.as_str())
2747 .ok_or("Missing required parameter: timer_id")?;
2748 let resp = client.get(&format!("/v2/team/{}/time_entries/{}", team_id, timer_id)).await.map_err(|e| e.to_string())?;
2749 let data = resp.get("data").cloned().unwrap_or(resp);
2750 Ok(compact_items(&[data], &["id", "task", "duration", "start", "end", "billable"]))
2751 }
2752
2753 "clickup_time_create" => {
2754 let team_id = resolve_workspace(args)?;
2755 let start = args.get("start").and_then(|v| v.as_i64())
2756 .ok_or("Missing required parameter: start")?;
2757 let duration = args.get("duration").and_then(|v| v.as_i64())
2758 .ok_or("Missing required parameter: duration")?;
2759 let mut body = json!({"start": start, "duration": duration});
2760 if let Some(task_id) = args.get("task_id").and_then(|v| v.as_str()) { body["tid"] = json!(task_id); }
2761 if let Some(desc) = args.get("description").and_then(|v| v.as_str()) { body["description"] = json!(desc); }
2762 if let Some(billable) = args.get("billable").and_then(|v| v.as_bool()) { body["billable"] = json!(billable); }
2763 let resp = client.post(&format!("/v2/team/{}/time_entries", team_id), &body).await.map_err(|e| e.to_string())?;
2764 let data = resp.get("data").cloned().unwrap_or(resp);
2765 Ok(compact_items(&[data], &["id", "task", "duration", "start", "billable"]))
2766 }
2767
2768 "clickup_time_update" => {
2769 let team_id = resolve_workspace(args)?;
2770 let timer_id = args.get("timer_id").and_then(|v| v.as_str())
2771 .ok_or("Missing required parameter: timer_id")?;
2772 let mut body = json!({});
2773 if let Some(start) = args.get("start").and_then(|v| v.as_i64()) { body["start"] = json!(start); }
2774 if let Some(duration) = args.get("duration").and_then(|v| v.as_i64()) { body["duration"] = json!(duration); }
2775 if let Some(desc) = args.get("description").and_then(|v| v.as_str()) { body["description"] = json!(desc); }
2776 if let Some(billable) = args.get("billable").and_then(|v| v.as_bool()) { body["billable"] = json!(billable); }
2777 let resp = client.put(&format!("/v2/team/{}/time_entries/{}", team_id, timer_id), &body).await.map_err(|e| e.to_string())?;
2778 let data = resp.get("data").cloned().unwrap_or(resp);
2779 Ok(compact_items(&[data], &["id", "task", "duration", "start", "billable"]))
2780 }
2781
2782 "clickup_time_delete" => {
2783 let team_id = resolve_workspace(args)?;
2784 let timer_id = args.get("timer_id").and_then(|v| v.as_str())
2785 .ok_or("Missing required parameter: timer_id")?;
2786 client.delete(&format!("/v2/team/{}/time_entries/{}", team_id, timer_id)).await.map_err(|e| e.to_string())?;
2787 Ok(json!({"message": format!("Time entry {} deleted", timer_id)}))
2788 }
2789
2790 "clickup_view_get" => {
2791 let view_id = args.get("view_id").and_then(|v| v.as_str())
2792 .ok_or("Missing required parameter: view_id")?;
2793 let resp = client.get(&format!("/v2/view/{}", view_id)).await.map_err(|e| e.to_string())?;
2794 let view = resp.get("view").cloned().unwrap_or(resp);
2795 Ok(compact_items(&[view], &["id", "name", "type"]))
2796 }
2797
2798 "clickup_view_create" => {
2799 let scope = args.get("scope").and_then(|v| v.as_str())
2800 .ok_or("Missing required parameter: scope")?;
2801 let scope_id = args.get("scope_id").and_then(|v| v.as_str())
2802 .ok_or("Missing required parameter: scope_id")?;
2803 let name = args.get("name").and_then(|v| v.as_str())
2804 .ok_or("Missing required parameter: name")?;
2805 let view_type = args.get("type").and_then(|v| v.as_str())
2806 .ok_or("Missing required parameter: type")?;
2807 let body = json!({"name": name, "type": view_type});
2808 let resp = client.post(&format!("/v2/{}/{}/view", scope, scope_id), &body).await.map_err(|e| e.to_string())?;
2809 let view = resp.get("view").cloned().unwrap_or(resp);
2810 Ok(compact_items(&[view], &["id", "name", "type"]))
2811 }
2812
2813 "clickup_view_update" => {
2814 let view_id = args.get("view_id").and_then(|v| v.as_str())
2815 .ok_or("Missing required parameter: view_id")?;
2816 let name = args.get("name").and_then(|v| v.as_str())
2817 .ok_or("Missing required parameter: name")?;
2818 let view_type = args.get("type").and_then(|v| v.as_str())
2819 .ok_or("Missing required parameter: type")?;
2820 let body = json!({"name": name, "type": view_type});
2821 let resp = client.put(&format!("/v2/view/{}", view_id), &body).await.map_err(|e| e.to_string())?;
2822 let view = resp.get("view").cloned().unwrap_or(resp);
2823 Ok(compact_items(&[view], &["id", "name", "type"]))
2824 }
2825
2826 "clickup_view_delete" => {
2827 let view_id = args.get("view_id").and_then(|v| v.as_str())
2828 .ok_or("Missing required parameter: view_id")?;
2829 client.delete(&format!("/v2/view/{}", view_id)).await.map_err(|e| e.to_string())?;
2830 Ok(json!({"message": format!("View {} deleted", view_id)}))
2831 }
2832
2833 "clickup_doc_create" => {
2834 let team_id = resolve_workspace(args)?;
2835 let name = args.get("name").and_then(|v| v.as_str())
2836 .ok_or("Missing required parameter: name")?;
2837 let mut body = json!({"name": name});
2838 if let Some(parent) = args.get("parent") { body["parent"] = parent.clone(); }
2839 let resp = client.post(&format!("/v3/workspaces/{}/docs", team_id), &body).await.map_err(|e| e.to_string())?;
2840 Ok(compact_items(&[resp], &["id", "name"]))
2841 }
2842
2843 "clickup_doc_add_page" => {
2844 let team_id = resolve_workspace(args)?;
2845 let doc_id = args.get("doc_id").and_then(|v| v.as_str())
2846 .ok_or("Missing required parameter: doc_id")?;
2847 let name = args.get("name").and_then(|v| v.as_str())
2848 .ok_or("Missing required parameter: name")?;
2849 let mut body = json!({"name": name});
2850 if let Some(content) = args.get("content").and_then(|v| v.as_str()) { body["content"] = json!(content); }
2851 if let Some(subtitle) = args.get("sub_title").and_then(|v| v.as_str()) { body["sub_title"] = json!(subtitle); }
2852 if let Some(parent_page_id) = args.get("parent_page_id").and_then(|v| v.as_str()) { body["parent_page_id"] = json!(parent_page_id); }
2853 let resp = client.post(&format!("/v3/workspaces/{}/docs/{}/pages", team_id, doc_id), &body).await.map_err(|e| e.to_string())?;
2854 Ok(compact_items(&[resp], &["id", "name"]))
2855 }
2856
2857 "clickup_doc_edit_page" => {
2858 let team_id = resolve_workspace(args)?;
2859 let doc_id = args.get("doc_id").and_then(|v| v.as_str())
2860 .ok_or("Missing required parameter: doc_id")?;
2861 let page_id = args.get("page_id").and_then(|v| v.as_str())
2862 .ok_or("Missing required parameter: page_id")?;
2863 let mut body = json!({});
2864 if let Some(name) = args.get("name").and_then(|v| v.as_str()) { body["name"] = json!(name); }
2865 if let Some(content) = args.get("content").and_then(|v| v.as_str()) { body["content"] = json!(content); }
2866 let resp = client.put(&format!("/v3/workspaces/{}/docs/{}/pages/{}", team_id, doc_id, page_id), &body).await.map_err(|e| e.to_string())?;
2867 Ok(compact_items(&[resp], &["id", "name"]))
2868 }
2869
2870 "clickup_chat_channel_create" => {
2871 let team_id = resolve_workspace(args)?;
2872 let name = args.get("name").and_then(|v| v.as_str())
2873 .ok_or("Missing required parameter: name")?;
2874 let mut body = json!({"name": name});
2875 if let Some(desc) = args.get("description").and_then(|v| v.as_str()) { body["description"] = json!(desc); }
2876 if let Some(vis) = args.get("visibility").and_then(|v| v.as_str()) { body["visibility"] = json!(vis); }
2877 let resp = client.post(&format!("/v3/workspaces/{}/chat/channels", team_id), &body).await.map_err(|e| e.to_string())?;
2878 Ok(compact_items(&[resp], &["id", "name", "visibility"]))
2879 }
2880
2881 "clickup_chat_channel_get" => {
2882 let team_id = resolve_workspace(args)?;
2883 let channel_id = args.get("channel_id").and_then(|v| v.as_str())
2884 .ok_or("Missing required parameter: channel_id")?;
2885 let resp = client.get(&format!("/v3/workspaces/{}/chat/channels/{}", team_id, channel_id)).await.map_err(|e| e.to_string())?;
2886 Ok(compact_items(&[resp], &["id", "name", "visibility"]))
2887 }
2888
2889 "clickup_chat_channel_update" => {
2890 let team_id = resolve_workspace(args)?;
2891 let channel_id = args.get("channel_id").and_then(|v| v.as_str())
2892 .ok_or("Missing required parameter: channel_id")?;
2893 let mut body = json!({});
2894 if let Some(name) = args.get("name").and_then(|v| v.as_str()) { body["name"] = json!(name); }
2895 if let Some(desc) = args.get("description").and_then(|v| v.as_str()) { body["description"] = json!(desc); }
2896 let resp = client.patch(&format!("/v3/workspaces/{}/chat/channels/{}", team_id, channel_id), &body).await.map_err(|e| e.to_string())?;
2897 Ok(compact_items(&[resp], &["id", "name"]))
2898 }
2899
2900 "clickup_chat_channel_delete" => {
2901 let team_id = resolve_workspace(args)?;
2902 let channel_id = args.get("channel_id").and_then(|v| v.as_str())
2903 .ok_or("Missing required parameter: channel_id")?;
2904 client.delete(&format!("/v3/workspaces/{}/chat/channels/{}", team_id, channel_id)).await.map_err(|e| e.to_string())?;
2905 Ok(json!({"message": format!("Channel {} deleted", channel_id)}))
2906 }
2907
2908 "clickup_chat_message_list" => {
2909 let team_id = resolve_workspace(args)?;
2910 let channel_id = args.get("channel_id").and_then(|v| v.as_str())
2911 .ok_or("Missing required parameter: channel_id")?;
2912 let mut path = format!("/v3/workspaces/{}/chat/channels/{}/messages", team_id, channel_id);
2913 if let Some(cursor) = args.get("cursor").and_then(|v| v.as_str()) {
2914 path.push_str(&format!("?cursor={}", cursor));
2915 }
2916 let resp = client.get(&path).await.map_err(|e| e.to_string())?;
2917 let messages = resp.get("messages").and_then(|m| m.as_array()).cloned().unwrap_or_default();
2918 Ok(compact_items(&messages, &["id", "content", "date"]))
2919 }
2920
2921 "clickup_chat_message_send" => {
2922 let team_id = resolve_workspace(args)?;
2923 let channel_id = args.get("channel_id").and_then(|v| v.as_str())
2924 .ok_or("Missing required parameter: channel_id")?;
2925 let content = args.get("content").and_then(|v| v.as_str())
2926 .ok_or("Missing required parameter: content")?;
2927 let body = json!({"content": content});
2928 let resp = client.post(&format!("/v3/workspaces/{}/chat/channels/{}/messages", team_id, channel_id), &body).await.map_err(|e| e.to_string())?;
2929 Ok(json!({"message": "Message sent", "id": resp.get("id")}))
2930 }
2931
2932 "clickup_chat_message_delete" => {
2933 let team_id = resolve_workspace(args)?;
2934 let message_id = args.get("message_id").and_then(|v| v.as_str())
2935 .ok_or("Missing required parameter: message_id")?;
2936 client.delete(&format!("/v3/workspaces/{}/chat/messages/{}", team_id, message_id)).await.map_err(|e| e.to_string())?;
2937 Ok(json!({"message": format!("Message {} deleted", message_id)}))
2938 }
2939
2940 "clickup_chat_dm" => {
2941 let team_id = resolve_workspace(args)?;
2942 let user_id = args.get("user_id").and_then(|v| v.as_i64())
2943 .ok_or("Missing required parameter: user_id")?;
2944 let content = args.get("content").and_then(|v| v.as_str())
2945 .ok_or("Missing required parameter: content")?;
2946 let body = json!({"user_id": user_id, "content": content});
2947 let resp = client.post(&format!("/v3/workspaces/{}/chat/channels/direct_message", team_id), &body).await.map_err(|e| e.to_string())?;
2948 Ok(json!({"message": "DM sent", "id": resp.get("id")}))
2949 }
2950
2951 "clickup_webhook_create" => {
2952 let team_id = resolve_workspace(args)?;
2953 let endpoint = args.get("endpoint").and_then(|v| v.as_str())
2954 .ok_or("Missing required parameter: endpoint")?;
2955 let events = args.get("events").ok_or("Missing required parameter: events")?;
2956 let mut body = json!({"endpoint": endpoint, "events": events});
2957 if let Some(space_id) = args.get("space_id").and_then(|v| v.as_str()) { body["space_id"] = json!(space_id); }
2958 if let Some(folder_id) = args.get("folder_id").and_then(|v| v.as_str()) { body["folder_id"] = json!(folder_id); }
2959 if let Some(list_id) = args.get("list_id").and_then(|v| v.as_str()) { body["list_id"] = json!(list_id); }
2960 if let Some(task_id) = args.get("task_id").and_then(|v| v.as_str()) { body["task_id"] = json!(task_id); }
2961 let resp = client.post(&format!("/v2/team/{}/webhook", team_id), &body).await.map_err(|e| e.to_string())?;
2962 let webhook = resp.get("webhook").cloned().unwrap_or(resp);
2963 Ok(compact_items(&[webhook], &["id", "endpoint", "events", "status"]))
2964 }
2965
2966 "clickup_webhook_update" => {
2967 let webhook_id = args.get("webhook_id").and_then(|v| v.as_str())
2968 .ok_or("Missing required parameter: webhook_id")?;
2969 let mut body = json!({});
2970 if let Some(endpoint) = args.get("endpoint").and_then(|v| v.as_str()) { body["endpoint"] = json!(endpoint); }
2971 if let Some(events) = args.get("events") { body["events"] = events.clone(); }
2972 if let Some(status) = args.get("status").and_then(|v| v.as_str()) { body["status"] = json!(status); }
2973 let resp = client.put(&format!("/v2/webhook/{}", webhook_id), &body).await.map_err(|e| e.to_string())?;
2974 let webhook = resp.get("webhook").cloned().unwrap_or(resp);
2975 Ok(compact_items(&[webhook], &["id", "endpoint", "events", "status"]))
2976 }
2977
2978 "clickup_webhook_delete" => {
2979 let webhook_id = args.get("webhook_id").and_then(|v| v.as_str())
2980 .ok_or("Missing required parameter: webhook_id")?;
2981 client.delete(&format!("/v2/webhook/{}", webhook_id)).await.map_err(|e| e.to_string())?;
2982 Ok(json!({"message": format!("Webhook {} deleted", webhook_id)}))
2983 }
2984
2985 "clickup_checklist_add_item" => {
2986 let checklist_id = args.get("checklist_id").and_then(|v| v.as_str())
2987 .ok_or("Missing required parameter: checklist_id")?;
2988 let name = args.get("name").and_then(|v| v.as_str())
2989 .ok_or("Missing required parameter: name")?;
2990 let mut body = json!({"name": name});
2991 if let Some(assignee) = args.get("assignee").and_then(|v| v.as_i64()) { body["assignee"] = json!(assignee); }
2992 let resp = client.post(&format!("/v2/checklist/{}/checklist_item", checklist_id), &body).await.map_err(|e| e.to_string())?;
2993 let item = resp.get("checklist").cloned().unwrap_or(resp);
2994 Ok(compact_items(&[item], &["id", "name"]))
2995 }
2996
2997 "clickup_checklist_update_item" => {
2998 let checklist_id = args.get("checklist_id").and_then(|v| v.as_str())
2999 .ok_or("Missing required parameter: checklist_id")?;
3000 let item_id = args.get("item_id").and_then(|v| v.as_str())
3001 .ok_or("Missing required parameter: item_id")?;
3002 let mut body = json!({});
3003 if let Some(name) = args.get("name").and_then(|v| v.as_str()) { body["name"] = json!(name); }
3004 if let Some(resolved) = args.get("resolved").and_then(|v| v.as_bool()) { body["resolved"] = json!(resolved); }
3005 if let Some(assignee) = args.get("assignee").and_then(|v| v.as_i64()) { body["assignee"] = json!(assignee); }
3006 client.put(&format!("/v2/checklist/{}/checklist_item/{}", checklist_id, item_id), &body).await.map_err(|e| e.to_string())?;
3007 Ok(json!({"message": format!("Checklist item {} updated", item_id)}))
3008 }
3009
3010 "clickup_checklist_delete_item" => {
3011 let checklist_id = args.get("checklist_id").and_then(|v| v.as_str())
3012 .ok_or("Missing required parameter: checklist_id")?;
3013 let item_id = args.get("item_id").and_then(|v| v.as_str())
3014 .ok_or("Missing required parameter: item_id")?;
3015 client.delete(&format!("/v2/checklist/{}/checklist_item/{}", checklist_id, item_id)).await.map_err(|e| e.to_string())?;
3016 Ok(json!({"message": format!("Checklist item {} deleted", item_id)}))
3017 }
3018
3019 "clickup_user_get" => {
3020 let team_id = resolve_workspace(args)?;
3021 let user_id = args.get("user_id").and_then(|v| v.as_i64())
3022 .ok_or("Missing required parameter: user_id")?;
3023 let resp = client.get(&format!("/v2/team/{}/user/{}", team_id, user_id)).await.map_err(|e| e.to_string())?;
3024 let member = resp.get("member").cloned().unwrap_or(resp);
3025 Ok(compact_items(&[member], &["user", "role"]))
3026 }
3027
3028 "clickup_workspace_seats" => {
3029 let team_id = resolve_workspace(args)?;
3030 let resp = client.get(&format!("/v2/team/{}/seats", team_id)).await.map_err(|e| e.to_string())?;
3031 Ok(json!(resp))
3032 }
3033
3034 "clickup_workspace_plan" => {
3035 let team_id = resolve_workspace(args)?;
3036 let resp = client.get(&format!("/v2/team/{}/plan", team_id)).await.map_err(|e| e.to_string())?;
3037 Ok(json!(resp))
3038 }
3039
3040 "clickup_tag_create" => {
3041 let space_id = args.get("space_id").and_then(|v| v.as_str())
3042 .ok_or("Missing required parameter: space_id")?;
3043 let name = args.get("name").and_then(|v| v.as_str())
3044 .ok_or("Missing required parameter: name")?;
3045 let mut tag = json!({"name": name});
3046 if let Some(fg) = args.get("tag_fg").and_then(|v| v.as_str()) { tag["tag_fg"] = json!(fg); }
3047 if let Some(bg) = args.get("tag_bg").and_then(|v| v.as_str()) { tag["tag_bg"] = json!(bg); }
3048 let body = json!({"tag": tag});
3049 client.post(&format!("/v2/space/{}/tag", space_id), &body).await.map_err(|e| e.to_string())?;
3050 Ok(json!({"message": format!("Tag '{}' created in space {}", name, space_id)}))
3051 }
3052
3053 "clickup_tag_update" => {
3054 let space_id = args.get("space_id").and_then(|v| v.as_str())
3055 .ok_or("Missing required parameter: space_id")?;
3056 let tag_name = args.get("tag_name").and_then(|v| v.as_str())
3057 .ok_or("Missing required parameter: tag_name")?;
3058 let mut tag = json!({});
3059 if let Some(name) = args.get("name").and_then(|v| v.as_str()) { tag["name"] = json!(name); }
3060 if let Some(fg) = args.get("tag_fg").and_then(|v| v.as_str()) { tag["tag_fg"] = json!(fg); }
3061 if let Some(bg) = args.get("tag_bg").and_then(|v| v.as_str()) { tag["tag_bg"] = json!(bg); }
3062 let body = json!({"tag": tag});
3063 client.put(&format!("/v2/space/{}/tag/{}", space_id, tag_name), &body).await.map_err(|e| e.to_string())?;
3064 Ok(json!({"message": format!("Tag '{}' updated", tag_name)}))
3065 }
3066
3067 "clickup_tag_delete" => {
3068 let space_id = args.get("space_id").and_then(|v| v.as_str())
3069 .ok_or("Missing required parameter: space_id")?;
3070 let tag_name = args.get("tag_name").and_then(|v| v.as_str())
3071 .ok_or("Missing required parameter: tag_name")?;
3072 client.delete(&format!("/v2/space/{}/tag/{}", space_id, tag_name)).await.map_err(|e| e.to_string())?;
3073 Ok(json!({"message": format!("Tag '{}' deleted from space {}", tag_name, space_id)}))
3074 }
3075
3076 "clickup_field_unset" => {
3077 let task_id = args.get("task_id").and_then(|v| v.as_str())
3078 .ok_or("Missing required parameter: task_id")?;
3079 let field_id = args.get("field_id").and_then(|v| v.as_str())
3080 .ok_or("Missing required parameter: field_id")?;
3081 client.delete(&format!("/v2/task/{}/field/{}", task_id, field_id)).await.map_err(|e| e.to_string())?;
3082 Ok(json!({"message": format!("Field {} unset on task {}", field_id, task_id)}))
3083 }
3084
3085 "clickup_attachment_list" => {
3086 let team_id = resolve_workspace(args)?;
3087 let task_id = args.get("task_id").and_then(|v| v.as_str())
3088 .ok_or("Missing required parameter: task_id")?;
3089 let resp = client.get(&format!("/v3/workspaces/{}/task/{}/attachments", team_id, task_id)).await.map_err(|e| e.to_string())?;
3090 let attachments = resp.get("attachments").and_then(|a| a.as_array()).cloned().unwrap_or_default();
3091 Ok(compact_items(&attachments, &["id", "title", "url", "date"]))
3092 }
3093
3094 "clickup_shared_list" => {
3095 let team_id = resolve_workspace(args)?;
3096 let resp = client.get(&format!("/v2/team/{}/shared", team_id)).await.map_err(|e| e.to_string())?;
3097 Ok(json!(resp))
3098 }
3099
3100 "clickup_group_list" => {
3101 let team_id = resolve_workspace(args)?;
3102 let mut qs = format!("team_id={}", team_id);
3103 if let Some(group_ids) = args.get("group_ids").and_then(|v| v.as_array()) {
3104 for id in group_ids {
3105 if let Some(id) = id.as_str() {
3106 qs.push_str(&format!("&group_ids[]={}", id));
3107 }
3108 }
3109 }
3110 let resp = client.get(&format!("/v2/group?{}", qs)).await.map_err(|e| e.to_string())?;
3111 let groups = resp.get("groups").and_then(|g| g.as_array()).cloned().unwrap_or_default();
3112 Ok(compact_items(&groups, &["id", "name", "members"]))
3113 }
3114
3115 "clickup_group_create" => {
3116 let team_id = resolve_workspace(args)?;
3117 let name = args.get("name").and_then(|v| v.as_str())
3118 .ok_or("Missing required parameter: name")?;
3119 let mut body = json!({"name": name});
3120 if let Some(members) = args.get("member_ids") { body["members"] = members.clone(); }
3121 let resp = client.post(&format!("/v2/team/{}/group", team_id), &body).await.map_err(|e| e.to_string())?;
3122 Ok(compact_items(&[resp], &["id", "name"]))
3123 }
3124
3125 "clickup_group_update" => {
3126 let group_id = args.get("group_id").and_then(|v| v.as_str())
3127 .ok_or("Missing required parameter: group_id")?;
3128 let mut body = json!({});
3129 if let Some(name) = args.get("name").and_then(|v| v.as_str()) { body["name"] = json!(name); }
3130 if let Some(add) = args.get("add_members") {
3131 body["members"] = json!({"add": add, "rem": args.get("rem_members").cloned().unwrap_or(json!([]))});
3132 } else if let Some(rem) = args.get("rem_members") {
3133 body["members"] = json!({"add": [], "rem": rem});
3134 }
3135 let resp = client.put(&format!("/v2/group/{}", group_id), &body).await.map_err(|e| e.to_string())?;
3136 Ok(compact_items(&[resp], &["id", "name"]))
3137 }
3138
3139 "clickup_group_delete" => {
3140 let group_id = args.get("group_id").and_then(|v| v.as_str())
3141 .ok_or("Missing required parameter: group_id")?;
3142 client.delete(&format!("/v2/group/{}", group_id)).await.map_err(|e| e.to_string())?;
3143 Ok(json!({"message": format!("Group {} deleted", group_id)}))
3144 }
3145
3146 "clickup_role_list" => {
3147 let team_id = resolve_workspace(args)?;
3148 let resp = client.get(&format!("/v2/team/{}/customroles", team_id)).await.map_err(|e| e.to_string())?;
3149 let roles = resp.get("roles").and_then(|r| r.as_array()).cloned().unwrap_or_default();
3150 Ok(compact_items(&roles, &["id", "name"]))
3151 }
3152
3153 "clickup_guest_get" => {
3154 let team_id = resolve_workspace(args)?;
3155 let guest_id = args.get("guest_id").and_then(|v| v.as_i64())
3156 .ok_or("Missing required parameter: guest_id")?;
3157 let resp = client.get(&format!("/v2/team/{}/guest/{}", team_id, guest_id)).await.map_err(|e| e.to_string())?;
3158 let guest = resp.get("guest").cloned().unwrap_or(resp);
3159 Ok(compact_items(&[guest], &["user", "role"]))
3160 }
3161
3162 "clickup_task_time_in_status" => {
3163 let task_id = args.get("task_id").and_then(|v| v.as_str())
3164 .ok_or("Missing required parameter: task_id")?;
3165 let resp = client.get(&format!("/v2/task/{}/time_in_status", task_id)).await.map_err(|e| e.to_string())?;
3166 Ok(resp)
3167 }
3168
3169 "clickup_task_move" => {
3170 let team_id = resolve_workspace(args)?;
3171 let task_id = args.get("task_id").and_then(|v| v.as_str())
3172 .ok_or("Missing required parameter: task_id")?;
3173 let list_id = args.get("list_id").and_then(|v| v.as_str())
3174 .ok_or("Missing required parameter: list_id")?;
3175 client.put(&format!("/v3/workspaces/{}/tasks/{}/home_list/{}", team_id, task_id, list_id), &json!({})).await.map_err(|e| e.to_string())?;
3176 Ok(json!({"message": format!("Task {} moved to list {}", task_id, list_id)}))
3177 }
3178
3179 "clickup_task_set_estimate" => {
3180 let team_id = resolve_workspace(args)?;
3181 let task_id = args.get("task_id").and_then(|v| v.as_str())
3182 .ok_or("Missing required parameter: task_id")?;
3183 let user_id = args.get("user_id").and_then(|v| v.as_i64())
3184 .ok_or("Missing required parameter: user_id")?;
3185 let time_estimate = args.get("time_estimate").and_then(|v| v.as_i64())
3186 .ok_or("Missing required parameter: time_estimate")?;
3187 let body = json!({"time_estimates": [{"user_id": user_id, "time_estimate": time_estimate}]});
3188 client.patch(&format!("/v3/workspaces/{}/tasks/{}/time_estimates_by_user", team_id, task_id), &body).await.map_err(|e| e.to_string())?;
3189 Ok(json!({"message": format!("Time estimate set for task {}", task_id)}))
3190 }
3191
3192 "clickup_task_replace_estimates" => {
3193 let team_id = resolve_workspace(args)?;
3194 let task_id = args.get("task_id").and_then(|v| v.as_str())
3195 .ok_or("Missing required parameter: task_id")?;
3196 let user_id = args.get("user_id").and_then(|v| v.as_i64())
3197 .ok_or("Missing required parameter: user_id")?;
3198 let time_estimate = args.get("time_estimate").and_then(|v| v.as_i64())
3199 .ok_or("Missing required parameter: time_estimate")?;
3200 let body = json!({"time_estimates": [{"user_id": user_id, "time_estimate": time_estimate}]});
3201 client.put(&format!("/v3/workspaces/{}/tasks/{}/time_estimates_by_user", team_id, task_id), &body).await.map_err(|e| e.to_string())?;
3202 Ok(json!({"message": format!("Time estimates replaced for task {}", task_id)}))
3203 }
3204
3205 "clickup_auth_check" => {
3206 client.get("/v2/user").await.map_err(|e| e.to_string())?;
3207 Ok(json!({"message": "Token valid"}))
3208 }
3209
3210 "clickup_checklist_update" => {
3211 let checklist_id = args.get("checklist_id").and_then(|v| v.as_str())
3212 .ok_or("Missing required parameter: checklist_id")?;
3213 let mut body = json!({});
3214 if let Some(name) = args.get("name").and_then(|v| v.as_str()) { body["name"] = json!(name); }
3215 if let Some(position) = args.get("position").and_then(|v| v.as_i64()) { body["position"] = json!(position); }
3216 let resp = client.put(&format!("/v2/checklist/{}", checklist_id), &body).await.map_err(|e| e.to_string())?;
3217 let checklist = resp.get("checklist").cloned().unwrap_or(resp);
3218 Ok(compact_items(&[checklist], &["id", "name"]))
3219 }
3220
3221 "clickup_comment_replies" => {
3222 let comment_id = args.get("comment_id").and_then(|v| v.as_str())
3223 .ok_or("Missing required parameter: comment_id")?;
3224 let resp = client.get(&format!("/v2/comment/{}/reply", comment_id)).await.map_err(|e| e.to_string())?;
3225 let comments = resp.get("comments")
3226 .or_else(|| resp.get("replies"))
3227 .and_then(|c| c.as_array())
3228 .cloned()
3229 .unwrap_or_default();
3230 Ok(compact_items(&comments, &["id", "user", "date", "comment_text"]))
3231 }
3232
3233 "clickup_comment_reply" => {
3234 let comment_id = args.get("comment_id").and_then(|v| v.as_str())
3235 .ok_or("Missing required parameter: comment_id")?;
3236 let text = args.get("text").and_then(|v| v.as_str())
3237 .ok_or("Missing required parameter: text")?;
3238 let mut body = json!({"comment_text": text});
3239 if let Some(assignee) = args.get("assignee").and_then(|v| v.as_i64()) { body["assignee"] = json!(assignee); }
3240 let resp = client.post(&format!("/v2/comment/{}/reply", comment_id), &body).await.map_err(|e| e.to_string())?;
3241 Ok(json!({"message": "Reply posted", "id": resp.get("id")}))
3242 }
3243
3244 "clickup_chat_channel_list" => {
3245 let team_id = resolve_workspace(args)?;
3246 let mut path = format!("/v3/workspaces/{}/chat/channels", team_id);
3247 if let Some(include_closed) = args.get("include_closed").and_then(|v| v.as_bool()) {
3248 path.push_str(&format!("?include_closed={}", include_closed));
3249 }
3250 let resp = client.get(&path).await.map_err(|e| e.to_string())?;
3251 let channels = resp.get("channels").and_then(|c| c.as_array()).cloned().unwrap_or_default();
3252 Ok(compact_items(&channels, &["id", "name", "type"]))
3253 }
3254
3255 "clickup_chat_channel_followers" => {
3256 let team_id = resolve_workspace(args)?;
3257 let channel_id = args.get("channel_id").and_then(|v| v.as_str())
3258 .ok_or("Missing required parameter: channel_id")?;
3259 let resp = client.get(&format!("/v3/workspaces/{}/chat/channels/{}/followers", team_id, channel_id)).await.map_err(|e| e.to_string())?;
3260 Ok(resp)
3261 }
3262
3263 "clickup_chat_channel_members" => {
3264 let team_id = resolve_workspace(args)?;
3265 let channel_id = args.get("channel_id").and_then(|v| v.as_str())
3266 .ok_or("Missing required parameter: channel_id")?;
3267 let resp = client.get(&format!("/v3/workspaces/{}/chat/channels/{}/members", team_id, channel_id)).await.map_err(|e| e.to_string())?;
3268 Ok(resp)
3269 }
3270
3271 "clickup_chat_message_update" => {
3272 let team_id = resolve_workspace(args)?;
3273 let message_id = args.get("message_id").and_then(|v| v.as_str())
3274 .ok_or("Missing required parameter: message_id")?;
3275 let text = args.get("text").and_then(|v| v.as_str())
3276 .ok_or("Missing required parameter: text")?;
3277 let body = json!({"content": text});
3278 client.patch(&format!("/v3/workspaces/{}/chat/messages/{}", team_id, message_id), &body).await.map_err(|e| e.to_string())?;
3279 Ok(json!({"message": format!("Message {} updated", message_id)}))
3280 }
3281
3282 "clickup_chat_reaction_list" => {
3283 let team_id = resolve_workspace(args)?;
3284 let message_id = args.get("message_id").and_then(|v| v.as_str())
3285 .ok_or("Missing required parameter: message_id")?;
3286 let resp = client.get(&format!("/v3/workspaces/{}/chat/messages/{}/reactions", team_id, message_id)).await.map_err(|e| e.to_string())?;
3287 Ok(resp)
3288 }
3289
3290 "clickup_chat_reaction_add" => {
3291 let team_id = resolve_workspace(args)?;
3292 let message_id = args.get("message_id").and_then(|v| v.as_str())
3293 .ok_or("Missing required parameter: message_id")?;
3294 let emoji = args.get("emoji").and_then(|v| v.as_str())
3295 .ok_or("Missing required parameter: emoji")?;
3296 let body = json!({"emoji": emoji});
3297 client.post(&format!("/v3/workspaces/{}/chat/messages/{}/reactions", team_id, message_id), &body).await.map_err(|e| e.to_string())?;
3298 Ok(json!({"message": format!("Reaction '{}' added to message {}", emoji, message_id)}))
3299 }
3300
3301 "clickup_chat_reaction_remove" => {
3302 let team_id = resolve_workspace(args)?;
3303 let message_id = args.get("message_id").and_then(|v| v.as_str())
3304 .ok_or("Missing required parameter: message_id")?;
3305 let emoji = args.get("emoji").and_then(|v| v.as_str())
3306 .ok_or("Missing required parameter: emoji")?;
3307 client.delete(&format!("/v3/workspaces/{}/chat/messages/{}/reactions/{}", team_id, message_id, emoji)).await.map_err(|e| e.to_string())?;
3308 Ok(json!({"message": format!("Reaction '{}' removed from message {}", emoji, message_id)}))
3309 }
3310
3311 "clickup_chat_reply_list" => {
3312 let team_id = resolve_workspace(args)?;
3313 let message_id = args.get("message_id").and_then(|v| v.as_str())
3314 .ok_or("Missing required parameter: message_id")?;
3315 let resp = client.get(&format!("/v3/workspaces/{}/chat/messages/{}/replies", team_id, message_id)).await.map_err(|e| e.to_string())?;
3316 Ok(resp)
3317 }
3318
3319 "clickup_chat_reply_send" => {
3320 let team_id = resolve_workspace(args)?;
3321 let message_id = args.get("message_id").and_then(|v| v.as_str())
3322 .ok_or("Missing required parameter: message_id")?;
3323 let text = args.get("text").and_then(|v| v.as_str())
3324 .ok_or("Missing required parameter: text")?;
3325 let body = json!({"content": text});
3326 let resp = client.post(&format!("/v3/workspaces/{}/chat/messages/{}/replies", team_id, message_id), &body).await.map_err(|e| e.to_string())?;
3327 Ok(json!({"message": "Reply sent", "id": resp.get("id")}))
3328 }
3329
3330 "clickup_chat_tagged_users" => {
3331 let team_id = resolve_workspace(args)?;
3332 let message_id = args.get("message_id").and_then(|v| v.as_str())
3333 .ok_or("Missing required parameter: message_id")?;
3334 let resp = client.get(&format!("/v3/workspaces/{}/chat/messages/{}/tagged_users", team_id, message_id)).await.map_err(|e| e.to_string())?;
3335 Ok(resp)
3336 }
3337
3338 "clickup_time_current" => {
3339 let team_id = resolve_workspace(args)?;
3340 let resp = client.get(&format!("/v2/team/{}/time_entries/current", team_id)).await.map_err(|e| e.to_string())?;
3341 let data = resp.get("data").cloned().unwrap_or(resp);
3342 Ok(compact_items(&[data], &["id", "task", "duration", "start", "billable"]))
3343 }
3344
3345 "clickup_time_tags" => {
3346 let team_id = resolve_workspace(args)?;
3347 let resp = client.get(&format!("/v2/team/{}/time_entries/tags", team_id)).await.map_err(|e| e.to_string())?;
3348 let tags = resp.get("data").and_then(|d| d.as_array()).cloned().unwrap_or_default();
3349 Ok(compact_items(&tags, &["name"]))
3350 }
3351
3352 "clickup_time_add_tags" => {
3353 let team_id = resolve_workspace(args)?;
3354 let entry_ids = args.get("entry_ids").and_then(|v| v.as_array())
3355 .ok_or("Missing required parameter: entry_ids")?;
3356 let tag_names = args.get("tag_names").and_then(|v| v.as_array())
3357 .ok_or("Missing required parameter: tag_names")?;
3358 let tags: Vec<Value> = tag_names.iter()
3359 .filter_map(|n| n.as_str())
3360 .map(|n| json!({"name": n}))
3361 .collect();
3362 let body = json!({"time_entry_ids": entry_ids, "tags": tags});
3363 client.post(&format!("/v2/team/{}/time_entries/tags", team_id), &body).await.map_err(|e| e.to_string())?;
3364 Ok(json!({"message": "Tags added to time entries"}))
3365 }
3366
3367 "clickup_time_remove_tags" => {
3368 let team_id = resolve_workspace(args)?;
3369 let entry_ids = args.get("entry_ids").and_then(|v| v.as_array())
3370 .ok_or("Missing required parameter: entry_ids")?;
3371 let tag_names = args.get("tag_names").and_then(|v| v.as_array())
3372 .ok_or("Missing required parameter: tag_names")?;
3373 let tags: Vec<Value> = tag_names.iter()
3374 .filter_map(|n| n.as_str())
3375 .map(|n| json!({"name": n}))
3376 .collect();
3377 let body = json!({"time_entry_ids": entry_ids, "tags": tags});
3378 client.delete_with_body(&format!("/v2/team/{}/time_entries/tags", team_id), &body).await.map_err(|e| e.to_string())?;
3379 Ok(json!({"message": "Tags removed from time entries"}))
3380 }
3381
3382 "clickup_time_rename_tag" => {
3383 let team_id = resolve_workspace(args)?;
3384 let name = args.get("name").and_then(|v| v.as_str())
3385 .ok_or("Missing required parameter: name")?;
3386 let new_name = args.get("new_name").and_then(|v| v.as_str())
3387 .ok_or("Missing required parameter: new_name")?;
3388 let body = json!({"name": name, "new_name": new_name});
3389 client.put(&format!("/v2/team/{}/time_entries/tags", team_id), &body).await.map_err(|e| e.to_string())?;
3390 Ok(json!({"message": format!("Tag '{}' renamed to '{}'", name, new_name)}))
3391 }
3392
3393 "clickup_time_history" => {
3394 let team_id = resolve_workspace(args)?;
3395 let timer_id = args.get("timer_id").and_then(|v| v.as_str())
3396 .ok_or("Missing required parameter: timer_id")?;
3397 let resp = client.get(&format!("/v2/team/{}/time_entries/{}/history", team_id, timer_id)).await.map_err(|e| e.to_string())?;
3398 Ok(resp)
3399 }
3400
3401 "clickup_guest_invite" => {
3402 let team_id = resolve_workspace(args)?;
3403 let email = args.get("email").and_then(|v| v.as_str())
3404 .ok_or("Missing required parameter: email")?;
3405 let mut body = json!({"email": email});
3406 if let Some(v) = args.get("can_edit_tags").and_then(|v| v.as_bool()) { body["can_edit_tags"] = json!(v); }
3407 if let Some(v) = args.get("can_see_time_spent").and_then(|v| v.as_bool()) { body["can_see_time_spent"] = json!(v); }
3408 if let Some(v) = args.get("can_create_views").and_then(|v| v.as_bool()) { body["can_create_views"] = json!(v); }
3409 let resp = client.post(&format!("/v2/team/{}/guest", team_id), &body).await.map_err(|e| e.to_string())?;
3410 let guest = resp.get("guest").cloned().unwrap_or(resp);
3411 let user = guest.get("user").cloned().unwrap_or(guest);
3412 Ok(compact_items(&[user], &["id", "email"]))
3413 }
3414
3415 "clickup_guest_update" => {
3416 let team_id = resolve_workspace(args)?;
3417 let guest_id = args.get("guest_id").and_then(|v| v.as_i64())
3418 .ok_or("Missing required parameter: guest_id")?;
3419 let mut body = json!({});
3420 if let Some(v) = args.get("can_edit_tags").and_then(|v| v.as_bool()) { body["can_edit_tags"] = json!(v); }
3421 if let Some(v) = args.get("can_see_time_spent").and_then(|v| v.as_bool()) { body["can_see_time_spent"] = json!(v); }
3422 if let Some(v) = args.get("can_create_views").and_then(|v| v.as_bool()) { body["can_create_views"] = json!(v); }
3423 client.put(&format!("/v2/team/{}/guest/{}", team_id, guest_id), &body).await.map_err(|e| e.to_string())?;
3424 Ok(json!({"message": format!("Guest {} updated", guest_id)}))
3425 }
3426
3427 "clickup_guest_remove" => {
3428 let team_id = resolve_workspace(args)?;
3429 let guest_id = args.get("guest_id").and_then(|v| v.as_i64())
3430 .ok_or("Missing required parameter: guest_id")?;
3431 client.delete(&format!("/v2/team/{}/guest/{}", team_id, guest_id)).await.map_err(|e| e.to_string())?;
3432 Ok(json!({"message": format!("Guest {} removed", guest_id)}))
3433 }
3434
3435 "clickup_guest_share_task" => {
3436 let task_id = args.get("task_id").and_then(|v| v.as_str())
3437 .ok_or("Missing required parameter: task_id")?;
3438 let guest_id = args.get("guest_id").and_then(|v| v.as_i64())
3439 .ok_or("Missing required parameter: guest_id")?;
3440 let permission = args.get("permission").and_then(|v| v.as_str())
3441 .ok_or("Missing required parameter: permission")?;
3442 let body = json!({"permission_level": permission});
3443 client.post(&format!("/v2/task/{}/guest/{}", task_id, guest_id), &body).await.map_err(|e| e.to_string())?;
3444 Ok(json!({"message": format!("Task {} shared with guest {}", task_id, guest_id)}))
3445 }
3446
3447 "clickup_guest_unshare_task" => {
3448 let task_id = args.get("task_id").and_then(|v| v.as_str())
3449 .ok_or("Missing required parameter: task_id")?;
3450 let guest_id = args.get("guest_id").and_then(|v| v.as_i64())
3451 .ok_or("Missing required parameter: guest_id")?;
3452 client.delete(&format!("/v2/task/{}/guest/{}", task_id, guest_id)).await.map_err(|e| e.to_string())?;
3453 Ok(json!({"message": format!("Guest {} unshared from task {}", guest_id, task_id)}))
3454 }
3455
3456 "clickup_guest_share_list" => {
3457 let list_id = args.get("list_id").and_then(|v| v.as_str())
3458 .ok_or("Missing required parameter: list_id")?;
3459 let guest_id = args.get("guest_id").and_then(|v| v.as_i64())
3460 .ok_or("Missing required parameter: guest_id")?;
3461 let permission = args.get("permission").and_then(|v| v.as_str())
3462 .ok_or("Missing required parameter: permission")?;
3463 let body = json!({"permission_level": permission});
3464 client.post(&format!("/v2/list/{}/guest/{}", list_id, guest_id), &body).await.map_err(|e| e.to_string())?;
3465 Ok(json!({"message": format!("List {} shared with guest {}", list_id, guest_id)}))
3466 }
3467
3468 "clickup_guest_unshare_list" => {
3469 let list_id = args.get("list_id").and_then(|v| v.as_str())
3470 .ok_or("Missing required parameter: list_id")?;
3471 let guest_id = args.get("guest_id").and_then(|v| v.as_i64())
3472 .ok_or("Missing required parameter: guest_id")?;
3473 client.delete(&format!("/v2/list/{}/guest/{}", list_id, guest_id)).await.map_err(|e| e.to_string())?;
3474 Ok(json!({"message": format!("Guest {} unshared from list {}", guest_id, list_id)}))
3475 }
3476
3477 "clickup_guest_share_folder" => {
3478 let folder_id = args.get("folder_id").and_then(|v| v.as_str())
3479 .ok_or("Missing required parameter: folder_id")?;
3480 let guest_id = args.get("guest_id").and_then(|v| v.as_i64())
3481 .ok_or("Missing required parameter: guest_id")?;
3482 let permission = args.get("permission").and_then(|v| v.as_str())
3483 .ok_or("Missing required parameter: permission")?;
3484 let body = json!({"permission_level": permission});
3485 client.post(&format!("/v2/folder/{}/guest/{}", folder_id, guest_id), &body).await.map_err(|e| e.to_string())?;
3486 Ok(json!({"message": format!("Folder {} shared with guest {}", folder_id, guest_id)}))
3487 }
3488
3489 "clickup_guest_unshare_folder" => {
3490 let folder_id = args.get("folder_id").and_then(|v| v.as_str())
3491 .ok_or("Missing required parameter: folder_id")?;
3492 let guest_id = args.get("guest_id").and_then(|v| v.as_i64())
3493 .ok_or("Missing required parameter: guest_id")?;
3494 client.delete(&format!("/v2/folder/{}/guest/{}", folder_id, guest_id)).await.map_err(|e| e.to_string())?;
3495 Ok(json!({"message": format!("Guest {} unshared from folder {}", guest_id, folder_id)}))
3496 }
3497
3498 "clickup_user_invite" => {
3499 let team_id = resolve_workspace(args)?;
3500 let email = args.get("email").and_then(|v| v.as_str())
3501 .ok_or("Missing required parameter: email")?;
3502 let mut body = json!({"email": email});
3503 if let Some(admin) = args.get("admin").and_then(|v| v.as_bool()) { body["admin"] = json!(admin); }
3504 let resp = client.post(&format!("/v2/team/{}/user", team_id), &body).await.map_err(|e| e.to_string())?;
3505 let member = resp.get("member").cloned().unwrap_or(resp);
3506 let user = member.get("user").cloned().unwrap_or(member);
3507 Ok(compact_items(&[user], &["id", "username", "email"]))
3508 }
3509
3510 "clickup_user_update" => {
3511 let team_id = resolve_workspace(args)?;
3512 let user_id = args.get("user_id").and_then(|v| v.as_i64())
3513 .ok_or("Missing required parameter: user_id")?;
3514 let mut body = json!({});
3515 if let Some(username) = args.get("username").and_then(|v| v.as_str()) { body["username"] = json!(username); }
3516 if let Some(admin) = args.get("admin").and_then(|v| v.as_bool()) { body["admin"] = json!(admin); }
3517 client.put(&format!("/v2/team/{}/user/{}", team_id, user_id), &body).await.map_err(|e| e.to_string())?;
3518 Ok(json!({"message": format!("User {} updated", user_id)}))
3519 }
3520
3521 "clickup_user_remove" => {
3522 let team_id = resolve_workspace(args)?;
3523 let user_id = args.get("user_id").and_then(|v| v.as_i64())
3524 .ok_or("Missing required parameter: user_id")?;
3525 client.delete(&format!("/v2/team/{}/user/{}", team_id, user_id)).await.map_err(|e| e.to_string())?;
3526 Ok(json!({"message": format!("User {} removed from workspace", user_id)}))
3527 }
3528
3529 "clickup_template_apply_task" => {
3530 let list_id = args.get("list_id").and_then(|v| v.as_str())
3531 .ok_or("Missing required parameter: list_id")?;
3532 let template_id = args.get("template_id").and_then(|v| v.as_str())
3533 .ok_or("Missing required parameter: template_id")?;
3534 let name = args.get("name").and_then(|v| v.as_str())
3535 .ok_or("Missing required parameter: name")?;
3536 let body = json!({"name": name});
3537 let resp = client.post(&format!("/v2/list/{}/taskTemplate/{}", list_id, template_id), &body).await.map_err(|e| e.to_string())?;
3538 Ok(compact_items(&[resp], &["id", "name"]))
3539 }
3540
3541 "clickup_template_apply_list" => {
3542 let template_id = args.get("template_id").and_then(|v| v.as_str())
3543 .ok_or("Missing required parameter: template_id")?;
3544 let name = args.get("name").and_then(|v| v.as_str())
3545 .ok_or("Missing required parameter: name")?;
3546 let body = json!({"name": name});
3547 let path = if let Some(folder_id) = args.get("folder_id").and_then(|v| v.as_str()) {
3548 format!("/v2/folder/{}/list_template/{}", folder_id, template_id)
3549 } else if let Some(space_id) = args.get("space_id").and_then(|v| v.as_str()) {
3550 format!("/v2/space/{}/list_template/{}", space_id, template_id)
3551 } else {
3552 return Err("Provide either folder_id or space_id".to_string());
3553 };
3554 client.post(&path, &body).await.map_err(|e| e.to_string())?;
3555 Ok(json!({"message": format!("List '{}' created from template {}", name, template_id)}))
3556 }
3557
3558 "clickup_template_apply_folder" => {
3559 let space_id = args.get("space_id").and_then(|v| v.as_str())
3560 .ok_or("Missing required parameter: space_id")?;
3561 let template_id = args.get("template_id").and_then(|v| v.as_str())
3562 .ok_or("Missing required parameter: template_id")?;
3563 let name = args.get("name").and_then(|v| v.as_str())
3564 .ok_or("Missing required parameter: name")?;
3565 let body = json!({"name": name});
3566 client.post(&format!("/v2/space/{}/folder_template/{}", space_id, template_id), &body).await.map_err(|e| e.to_string())?;
3567 Ok(json!({"message": format!("Folder '{}' created from template {}", name, template_id)}))
3568 }
3569
3570 "clickup_attachment_upload" => {
3571 let task_id = args.get("task_id").and_then(|v| v.as_str())
3572 .ok_or("Missing required parameter: task_id")?;
3573 let file_path = args.get("file_path").and_then(|v| v.as_str())
3574 .ok_or("Missing required parameter: file_path")?;
3575 let path = format!("/v2/task/{}/attachment", task_id);
3576 let resp = client.upload_file(&path, std::path::Path::new(file_path)).await.map_err(|e| e.to_string())?;
3577 Ok(json!({"message": "File uploaded", "id": resp.get("id"), "url": resp.get("url")}))
3578 }
3579
3580 "clickup_task_type_list" => {
3581 let team_id = resolve_workspace(args)?;
3582 let resp = client.get(&format!("/v2/team/{}/custom_item", team_id)).await.map_err(|e| e.to_string())?;
3583 let items = resp.get("custom_items").and_then(|i| i.as_array()).cloned().unwrap_or_default();
3584 Ok(compact_items(&items, &["id", "name", "name_plural"]))
3585 }
3586
3587 "clickup_doc_get_page" => {
3588 let team_id = resolve_workspace(args)?;
3589 let doc_id = args.get("doc_id").and_then(|v| v.as_str())
3590 .ok_or("Missing required parameter: doc_id")?;
3591 let page_id = args.get("page_id").and_then(|v| v.as_str())
3592 .ok_or("Missing required parameter: page_id")?;
3593 let resp = client.get(&format!("/v3/workspaces/{}/docs/{}/pages/{}", team_id, doc_id, page_id)).await.map_err(|e| e.to_string())?;
3594 Ok(resp)
3595 }
3596
3597 "clickup_audit_log_query" => {
3598 let team_id = resolve_workspace(args)?;
3599 let event_type = args.get("type").and_then(|v| v.as_str())
3600 .ok_or("Missing required parameter: type")?;
3601 let mut body = json!({"type": event_type});
3602 if let Some(user_id) = args.get("user_id").and_then(|v| v.as_i64()) {
3603 body["user_id"] = json!(user_id);
3604 }
3605 if let Some(start_date) = args.get("start_date").and_then(|v| v.as_i64()) {
3606 body["date_filter"] = json!({"start_date": start_date, "end_date": args.get("end_date").and_then(|v| v.as_i64()).unwrap_or(i64::MAX)});
3607 } else if let Some(end_date) = args.get("end_date").and_then(|v| v.as_i64()) {
3608 body["date_filter"] = json!({"end_date": end_date});
3609 }
3610 let resp = client.post(&format!("/v3/workspaces/{}/auditlogs", team_id), &body).await.map_err(|e| e.to_string())?;
3611 Ok(resp)
3612 }
3613
3614 "clickup_acl_update" => {
3615 let team_id = resolve_workspace(args)?;
3616 let object_type = args.get("object_type").and_then(|v| v.as_str())
3617 .ok_or("Missing required parameter: object_type")?;
3618 let object_id = args.get("object_id").and_then(|v| v.as_str())
3619 .ok_or("Missing required parameter: object_id")?;
3620 let mut body = json!({});
3621 if let Some(private) = args.get("private").and_then(|v| v.as_bool()) { body["private"] = json!(private); }
3622 client.patch(&format!("/v3/workspaces/{}/{}/{}/acls", team_id, object_type, object_id), &body).await.map_err(|e| e.to_string())?;
3623 Ok(json!({"message": format!("ACL updated for {} {}", object_type, object_id)}))
3624 }
3625
3626 unknown => Err(format!("Unknown tool: {}", unknown)),
3627 }
3628}
3629
3630pub async fn serve() -> Result<(), Box<dyn std::error::Error>> {
3633 let config = Config::load().map_err(|e| format!("Failed to load config: {}", e))?;
3635 let token = config.auth.token.clone();
3636 if token.is_empty() {
3637 return Err("No API token configured. Run `clickup setup` first.".into());
3638 }
3639 let workspace_id = config.defaults.workspace_id.clone();
3640
3641 let client = ClickUpClient::new(&token, 30)
3642 .map_err(|e| format!("Failed to create HTTP client: {}", e))?;
3643
3644 let stdin = tokio::io::stdin();
3645 let reader = BufReader::new(stdin);
3646 let mut lines = reader.lines();
3647
3648 while let Some(line) = lines.next_line().await? {
3649 let line = line.trim().to_string();
3650 if line.is_empty() {
3651 continue;
3652 }
3653
3654 let msg: Value = match serde_json::from_str(&line) {
3655 Ok(v) => v,
3656 Err(e) => {
3657 let resp = error_response(&Value::Null, -32700, &format!("Parse error: {}", e));
3659 println!("{}", resp);
3660 continue;
3661 }
3662 };
3663
3664 let id = msg.get("id").cloned().unwrap_or(Value::Null);
3666 let method = msg.get("method").and_then(|v| v.as_str()).unwrap_or("");
3667
3668 if id.is_null() && method.starts_with("notifications/") {
3669 continue;
3671 }
3672
3673 let resp = match method {
3674 "initialize" => {
3675 let version = msg
3676 .get("params")
3677 .and_then(|p| p.get("protocolVersion"))
3678 .and_then(|v| v.as_str())
3679 .unwrap_or("2024-11-05");
3680 ok_response(
3681 &id,
3682 json!({
3683 "protocolVersion": version,
3684 "capabilities": {"tools": {}},
3685 "serverInfo": {
3686 "name": "clickup-cli",
3687 "version": env!("CARGO_PKG_VERSION")
3688 }
3689 }),
3690 )
3691 }
3692
3693 "tools/list" => ok_response(&id, json!({"tools": tool_list()})),
3694
3695 "tools/call" => {
3696 let params = msg.get("params").cloned().unwrap_or(json!({}));
3697 let tool_name = params
3698 .get("name")
3699 .and_then(|v| v.as_str())
3700 .unwrap_or("");
3701 let arguments = params.get("arguments").cloned().unwrap_or(json!({}));
3702
3703 if tool_name.is_empty() {
3704 let result = tool_error("Missing tool name".to_string());
3705 ok_response(&id, result)
3706 } else {
3707 let result = call_tool(tool_name, &arguments, &client, &workspace_id).await;
3708 ok_response(&id, result)
3709 }
3710 }
3711
3712 other => {
3713 eprintln!("Unknown method: {}", other);
3715 error_response(&id, -32601, &format!("Method not found: {}", other))
3716 }
3717 };
3718
3719 println!("{}", resp);
3720 }
3721
3722 Ok(())
3723}