Skip to main content

clickup_cli/
mcp.rs

1use crate::client::ClickUpClient;
2use crate::config::Config;
3use crate::output::compact_items;
4use serde_json::{json, Value};
5use tokio::io::{AsyncBufReadExt, BufReader};
6
7// ── JSON-RPC helpers ──────────────────────────────────────────────────────────
8
9fn 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
25// ── Tool definitions ──────────────────────────────────────────────────────────
26
27fn 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
1927// ── Tool execution ────────────────────────────────────────────────────────────
1928
1929async 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    // Resolve workspace ID from args or config
1952    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
3630// ── Main server loop ──────────────────────────────────────────────────────────
3631
3632pub async fn serve() -> Result<(), Box<dyn std::error::Error>> {
3633    // Load config at startup
3634    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                // Parse error — send error response with null id
3658                let resp = error_response(&Value::Null, -32700, &format!("Parse error: {}", e));
3659                println!("{}", resp);
3660                continue;
3661            }
3662        };
3663
3664        // Notifications have no id — don't respond
3665        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            // Notification — no response needed
3670            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                // Unknown method
3714                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}