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}
1354
1355// ── Tool execution ────────────────────────────────────────────────────────────
1356
1357async fn call_tool(
1358    name: &str,
1359    args: &Value,
1360    client: &ClickUpClient,
1361    workspace_id: &Option<String>,
1362) -> Value {
1363    let result = dispatch_tool(name, args, client, workspace_id).await;
1364    match result {
1365        Ok(v) => tool_result(v.to_string()),
1366        Err(e) => tool_error(format!("Error: {}", e)),
1367    }
1368}
1369
1370async fn dispatch_tool(
1371    name: &str,
1372    args: &Value,
1373    client: &ClickUpClient,
1374    workspace_id: &Option<String>,
1375) -> Result<Value, String> {
1376    let empty = json!({});
1377    let args = if args.is_null() { &empty } else { args };
1378
1379    // Resolve workspace ID from args or config
1380    let resolve_workspace = |args: &Value| -> Result<String, String> {
1381        if let Some(id) = args.get("team_id").and_then(|v| v.as_str()) {
1382            return Ok(id.to_string());
1383        }
1384        workspace_id
1385            .clone()
1386            .ok_or_else(|| "No workspace_id found in config. Please run `clickup setup` or provide team_id in the tool arguments.".to_string())
1387    };
1388
1389    match name {
1390        "clickup_whoami" => {
1391            let resp = client.get("/v2/user").await.map_err(|e| e.to_string())?;
1392            let user = resp.get("user").cloned().unwrap_or(resp);
1393            Ok(compact_items(&[user], &["id", "username", "email"]))
1394        }
1395
1396        "clickup_workspace_list" => {
1397            let resp = client.get("/v2/team").await.map_err(|e| e.to_string())?;
1398            let teams = resp.get("teams").and_then(|t| t.as_array()).cloned().unwrap_or_default();
1399            let items: Vec<Value> = teams.iter().map(|ws| {
1400                json!({
1401                    "id": ws.get("id"),
1402                    "name": ws.get("name"),
1403                    "members": ws.get("members").and_then(|m| m.as_array()).map(|a| a.len()).unwrap_or(0),
1404                })
1405            }).collect();
1406            Ok(compact_items(&items, &["id", "name", "members"]))
1407        }
1408
1409        "clickup_space_list" => {
1410            let team_id = resolve_workspace(args)?;
1411            let archived = args.get("archived").and_then(|v| v.as_bool()).unwrap_or(false);
1412            let path = format!("/v2/team/{}/space?archived={}", team_id, archived);
1413            let resp = client.get(&path).await.map_err(|e| e.to_string())?;
1414            let spaces = resp.get("spaces").and_then(|s| s.as_array()).cloned().unwrap_or_default();
1415            Ok(compact_items(&spaces, &["id", "name", "private", "archived"]))
1416        }
1417
1418        "clickup_folder_list" => {
1419            let space_id = args
1420                .get("space_id")
1421                .and_then(|v| v.as_str())
1422                .ok_or("Missing required parameter: space_id")?;
1423            let archived = args.get("archived").and_then(|v| v.as_bool()).unwrap_or(false);
1424            let path = format!("/v2/space/{}/folder?archived={}", space_id, archived);
1425            let resp = client.get(&path).await.map_err(|e| e.to_string())?;
1426            let folders = resp.get("folders").and_then(|f| f.as_array()).cloned().unwrap_or_default();
1427            let items: Vec<Value> = folders.iter().map(|f| {
1428                let list_count = f.get("lists").and_then(|l| l.as_array()).map(|a| a.len()).unwrap_or(0);
1429                json!({
1430                    "id": f.get("id"),
1431                    "name": f.get("name"),
1432                    "task_count": f.get("task_count"),
1433                    "list_count": list_count,
1434                })
1435            }).collect();
1436            Ok(compact_items(&items, &["id", "name", "task_count", "list_count"]))
1437        }
1438
1439        "clickup_list_list" => {
1440            let archived = args.get("archived").and_then(|v| v.as_bool()).unwrap_or(false);
1441            let path = if let Some(folder_id) = args.get("folder_id").and_then(|v| v.as_str()) {
1442                format!("/v2/folder/{}/list?archived={}", folder_id, archived)
1443            } else if let Some(space_id) = args.get("space_id").and_then(|v| v.as_str()) {
1444                format!("/v2/space/{}/list?archived={}", space_id, archived)
1445            } else {
1446                return Err("Provide either folder_id or space_id".to_string());
1447            };
1448            let resp = client.get(&path).await.map_err(|e| e.to_string())?;
1449            let lists = resp.get("lists").and_then(|l| l.as_array()).cloned().unwrap_or_default();
1450            Ok(compact_items(&lists, &["id", "name", "task_count", "status", "due_date"]))
1451        }
1452
1453        "clickup_task_list" => {
1454            let list_id = args
1455                .get("list_id")
1456                .and_then(|v| v.as_str())
1457                .ok_or("Missing required parameter: list_id")?;
1458            let mut qs = String::new();
1459            if let Some(include_closed) = args.get("include_closed").and_then(|v| v.as_bool()) {
1460                qs.push_str(&format!("&include_closed={}", include_closed));
1461            }
1462            if let Some(statuses) = args.get("statuses").and_then(|v| v.as_array()) {
1463                for s in statuses {
1464                    if let Some(s) = s.as_str() {
1465                        qs.push_str(&format!("&statuses[]={}", s));
1466                    }
1467                }
1468            }
1469            if let Some(assignees) = args.get("assignees").and_then(|v| v.as_array()) {
1470                for a in assignees {
1471                    if let Some(a) = a.as_str() {
1472                        qs.push_str(&format!("&assignees[]={}", a));
1473                    }
1474                }
1475            }
1476            let path = format!("/v2/list/{}/task?{}", list_id, qs.trim_start_matches('&'));
1477            let resp = client.get(&path).await.map_err(|e| e.to_string())?;
1478            let tasks = resp.get("tasks").and_then(|t| t.as_array()).cloned().unwrap_or_default();
1479            Ok(compact_items(&tasks, &["id", "name", "status", "priority", "assignees", "due_date"]))
1480        }
1481
1482        "clickup_task_get" => {
1483            let task_id = args
1484                .get("task_id")
1485                .and_then(|v| v.as_str())
1486                .ok_or("Missing required parameter: task_id")?;
1487            let include_subtasks = args
1488                .get("include_subtasks")
1489                .and_then(|v| v.as_bool())
1490                .unwrap_or(false);
1491            let path = format!(
1492                "/v2/task/{}?include_subtasks={}",
1493                task_id, include_subtasks
1494            );
1495            let resp = client.get(&path).await.map_err(|e| e.to_string())?;
1496            Ok(compact_items(&[resp], &["id", "name", "status", "priority", "assignees", "due_date", "description"]))
1497        }
1498
1499        "clickup_task_create" => {
1500            let list_id = args
1501                .get("list_id")
1502                .and_then(|v| v.as_str())
1503                .ok_or("Missing required parameter: list_id")?;
1504            let name = args
1505                .get("name")
1506                .and_then(|v| v.as_str())
1507                .ok_or("Missing required parameter: name")?;
1508            let mut body = json!({"name": name});
1509            if let Some(desc) = args.get("description").and_then(|v| v.as_str()) {
1510                body["description"] = json!(desc);
1511            }
1512            if let Some(status) = args.get("status").and_then(|v| v.as_str()) {
1513                body["status"] = json!(status);
1514            }
1515            if let Some(priority) = args.get("priority").and_then(|v| v.as_i64()) {
1516                body["priority"] = json!(priority);
1517            }
1518            if let Some(assignees) = args.get("assignees") {
1519                body["assignees"] = assignees.clone();
1520            }
1521            if let Some(tags) = args.get("tags") {
1522                body["tags"] = tags.clone();
1523            }
1524            if let Some(due_date) = args.get("due_date").and_then(|v| v.as_i64()) {
1525                body["due_date"] = json!(due_date);
1526            }
1527            let path = format!("/v2/list/{}/task", list_id);
1528            let resp = client.post(&path, &body).await.map_err(|e| e.to_string())?;
1529            Ok(compact_items(&[resp], &["id", "name", "status", "priority", "assignees", "due_date"]))
1530        }
1531
1532        "clickup_task_update" => {
1533            let task_id = args
1534                .get("task_id")
1535                .and_then(|v| v.as_str())
1536                .ok_or("Missing required parameter: task_id")?;
1537            let mut body = json!({});
1538            if let Some(name) = args.get("name").and_then(|v| v.as_str()) {
1539                body["name"] = json!(name);
1540            }
1541            if let Some(status) = args.get("status").and_then(|v| v.as_str()) {
1542                body["status"] = json!(status);
1543            }
1544            if let Some(priority) = args.get("priority").and_then(|v| v.as_i64()) {
1545                body["priority"] = json!(priority);
1546            }
1547            if let Some(desc) = args.get("description").and_then(|v| v.as_str()) {
1548                body["description"] = json!(desc);
1549            }
1550            if let Some(add) = args.get("add_assignees") {
1551                body["assignees"] = json!({"add": add, "rem": args.get("rem_assignees").cloned().unwrap_or(json!([]))});
1552            } else if let Some(rem) = args.get("rem_assignees") {
1553                body["assignees"] = json!({"add": [], "rem": rem});
1554            }
1555            let path = format!("/v2/task/{}", task_id);
1556            let resp = client.put(&path, &body).await.map_err(|e| e.to_string())?;
1557            Ok(compact_items(&[resp], &["id", "name", "status", "priority", "assignees", "due_date"]))
1558        }
1559
1560        "clickup_task_delete" => {
1561            let task_id = args
1562                .get("task_id")
1563                .and_then(|v| v.as_str())
1564                .ok_or("Missing required parameter: task_id")?;
1565            let path = format!("/v2/task/{}", task_id);
1566            client.delete(&path).await.map_err(|e| e.to_string())?;
1567            Ok(json!({"message": format!("Task {} deleted", task_id)}))
1568        }
1569
1570        "clickup_task_search" => {
1571            let team_id = resolve_workspace(args)?;
1572            let mut qs = String::new();
1573            if let Some(space_ids) = args.get("space_ids").and_then(|v| v.as_array()) {
1574                for id in space_ids {
1575                    if let Some(id) = id.as_str() {
1576                        qs.push_str(&format!("&space_ids[]={}", id));
1577                    }
1578                }
1579            }
1580            if let Some(list_ids) = args.get("list_ids").and_then(|v| v.as_array()) {
1581                for id in list_ids {
1582                    if let Some(id) = id.as_str() {
1583                        qs.push_str(&format!("&list_ids[]={}", id));
1584                    }
1585                }
1586            }
1587            if let Some(statuses) = args.get("statuses").and_then(|v| v.as_array()) {
1588                for s in statuses {
1589                    if let Some(s) = s.as_str() {
1590                        qs.push_str(&format!("&statuses[]={}", s));
1591                    }
1592                }
1593            }
1594            if let Some(assignees) = args.get("assignees").and_then(|v| v.as_array()) {
1595                for a in assignees {
1596                    if let Some(a) = a.as_str() {
1597                        qs.push_str(&format!("&assignees[]={}", a));
1598                    }
1599                }
1600            }
1601            let path = format!(
1602                "/v2/team/{}/task?{}",
1603                team_id,
1604                qs.trim_start_matches('&')
1605            );
1606            let resp = client.get(&path).await.map_err(|e| e.to_string())?;
1607            let tasks = resp.get("tasks").and_then(|t| t.as_array()).cloned().unwrap_or_default();
1608            Ok(compact_items(&tasks, &["id", "name", "status", "priority", "assignees", "due_date"]))
1609        }
1610
1611        "clickup_comment_list" => {
1612            let task_id = args
1613                .get("task_id")
1614                .and_then(|v| v.as_str())
1615                .ok_or("Missing required parameter: task_id")?;
1616            let path = format!("/v2/task/{}/comment", task_id);
1617            let resp = client.get(&path).await.map_err(|e| e.to_string())?;
1618            let comments = resp.get("comments").and_then(|c| c.as_array()).cloned().unwrap_or_default();
1619            Ok(compact_items(&comments, &["id", "user", "date", "comment_text"]))
1620        }
1621
1622        "clickup_comment_create" => {
1623            let task_id = args
1624                .get("task_id")
1625                .and_then(|v| v.as_str())
1626                .ok_or("Missing required parameter: task_id")?;
1627            let text = args
1628                .get("text")
1629                .and_then(|v| v.as_str())
1630                .ok_or("Missing required parameter: text")?;
1631            let mut body = json!({"comment_text": text});
1632            if let Some(assignee) = args.get("assignee").and_then(|v| v.as_i64()) {
1633                body["assignee"] = json!(assignee);
1634            }
1635            if let Some(notify_all) = args.get("notify_all").and_then(|v| v.as_bool()) {
1636                body["notify_all"] = json!(notify_all);
1637            }
1638            let path = format!("/v2/task/{}/comment", task_id);
1639            let resp = client.post(&path, &body).await.map_err(|e| e.to_string())?;
1640            Ok(json!({"message": "Comment created", "id": resp.get("id")}))
1641        }
1642
1643        "clickup_field_list" => {
1644            let list_id = args
1645                .get("list_id")
1646                .and_then(|v| v.as_str())
1647                .ok_or("Missing required parameter: list_id")?;
1648            let path = format!("/v2/list/{}/field", list_id);
1649            let resp = client.get(&path).await.map_err(|e| e.to_string())?;
1650            let fields = resp.get("fields").and_then(|f| f.as_array()).cloned().unwrap_or_default();
1651            Ok(compact_items(&fields, &["id", "name", "type", "required"]))
1652        }
1653
1654        "clickup_field_set" => {
1655            let task_id = args
1656                .get("task_id")
1657                .and_then(|v| v.as_str())
1658                .ok_or("Missing required parameter: task_id")?;
1659            let field_id = args
1660                .get("field_id")
1661                .and_then(|v| v.as_str())
1662                .ok_or("Missing required parameter: field_id")?;
1663            let value = args.get("value").ok_or("Missing required parameter: value")?;
1664            let body = json!({"value": value});
1665            let path = format!("/v2/task/{}/field/{}", task_id, field_id);
1666            client.post(&path, &body).await.map_err(|e| e.to_string())?;
1667            Ok(json!({"message": format!("Field {} set on task {}", field_id, task_id)}))
1668        }
1669
1670        "clickup_time_start" => {
1671            let team_id = resolve_workspace(args)?;
1672            let mut body = json!({});
1673            if let Some(task_id) = args.get("task_id").and_then(|v| v.as_str()) {
1674                body["tid"] = json!(task_id);
1675            }
1676            if let Some(desc) = args.get("description").and_then(|v| v.as_str()) {
1677                body["description"] = json!(desc);
1678            }
1679            if let Some(billable) = args.get("billable").and_then(|v| v.as_bool()) {
1680                body["billable"] = json!(billable);
1681            }
1682            let path = format!("/v2/team/{}/time_entries/start", team_id);
1683            let resp = client.post(&path, &body).await.map_err(|e| e.to_string())?;
1684            let data = resp.get("data").cloned().unwrap_or(resp);
1685            Ok(compact_items(&[data], &["id", "task", "duration", "start", "billable"]))
1686        }
1687
1688        "clickup_time_stop" => {
1689            let team_id = resolve_workspace(args)?;
1690            let path = format!("/v2/team/{}/time_entries/stop", team_id);
1691            let resp = client.post(&path, &json!({})).await.map_err(|e| e.to_string())?;
1692            let data = resp.get("data").cloned().unwrap_or(resp);
1693            Ok(compact_items(&[data], &["id", "task", "duration", "start", "end", "billable"]))
1694        }
1695
1696        "clickup_time_list" => {
1697            let team_id = resolve_workspace(args)?;
1698            let mut qs = String::new();
1699            if let Some(start_date) = args.get("start_date").and_then(|v| v.as_i64()) {
1700                qs.push_str(&format!("&start_date={}", start_date));
1701            }
1702            if let Some(end_date) = args.get("end_date").and_then(|v| v.as_i64()) {
1703                qs.push_str(&format!("&end_date={}", end_date));
1704            }
1705            if let Some(task_id) = args.get("task_id").and_then(|v| v.as_str()) {
1706                qs.push_str(&format!("&task_id={}", task_id));
1707            }
1708            let path = format!(
1709                "/v2/team/{}/time_entries?{}",
1710                team_id,
1711                qs.trim_start_matches('&')
1712            );
1713            let resp = client.get(&path).await.map_err(|e| e.to_string())?;
1714            let entries = resp.get("data").and_then(|d| d.as_array()).cloned().unwrap_or_default();
1715            Ok(compact_items(&entries, &["id", "task", "duration", "start", "billable"]))
1716        }
1717
1718        "clickup_checklist_create" => {
1719            let task_id = args
1720                .get("task_id")
1721                .and_then(|v| v.as_str())
1722                .ok_or("Missing required parameter: task_id")?;
1723            let name = args
1724                .get("name")
1725                .and_then(|v| v.as_str())
1726                .ok_or("Missing required parameter: name")?;
1727            let path = format!("/v2/task/{}/checklist", task_id);
1728            let body = json!({"name": name});
1729            let resp = client.post(&path, &body).await.map_err(|e| e.to_string())?;
1730            let checklist = resp.get("checklist").cloned().unwrap_or(resp);
1731            Ok(compact_items(&[checklist], &["id", "name"]))
1732        }
1733
1734        "clickup_checklist_delete" => {
1735            let checklist_id = args
1736                .get("checklist_id")
1737                .and_then(|v| v.as_str())
1738                .ok_or("Missing required parameter: checklist_id")?;
1739            let path = format!("/v2/checklist/{}", checklist_id);
1740            client.delete(&path).await.map_err(|e| e.to_string())?;
1741            Ok(json!({"message": format!("Checklist {} deleted", checklist_id)}))
1742        }
1743
1744        "clickup_goal_list" => {
1745            let team_id = resolve_workspace(args)?;
1746            let path = format!("/v2/team/{}/goal", team_id);
1747            let resp = client.get(&path).await.map_err(|e| e.to_string())?;
1748            let goals = resp.get("goals").and_then(|g| g.as_array()).cloned().unwrap_or_default();
1749            Ok(compact_items(&goals, &["id", "name", "percent_completed", "due_date"]))
1750        }
1751
1752        "clickup_goal_get" => {
1753            let goal_id = args
1754                .get("goal_id")
1755                .and_then(|v| v.as_str())
1756                .ok_or("Missing required parameter: goal_id")?;
1757            let path = format!("/v2/goal/{}", goal_id);
1758            let resp = client.get(&path).await.map_err(|e| e.to_string())?;
1759            let goal = resp.get("goal").cloned().unwrap_or(resp);
1760            Ok(compact_items(&[goal], &["id", "name", "percent_completed", "due_date", "description"]))
1761        }
1762
1763        "clickup_goal_create" => {
1764            let team_id = resolve_workspace(args)?;
1765            let name = args
1766                .get("name")
1767                .and_then(|v| v.as_str())
1768                .ok_or("Missing required parameter: name")?;
1769            let mut body = json!({"name": name});
1770            if let Some(due_date) = args.get("due_date").and_then(|v| v.as_i64()) {
1771                body["due_date"] = json!(due_date);
1772            }
1773            if let Some(desc) = args.get("description").and_then(|v| v.as_str()) {
1774                body["description"] = json!(desc);
1775            }
1776            if let Some(owner_ids) = args.get("owner_ids") {
1777                body["owners"] = owner_ids.clone();
1778            }
1779            let path = format!("/v2/team/{}/goal", team_id);
1780            let resp = client.post(&path, &body).await.map_err(|e| e.to_string())?;
1781            let goal = resp.get("goal").cloned().unwrap_or(resp);
1782            Ok(compact_items(&[goal], &["id", "name"]))
1783        }
1784
1785        "clickup_goal_update" => {
1786            let goal_id = args
1787                .get("goal_id")
1788                .and_then(|v| v.as_str())
1789                .ok_or("Missing required parameter: goal_id")?;
1790            let mut body = json!({});
1791            if let Some(name) = args.get("name").and_then(|v| v.as_str()) {
1792                body["name"] = json!(name);
1793            }
1794            if let Some(due_date) = args.get("due_date").and_then(|v| v.as_i64()) {
1795                body["due_date"] = json!(due_date);
1796            }
1797            if let Some(desc) = args.get("description").and_then(|v| v.as_str()) {
1798                body["description"] = json!(desc);
1799            }
1800            let path = format!("/v2/goal/{}", goal_id);
1801            let resp = client.put(&path, &body).await.map_err(|e| e.to_string())?;
1802            let goal = resp.get("goal").cloned().unwrap_or(resp);
1803            Ok(compact_items(&[goal], &["id", "name"]))
1804        }
1805
1806        "clickup_view_list" => {
1807            let path = if let Some(space_id) = args.get("space_id").and_then(|v| v.as_str()) {
1808                format!("/v2/space/{}/view", space_id)
1809            } else if let Some(folder_id) = args.get("folder_id").and_then(|v| v.as_str()) {
1810                format!("/v2/folder/{}/view", folder_id)
1811            } else if let Some(list_id) = args.get("list_id").and_then(|v| v.as_str()) {
1812                format!("/v2/list/{}/view", list_id)
1813            } else {
1814                let team_id = resolve_workspace(args)?;
1815                format!("/v2/team/{}/view", team_id)
1816            };
1817            let resp = client.get(&path).await.map_err(|e| e.to_string())?;
1818            let views = resp.get("views").and_then(|v| v.as_array()).cloned().unwrap_or_default();
1819            Ok(compact_items(&views, &["id", "name", "type"]))
1820        }
1821
1822        "clickup_view_tasks" => {
1823            let view_id = args
1824                .get("view_id")
1825                .and_then(|v| v.as_str())
1826                .ok_or("Missing required parameter: view_id")?;
1827            let page = args.get("page").and_then(|v| v.as_i64()).unwrap_or(0);
1828            let path = format!("/v2/view/{}/task?page={}", view_id, page);
1829            let resp = client.get(&path).await.map_err(|e| e.to_string())?;
1830            let tasks = resp.get("tasks").and_then(|t| t.as_array()).cloned().unwrap_or_default();
1831            Ok(compact_items(&tasks, &["id", "name", "status", "priority", "assignees", "due_date"]))
1832        }
1833
1834        "clickup_doc_list" => {
1835            let team_id = resolve_workspace(args)?;
1836            let path = format!("/v3/workspaces/{}/docs", team_id);
1837            let resp = client.get(&path).await.map_err(|e| e.to_string())?;
1838            let docs = resp.get("docs").and_then(|d| d.as_array()).cloned().unwrap_or_default();
1839            Ok(compact_items(&docs, &["id", "name", "date_created"]))
1840        }
1841
1842        "clickup_doc_get" => {
1843            let team_id = resolve_workspace(args)?;
1844            let doc_id = args
1845                .get("doc_id")
1846                .and_then(|v| v.as_str())
1847                .ok_or("Missing required parameter: doc_id")?;
1848            let path = format!("/v3/workspaces/{}/docs/{}", team_id, doc_id);
1849            let resp = client.get(&path).await.map_err(|e| e.to_string())?;
1850            Ok(compact_items(&[resp], &["id", "name", "date_created"]))
1851        }
1852
1853        "clickup_doc_pages" => {
1854            let team_id = resolve_workspace(args)?;
1855            let doc_id = args
1856                .get("doc_id")
1857                .and_then(|v| v.as_str())
1858                .ok_or("Missing required parameter: doc_id")?;
1859            let content = args.get("content").and_then(|v| v.as_bool()).unwrap_or(false);
1860            let path = format!("/v3/workspaces/{}/docs/{}/pages?content_format=text/md&max_page_depth=-1&include_content={}", team_id, doc_id, content);
1861            let resp = client.get(&path).await.map_err(|e| e.to_string())?;
1862            let pages = resp.get("pages").and_then(|p| p.as_array()).cloned().unwrap_or_default();
1863            Ok(compact_items(&pages, &["id", "name"]))
1864        }
1865
1866        "clickup_tag_list" => {
1867            let space_id = args
1868                .get("space_id")
1869                .and_then(|v| v.as_str())
1870                .ok_or("Missing required parameter: space_id")?;
1871            let path = format!("/v2/space/{}/tag", space_id);
1872            let resp = client.get(&path).await.map_err(|e| e.to_string())?;
1873            let tags = resp.get("tags").and_then(|t| t.as_array()).cloned().unwrap_or_default();
1874            Ok(compact_items(&tags, &["name", "tag_fg", "tag_bg"]))
1875        }
1876
1877        "clickup_task_add_tag" => {
1878            let task_id = args
1879                .get("task_id")
1880                .and_then(|v| v.as_str())
1881                .ok_or("Missing required parameter: task_id")?;
1882            let tag_name = args
1883                .get("tag_name")
1884                .and_then(|v| v.as_str())
1885                .ok_or("Missing required parameter: tag_name")?;
1886            let path = format!("/v2/task/{}/tag/{}", task_id, tag_name);
1887            client.post(&path, &json!({})).await.map_err(|e| e.to_string())?;
1888            Ok(json!({"message": format!("Tag '{}' added to task {}", tag_name, task_id)}))
1889        }
1890
1891        "clickup_task_remove_tag" => {
1892            let task_id = args
1893                .get("task_id")
1894                .and_then(|v| v.as_str())
1895                .ok_or("Missing required parameter: task_id")?;
1896            let tag_name = args
1897                .get("tag_name")
1898                .and_then(|v| v.as_str())
1899                .ok_or("Missing required parameter: tag_name")?;
1900            let path = format!("/v2/task/{}/tag/{}", task_id, tag_name);
1901            client.delete(&path).await.map_err(|e| e.to_string())?;
1902            Ok(json!({"message": format!("Tag '{}' removed from task {}", tag_name, task_id)}))
1903        }
1904
1905        "clickup_webhook_list" => {
1906            let team_id = resolve_workspace(args)?;
1907            let path = format!("/v2/team/{}/webhook", team_id);
1908            let resp = client.get(&path).await.map_err(|e| e.to_string())?;
1909            let webhooks = resp.get("webhooks").and_then(|w| w.as_array()).cloned().unwrap_or_default();
1910            Ok(compact_items(&webhooks, &["id", "endpoint", "events", "status"]))
1911        }
1912
1913        "clickup_member_list" => {
1914            let path = if let Some(task_id) = args.get("task_id").and_then(|v| v.as_str()) {
1915                format!("/v2/task/{}/member", task_id)
1916            } else if let Some(list_id) = args.get("list_id").and_then(|v| v.as_str()) {
1917                format!("/v2/list/{}/member", list_id)
1918            } else {
1919                return Err("Provide either task_id or list_id".to_string());
1920            };
1921            let resp = client.get(&path).await.map_err(|e| e.to_string())?;
1922            let members = resp.get("members").and_then(|m| m.as_array()).cloned().unwrap_or_default();
1923            Ok(compact_items(&members, &["id", "username", "email"]))
1924        }
1925
1926        "clickup_template_list" => {
1927            let team_id = resolve_workspace(args)?;
1928            let page = args.get("page").and_then(|v| v.as_i64()).unwrap_or(0);
1929            let path = format!("/v2/team/{}/taskTemplate?page={}", team_id, page);
1930            let resp = client.get(&path).await.map_err(|e| e.to_string())?;
1931            let templates = resp.get("templates").and_then(|t| t.as_array()).cloned().unwrap_or_default();
1932            Ok(compact_items(&templates, &["id", "name"]))
1933        }
1934
1935        "clickup_space_get" => {
1936            let space_id = args.get("space_id").and_then(|v| v.as_str())
1937                .ok_or("Missing required parameter: space_id")?;
1938            let resp = client.get(&format!("/v2/space/{}", space_id)).await.map_err(|e| e.to_string())?;
1939            Ok(compact_items(&[resp], &["id", "name", "private", "archived"]))
1940        }
1941
1942        "clickup_space_create" => {
1943            let team_id = resolve_workspace(args)?;
1944            let name = args.get("name").and_then(|v| v.as_str())
1945                .ok_or("Missing required parameter: name")?;
1946            let mut body = json!({"name": name});
1947            if let Some(private) = args.get("private").and_then(|v| v.as_bool()) {
1948                body["private"] = json!(private);
1949            }
1950            let resp = client.post(&format!("/v2/team/{}/space", team_id), &body).await.map_err(|e| e.to_string())?;
1951            Ok(compact_items(&[resp], &["id", "name", "private"]))
1952        }
1953
1954        "clickup_space_update" => {
1955            let space_id = args.get("space_id").and_then(|v| v.as_str())
1956                .ok_or("Missing required parameter: space_id")?;
1957            let mut body = json!({});
1958            if let Some(name) = args.get("name").and_then(|v| v.as_str()) { body["name"] = json!(name); }
1959            if let Some(private) = args.get("private").and_then(|v| v.as_bool()) { body["private"] = json!(private); }
1960            if let Some(archived) = args.get("archived").and_then(|v| v.as_bool()) { body["archived"] = json!(archived); }
1961            let resp = client.put(&format!("/v2/space/{}", space_id), &body).await.map_err(|e| e.to_string())?;
1962            Ok(compact_items(&[resp], &["id", "name", "private", "archived"]))
1963        }
1964
1965        "clickup_space_delete" => {
1966            let space_id = args.get("space_id").and_then(|v| v.as_str())
1967                .ok_or("Missing required parameter: space_id")?;
1968            client.delete(&format!("/v2/space/{}", space_id)).await.map_err(|e| e.to_string())?;
1969            Ok(json!({"message": format!("Space {} deleted", space_id)}))
1970        }
1971
1972        "clickup_folder_get" => {
1973            let folder_id = args.get("folder_id").and_then(|v| v.as_str())
1974                .ok_or("Missing required parameter: folder_id")?;
1975            let resp = client.get(&format!("/v2/folder/{}", folder_id)).await.map_err(|e| e.to_string())?;
1976            Ok(compact_items(&[resp], &["id", "name", "task_count"]))
1977        }
1978
1979        "clickup_folder_create" => {
1980            let space_id = args.get("space_id").and_then(|v| v.as_str())
1981                .ok_or("Missing required parameter: space_id")?;
1982            let name = args.get("name").and_then(|v| v.as_str())
1983                .ok_or("Missing required parameter: name")?;
1984            let body = json!({"name": name});
1985            let resp = client.post(&format!("/v2/space/{}/folder", space_id), &body).await.map_err(|e| e.to_string())?;
1986            Ok(compact_items(&[resp], &["id", "name"]))
1987        }
1988
1989        "clickup_folder_update" => {
1990            let folder_id = args.get("folder_id").and_then(|v| v.as_str())
1991                .ok_or("Missing required parameter: folder_id")?;
1992            let name = args.get("name").and_then(|v| v.as_str())
1993                .ok_or("Missing required parameter: name")?;
1994            let body = json!({"name": name});
1995            let resp = client.put(&format!("/v2/folder/{}", folder_id), &body).await.map_err(|e| e.to_string())?;
1996            Ok(compact_items(&[resp], &["id", "name"]))
1997        }
1998
1999        "clickup_folder_delete" => {
2000            let folder_id = args.get("folder_id").and_then(|v| v.as_str())
2001                .ok_or("Missing required parameter: folder_id")?;
2002            client.delete(&format!("/v2/folder/{}", folder_id)).await.map_err(|e| e.to_string())?;
2003            Ok(json!({"message": format!("Folder {} deleted", folder_id)}))
2004        }
2005
2006        "clickup_list_get" => {
2007            let list_id = args.get("list_id").and_then(|v| v.as_str())
2008                .ok_or("Missing required parameter: list_id")?;
2009            let resp = client.get(&format!("/v2/list/{}", list_id)).await.map_err(|e| e.to_string())?;
2010            Ok(compact_items(&[resp], &["id", "name", "task_count", "status", "due_date"]))
2011        }
2012
2013        "clickup_list_create" => {
2014            let name = args.get("name").and_then(|v| v.as_str())
2015                .ok_or("Missing required parameter: name")?;
2016            let mut body = json!({"name": name});
2017            if let Some(content) = args.get("content").and_then(|v| v.as_str()) { body["content"] = json!(content); }
2018            if let Some(due_date) = args.get("due_date").and_then(|v| v.as_i64()) { body["due_date"] = json!(due_date); }
2019            if let Some(status) = args.get("status").and_then(|v| v.as_str()) { body["status"] = json!(status); }
2020            let path = if let Some(folder_id) = args.get("folder_id").and_then(|v| v.as_str()) {
2021                format!("/v2/folder/{}/list", folder_id)
2022            } else if let Some(space_id) = args.get("space_id").and_then(|v| v.as_str()) {
2023                format!("/v2/space/{}/list", space_id)
2024            } else {
2025                return Err("Provide either folder_id or space_id".to_string());
2026            };
2027            let resp = client.post(&path, &body).await.map_err(|e| e.to_string())?;
2028            Ok(compact_items(&[resp], &["id", "name"]))
2029        }
2030
2031        "clickup_list_update" => {
2032            let list_id = args.get("list_id").and_then(|v| v.as_str())
2033                .ok_or("Missing required parameter: list_id")?;
2034            let mut body = json!({});
2035            if let Some(name) = args.get("name").and_then(|v| v.as_str()) { body["name"] = json!(name); }
2036            if let Some(content) = args.get("content").and_then(|v| v.as_str()) { body["content"] = json!(content); }
2037            if let Some(due_date) = args.get("due_date").and_then(|v| v.as_i64()) { body["due_date"] = json!(due_date); }
2038            if let Some(status) = args.get("status").and_then(|v| v.as_str()) { body["status"] = json!(status); }
2039            let resp = client.put(&format!("/v2/list/{}", list_id), &body).await.map_err(|e| e.to_string())?;
2040            Ok(compact_items(&[resp], &["id", "name", "task_count", "status"]))
2041        }
2042
2043        "clickup_list_delete" => {
2044            let list_id = args.get("list_id").and_then(|v| v.as_str())
2045                .ok_or("Missing required parameter: list_id")?;
2046            client.delete(&format!("/v2/list/{}", list_id)).await.map_err(|e| e.to_string())?;
2047            Ok(json!({"message": format!("List {} deleted", list_id)}))
2048        }
2049
2050        "clickup_list_add_task" => {
2051            let list_id = args.get("list_id").and_then(|v| v.as_str())
2052                .ok_or("Missing required parameter: list_id")?;
2053            let task_id = args.get("task_id").and_then(|v| v.as_str())
2054                .ok_or("Missing required parameter: task_id")?;
2055            client.post(&format!("/v2/list/{}/task/{}", list_id, task_id), &json!({})).await.map_err(|e| e.to_string())?;
2056            Ok(json!({"message": format!("Task {} added to list {}", task_id, list_id)}))
2057        }
2058
2059        "clickup_list_remove_task" => {
2060            let list_id = args.get("list_id").and_then(|v| v.as_str())
2061                .ok_or("Missing required parameter: list_id")?;
2062            let task_id = args.get("task_id").and_then(|v| v.as_str())
2063                .ok_or("Missing required parameter: task_id")?;
2064            client.delete(&format!("/v2/list/{}/task/{}", list_id, task_id)).await.map_err(|e| e.to_string())?;
2065            Ok(json!({"message": format!("Task {} removed from list {}", task_id, list_id)}))
2066        }
2067
2068        "clickup_comment_update" => {
2069            let comment_id = args.get("comment_id").and_then(|v| v.as_str())
2070                .ok_or("Missing required parameter: comment_id")?;
2071            let text = args.get("text").and_then(|v| v.as_str())
2072                .ok_or("Missing required parameter: text")?;
2073            let mut body = json!({"comment_text": text});
2074            if let Some(assignee) = args.get("assignee").and_then(|v| v.as_i64()) { body["assignee"] = json!(assignee); }
2075            if let Some(resolved) = args.get("resolved").and_then(|v| v.as_bool()) { body["resolved"] = json!(resolved); }
2076            client.put(&format!("/v2/comment/{}", comment_id), &body).await.map_err(|e| e.to_string())?;
2077            Ok(json!({"message": format!("Comment {} updated", comment_id)}))
2078        }
2079
2080        "clickup_comment_delete" => {
2081            let comment_id = args.get("comment_id").and_then(|v| v.as_str())
2082                .ok_or("Missing required parameter: comment_id")?;
2083            client.delete(&format!("/v2/comment/{}", comment_id)).await.map_err(|e| e.to_string())?;
2084            Ok(json!({"message": format!("Comment {} deleted", comment_id)}))
2085        }
2086
2087        "clickup_task_add_dep" => {
2088            let task_id = args.get("task_id").and_then(|v| v.as_str())
2089                .ok_or("Missing required parameter: task_id")?;
2090            let mut body = json!({});
2091            if let Some(dep) = args.get("depends_on").and_then(|v| v.as_str()) { body["depends_on"] = json!(dep); }
2092            if let Some(dep) = args.get("dependency_of").and_then(|v| v.as_str()) { body["dependency_of"] = json!(dep); }
2093            client.post(&format!("/v2/task/{}/dependency", task_id), &body).await.map_err(|e| e.to_string())?;
2094            Ok(json!({"message": format!("Dependency added to task {}", task_id)}))
2095        }
2096
2097        "clickup_task_remove_dep" => {
2098            let task_id = args.get("task_id").and_then(|v| v.as_str())
2099                .ok_or("Missing required parameter: task_id")?;
2100            let mut body = json!({});
2101            if let Some(dep) = args.get("depends_on").and_then(|v| v.as_str()) { body["depends_on"] = json!(dep); }
2102            if let Some(dep) = args.get("dependency_of").and_then(|v| v.as_str()) { body["dependency_of"] = json!(dep); }
2103            client.delete_with_body(&format!("/v2/task/{}/dependency", task_id), &body).await.map_err(|e| e.to_string())?;
2104            Ok(json!({"message": format!("Dependency removed from task {}", task_id)}))
2105        }
2106
2107        "clickup_task_link" => {
2108            let task_id = args.get("task_id").and_then(|v| v.as_str())
2109                .ok_or("Missing required parameter: task_id")?;
2110            let links_to = args.get("links_to").and_then(|v| v.as_str())
2111                .ok_or("Missing required parameter: links_to")?;
2112            let resp = client.post(&format!("/v2/task/{}/link/{}", task_id, links_to), &json!({})).await.map_err(|e| e.to_string())?;
2113            Ok(json!({"message": format!("Task {} linked to {}", task_id, links_to), "data": resp}))
2114        }
2115
2116        "clickup_task_unlink" => {
2117            let task_id = args.get("task_id").and_then(|v| v.as_str())
2118                .ok_or("Missing required parameter: task_id")?;
2119            let links_to = args.get("links_to").and_then(|v| v.as_str())
2120                .ok_or("Missing required parameter: links_to")?;
2121            client.delete(&format!("/v2/task/{}/link/{}", task_id, links_to)).await.map_err(|e| e.to_string())?;
2122            Ok(json!({"message": format!("Task {} unlinked from {}", task_id, links_to)}))
2123        }
2124
2125        "clickup_goal_delete" => {
2126            let goal_id = args.get("goal_id").and_then(|v| v.as_str())
2127                .ok_or("Missing required parameter: goal_id")?;
2128            client.delete(&format!("/v2/goal/{}", goal_id)).await.map_err(|e| e.to_string())?;
2129            Ok(json!({"message": format!("Goal {} deleted", goal_id)}))
2130        }
2131
2132        "clickup_goal_add_kr" => {
2133            let goal_id = args.get("goal_id").and_then(|v| v.as_str())
2134                .ok_or("Missing required parameter: goal_id")?;
2135            let name = args.get("name").and_then(|v| v.as_str())
2136                .ok_or("Missing required parameter: name")?;
2137            let kr_type = args.get("type").and_then(|v| v.as_str())
2138                .ok_or("Missing required parameter: type")?;
2139            let steps_start = args.get("steps_start").and_then(|v| v.as_f64())
2140                .ok_or("Missing required parameter: steps_start")?;
2141            let steps_end = args.get("steps_end").and_then(|v| v.as_f64())
2142                .ok_or("Missing required parameter: steps_end")?;
2143            let mut body = json!({"name": name, "type": kr_type, "steps_start": steps_start, "steps_end": steps_end});
2144            if let Some(unit) = args.get("unit").and_then(|v| v.as_str()) { body["unit"] = json!(unit); }
2145            if let Some(owners) = args.get("owner_ids") { body["owners"] = owners.clone(); }
2146            if let Some(task_ids) = args.get("task_ids") { body["task_ids"] = task_ids.clone(); }
2147            if let Some(list_ids) = args.get("list_ids") { body["list_ids"] = list_ids.clone(); }
2148            let resp = client.post(&format!("/v2/goal/{}/key_result", goal_id), &body).await.map_err(|e| e.to_string())?;
2149            let kr = resp.get("key_result").cloned().unwrap_or(resp);
2150            Ok(compact_items(&[kr], &["id", "name", "type", "steps_start", "steps_end", "steps_current"]))
2151        }
2152
2153        "clickup_goal_update_kr" => {
2154            let kr_id = args.get("kr_id").and_then(|v| v.as_str())
2155                .ok_or("Missing required parameter: kr_id")?;
2156            let mut body = json!({});
2157            if let Some(v) = args.get("steps_current").and_then(|v| v.as_f64()) { body["steps_current"] = json!(v); }
2158            if let Some(v) = args.get("name").and_then(|v| v.as_str()) { body["name"] = json!(v); }
2159            if let Some(v) = args.get("unit").and_then(|v| v.as_str()) { body["unit"] = json!(v); }
2160            let resp = client.put(&format!("/v2/key_result/{}", kr_id), &body).await.map_err(|e| e.to_string())?;
2161            let kr = resp.get("key_result").cloned().unwrap_or(resp);
2162            Ok(compact_items(&[kr], &["id", "name", "steps_current", "steps_end"]))
2163        }
2164
2165        "clickup_goal_delete_kr" => {
2166            let kr_id = args.get("kr_id").and_then(|v| v.as_str())
2167                .ok_or("Missing required parameter: kr_id")?;
2168            client.delete(&format!("/v2/key_result/{}", kr_id)).await.map_err(|e| e.to_string())?;
2169            Ok(json!({"message": format!("Key result {} deleted", kr_id)}))
2170        }
2171
2172        "clickup_time_get" => {
2173            let team_id = resolve_workspace(args)?;
2174            let timer_id = args.get("timer_id").and_then(|v| v.as_str())
2175                .ok_or("Missing required parameter: timer_id")?;
2176            let resp = client.get(&format!("/v2/team/{}/time_entries/{}", team_id, timer_id)).await.map_err(|e| e.to_string())?;
2177            let data = resp.get("data").cloned().unwrap_or(resp);
2178            Ok(compact_items(&[data], &["id", "task", "duration", "start", "end", "billable"]))
2179        }
2180
2181        "clickup_time_create" => {
2182            let team_id = resolve_workspace(args)?;
2183            let start = args.get("start").and_then(|v| v.as_i64())
2184                .ok_or("Missing required parameter: start")?;
2185            let duration = args.get("duration").and_then(|v| v.as_i64())
2186                .ok_or("Missing required parameter: duration")?;
2187            let mut body = json!({"start": start, "duration": duration});
2188            if let Some(task_id) = args.get("task_id").and_then(|v| v.as_str()) { body["tid"] = json!(task_id); }
2189            if let Some(desc) = args.get("description").and_then(|v| v.as_str()) { body["description"] = json!(desc); }
2190            if let Some(billable) = args.get("billable").and_then(|v| v.as_bool()) { body["billable"] = json!(billable); }
2191            let resp = client.post(&format!("/v2/team/{}/time_entries", team_id), &body).await.map_err(|e| e.to_string())?;
2192            let data = resp.get("data").cloned().unwrap_or(resp);
2193            Ok(compact_items(&[data], &["id", "task", "duration", "start", "billable"]))
2194        }
2195
2196        "clickup_time_update" => {
2197            let team_id = resolve_workspace(args)?;
2198            let timer_id = args.get("timer_id").and_then(|v| v.as_str())
2199                .ok_or("Missing required parameter: timer_id")?;
2200            let mut body = json!({});
2201            if let Some(start) = args.get("start").and_then(|v| v.as_i64()) { body["start"] = json!(start); }
2202            if let Some(duration) = args.get("duration").and_then(|v| v.as_i64()) { body["duration"] = json!(duration); }
2203            if let Some(desc) = args.get("description").and_then(|v| v.as_str()) { body["description"] = json!(desc); }
2204            if let Some(billable) = args.get("billable").and_then(|v| v.as_bool()) { body["billable"] = json!(billable); }
2205            let resp = client.put(&format!("/v2/team/{}/time_entries/{}", team_id, timer_id), &body).await.map_err(|e| e.to_string())?;
2206            let data = resp.get("data").cloned().unwrap_or(resp);
2207            Ok(compact_items(&[data], &["id", "task", "duration", "start", "billable"]))
2208        }
2209
2210        "clickup_time_delete" => {
2211            let team_id = resolve_workspace(args)?;
2212            let timer_id = args.get("timer_id").and_then(|v| v.as_str())
2213                .ok_or("Missing required parameter: timer_id")?;
2214            client.delete(&format!("/v2/team/{}/time_entries/{}", team_id, timer_id)).await.map_err(|e| e.to_string())?;
2215            Ok(json!({"message": format!("Time entry {} deleted", timer_id)}))
2216        }
2217
2218        "clickup_view_get" => {
2219            let view_id = args.get("view_id").and_then(|v| v.as_str())
2220                .ok_or("Missing required parameter: view_id")?;
2221            let resp = client.get(&format!("/v2/view/{}", view_id)).await.map_err(|e| e.to_string())?;
2222            let view = resp.get("view").cloned().unwrap_or(resp);
2223            Ok(compact_items(&[view], &["id", "name", "type"]))
2224        }
2225
2226        "clickup_view_create" => {
2227            let scope = args.get("scope").and_then(|v| v.as_str())
2228                .ok_or("Missing required parameter: scope")?;
2229            let scope_id = args.get("scope_id").and_then(|v| v.as_str())
2230                .ok_or("Missing required parameter: scope_id")?;
2231            let name = args.get("name").and_then(|v| v.as_str())
2232                .ok_or("Missing required parameter: name")?;
2233            let view_type = args.get("type").and_then(|v| v.as_str())
2234                .ok_or("Missing required parameter: type")?;
2235            let body = json!({"name": name, "type": view_type});
2236            let resp = client.post(&format!("/v2/{}/{}/view", scope, scope_id), &body).await.map_err(|e| e.to_string())?;
2237            let view = resp.get("view").cloned().unwrap_or(resp);
2238            Ok(compact_items(&[view], &["id", "name", "type"]))
2239        }
2240
2241        "clickup_view_update" => {
2242            let view_id = args.get("view_id").and_then(|v| v.as_str())
2243                .ok_or("Missing required parameter: view_id")?;
2244            let name = args.get("name").and_then(|v| v.as_str())
2245                .ok_or("Missing required parameter: name")?;
2246            let view_type = args.get("type").and_then(|v| v.as_str())
2247                .ok_or("Missing required parameter: type")?;
2248            let body = json!({"name": name, "type": view_type});
2249            let resp = client.put(&format!("/v2/view/{}", view_id), &body).await.map_err(|e| e.to_string())?;
2250            let view = resp.get("view").cloned().unwrap_or(resp);
2251            Ok(compact_items(&[view], &["id", "name", "type"]))
2252        }
2253
2254        "clickup_view_delete" => {
2255            let view_id = args.get("view_id").and_then(|v| v.as_str())
2256                .ok_or("Missing required parameter: view_id")?;
2257            client.delete(&format!("/v2/view/{}", view_id)).await.map_err(|e| e.to_string())?;
2258            Ok(json!({"message": format!("View {} deleted", view_id)}))
2259        }
2260
2261        "clickup_doc_create" => {
2262            let team_id = resolve_workspace(args)?;
2263            let name = args.get("name").and_then(|v| v.as_str())
2264                .ok_or("Missing required parameter: name")?;
2265            let mut body = json!({"name": name});
2266            if let Some(parent) = args.get("parent") { body["parent"] = parent.clone(); }
2267            let resp = client.post(&format!("/v3/workspaces/{}/docs", team_id), &body).await.map_err(|e| e.to_string())?;
2268            Ok(compact_items(&[resp], &["id", "name"]))
2269        }
2270
2271        "clickup_doc_add_page" => {
2272            let team_id = resolve_workspace(args)?;
2273            let doc_id = args.get("doc_id").and_then(|v| v.as_str())
2274                .ok_or("Missing required parameter: doc_id")?;
2275            let name = args.get("name").and_then(|v| v.as_str())
2276                .ok_or("Missing required parameter: name")?;
2277            let mut body = json!({"name": name});
2278            if let Some(content) = args.get("content").and_then(|v| v.as_str()) { body["content"] = json!(content); }
2279            if let Some(subtitle) = args.get("sub_title").and_then(|v| v.as_str()) { body["sub_title"] = json!(subtitle); }
2280            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); }
2281            let resp = client.post(&format!("/v3/workspaces/{}/docs/{}/pages", team_id, doc_id), &body).await.map_err(|e| e.to_string())?;
2282            Ok(compact_items(&[resp], &["id", "name"]))
2283        }
2284
2285        "clickup_doc_edit_page" => {
2286            let team_id = resolve_workspace(args)?;
2287            let doc_id = args.get("doc_id").and_then(|v| v.as_str())
2288                .ok_or("Missing required parameter: doc_id")?;
2289            let page_id = args.get("page_id").and_then(|v| v.as_str())
2290                .ok_or("Missing required parameter: page_id")?;
2291            let mut body = json!({});
2292            if let Some(name) = args.get("name").and_then(|v| v.as_str()) { body["name"] = json!(name); }
2293            if let Some(content) = args.get("content").and_then(|v| v.as_str()) { body["content"] = json!(content); }
2294            let resp = client.put(&format!("/v3/workspaces/{}/docs/{}/pages/{}", team_id, doc_id, page_id), &body).await.map_err(|e| e.to_string())?;
2295            Ok(compact_items(&[resp], &["id", "name"]))
2296        }
2297
2298        "clickup_chat_channel_create" => {
2299            let team_id = resolve_workspace(args)?;
2300            let name = args.get("name").and_then(|v| v.as_str())
2301                .ok_or("Missing required parameter: name")?;
2302            let mut body = json!({"name": name});
2303            if let Some(desc) = args.get("description").and_then(|v| v.as_str()) { body["description"] = json!(desc); }
2304            if let Some(vis) = args.get("visibility").and_then(|v| v.as_str()) { body["visibility"] = json!(vis); }
2305            let resp = client.post(&format!("/v3/workspaces/{}/chat/channels", team_id), &body).await.map_err(|e| e.to_string())?;
2306            Ok(compact_items(&[resp], &["id", "name", "visibility"]))
2307        }
2308
2309        "clickup_chat_channel_get" => {
2310            let team_id = resolve_workspace(args)?;
2311            let channel_id = args.get("channel_id").and_then(|v| v.as_str())
2312                .ok_or("Missing required parameter: channel_id")?;
2313            let resp = client.get(&format!("/v3/workspaces/{}/chat/channels/{}", team_id, channel_id)).await.map_err(|e| e.to_string())?;
2314            Ok(compact_items(&[resp], &["id", "name", "visibility"]))
2315        }
2316
2317        "clickup_chat_channel_update" => {
2318            let team_id = resolve_workspace(args)?;
2319            let channel_id = args.get("channel_id").and_then(|v| v.as_str())
2320                .ok_or("Missing required parameter: channel_id")?;
2321            let mut body = json!({});
2322            if let Some(name) = args.get("name").and_then(|v| v.as_str()) { body["name"] = json!(name); }
2323            if let Some(desc) = args.get("description").and_then(|v| v.as_str()) { body["description"] = json!(desc); }
2324            let resp = client.patch(&format!("/v3/workspaces/{}/chat/channels/{}", team_id, channel_id), &body).await.map_err(|e| e.to_string())?;
2325            Ok(compact_items(&[resp], &["id", "name"]))
2326        }
2327
2328        "clickup_chat_channel_delete" => {
2329            let team_id = resolve_workspace(args)?;
2330            let channel_id = args.get("channel_id").and_then(|v| v.as_str())
2331                .ok_or("Missing required parameter: channel_id")?;
2332            client.delete(&format!("/v3/workspaces/{}/chat/channels/{}", team_id, channel_id)).await.map_err(|e| e.to_string())?;
2333            Ok(json!({"message": format!("Channel {} deleted", channel_id)}))
2334        }
2335
2336        "clickup_chat_message_list" => {
2337            let team_id = resolve_workspace(args)?;
2338            let channel_id = args.get("channel_id").and_then(|v| v.as_str())
2339                .ok_or("Missing required parameter: channel_id")?;
2340            let mut path = format!("/v3/workspaces/{}/chat/channels/{}/messages", team_id, channel_id);
2341            if let Some(cursor) = args.get("cursor").and_then(|v| v.as_str()) {
2342                path.push_str(&format!("?cursor={}", cursor));
2343            }
2344            let resp = client.get(&path).await.map_err(|e| e.to_string())?;
2345            let messages = resp.get("messages").and_then(|m| m.as_array()).cloned().unwrap_or_default();
2346            Ok(compact_items(&messages, &["id", "content", "date"]))
2347        }
2348
2349        "clickup_chat_message_send" => {
2350            let team_id = resolve_workspace(args)?;
2351            let channel_id = args.get("channel_id").and_then(|v| v.as_str())
2352                .ok_or("Missing required parameter: channel_id")?;
2353            let content = args.get("content").and_then(|v| v.as_str())
2354                .ok_or("Missing required parameter: content")?;
2355            let body = json!({"content": content});
2356            let resp = client.post(&format!("/v3/workspaces/{}/chat/channels/{}/messages", team_id, channel_id), &body).await.map_err(|e| e.to_string())?;
2357            Ok(json!({"message": "Message sent", "id": resp.get("id")}))
2358        }
2359
2360        "clickup_chat_message_delete" => {
2361            let team_id = resolve_workspace(args)?;
2362            let message_id = args.get("message_id").and_then(|v| v.as_str())
2363                .ok_or("Missing required parameter: message_id")?;
2364            client.delete(&format!("/v3/workspaces/{}/chat/messages/{}", team_id, message_id)).await.map_err(|e| e.to_string())?;
2365            Ok(json!({"message": format!("Message {} deleted", message_id)}))
2366        }
2367
2368        "clickup_chat_dm" => {
2369            let team_id = resolve_workspace(args)?;
2370            let user_id = args.get("user_id").and_then(|v| v.as_i64())
2371                .ok_or("Missing required parameter: user_id")?;
2372            let content = args.get("content").and_then(|v| v.as_str())
2373                .ok_or("Missing required parameter: content")?;
2374            let body = json!({"user_id": user_id, "content": content});
2375            let resp = client.post(&format!("/v3/workspaces/{}/chat/channels/direct_message", team_id), &body).await.map_err(|e| e.to_string())?;
2376            Ok(json!({"message": "DM sent", "id": resp.get("id")}))
2377        }
2378
2379        "clickup_webhook_create" => {
2380            let team_id = resolve_workspace(args)?;
2381            let endpoint = args.get("endpoint").and_then(|v| v.as_str())
2382                .ok_or("Missing required parameter: endpoint")?;
2383            let events = args.get("events").ok_or("Missing required parameter: events")?;
2384            let mut body = json!({"endpoint": endpoint, "events": events});
2385            if let Some(space_id) = args.get("space_id").and_then(|v| v.as_str()) { body["space_id"] = json!(space_id); }
2386            if let Some(folder_id) = args.get("folder_id").and_then(|v| v.as_str()) { body["folder_id"] = json!(folder_id); }
2387            if let Some(list_id) = args.get("list_id").and_then(|v| v.as_str()) { body["list_id"] = json!(list_id); }
2388            if let Some(task_id) = args.get("task_id").and_then(|v| v.as_str()) { body["task_id"] = json!(task_id); }
2389            let resp = client.post(&format!("/v2/team/{}/webhook", team_id), &body).await.map_err(|e| e.to_string())?;
2390            let webhook = resp.get("webhook").cloned().unwrap_or(resp);
2391            Ok(compact_items(&[webhook], &["id", "endpoint", "events", "status"]))
2392        }
2393
2394        "clickup_webhook_update" => {
2395            let webhook_id = args.get("webhook_id").and_then(|v| v.as_str())
2396                .ok_or("Missing required parameter: webhook_id")?;
2397            let mut body = json!({});
2398            if let Some(endpoint) = args.get("endpoint").and_then(|v| v.as_str()) { body["endpoint"] = json!(endpoint); }
2399            if let Some(events) = args.get("events") { body["events"] = events.clone(); }
2400            if let Some(status) = args.get("status").and_then(|v| v.as_str()) { body["status"] = json!(status); }
2401            let resp = client.put(&format!("/v2/webhook/{}", webhook_id), &body).await.map_err(|e| e.to_string())?;
2402            let webhook = resp.get("webhook").cloned().unwrap_or(resp);
2403            Ok(compact_items(&[webhook], &["id", "endpoint", "events", "status"]))
2404        }
2405
2406        "clickup_webhook_delete" => {
2407            let webhook_id = args.get("webhook_id").and_then(|v| v.as_str())
2408                .ok_or("Missing required parameter: webhook_id")?;
2409            client.delete(&format!("/v2/webhook/{}", webhook_id)).await.map_err(|e| e.to_string())?;
2410            Ok(json!({"message": format!("Webhook {} deleted", webhook_id)}))
2411        }
2412
2413        "clickup_checklist_add_item" => {
2414            let checklist_id = args.get("checklist_id").and_then(|v| v.as_str())
2415                .ok_or("Missing required parameter: checklist_id")?;
2416            let name = args.get("name").and_then(|v| v.as_str())
2417                .ok_or("Missing required parameter: name")?;
2418            let mut body = json!({"name": name});
2419            if let Some(assignee) = args.get("assignee").and_then(|v| v.as_i64()) { body["assignee"] = json!(assignee); }
2420            let resp = client.post(&format!("/v2/checklist/{}/checklist_item", checklist_id), &body).await.map_err(|e| e.to_string())?;
2421            let item = resp.get("checklist").cloned().unwrap_or(resp);
2422            Ok(compact_items(&[item], &["id", "name"]))
2423        }
2424
2425        "clickup_checklist_update_item" => {
2426            let checklist_id = args.get("checklist_id").and_then(|v| v.as_str())
2427                .ok_or("Missing required parameter: checklist_id")?;
2428            let item_id = args.get("item_id").and_then(|v| v.as_str())
2429                .ok_or("Missing required parameter: item_id")?;
2430            let mut body = json!({});
2431            if let Some(name) = args.get("name").and_then(|v| v.as_str()) { body["name"] = json!(name); }
2432            if let Some(resolved) = args.get("resolved").and_then(|v| v.as_bool()) { body["resolved"] = json!(resolved); }
2433            if let Some(assignee) = args.get("assignee").and_then(|v| v.as_i64()) { body["assignee"] = json!(assignee); }
2434            client.put(&format!("/v2/checklist/{}/checklist_item/{}", checklist_id, item_id), &body).await.map_err(|e| e.to_string())?;
2435            Ok(json!({"message": format!("Checklist item {} updated", item_id)}))
2436        }
2437
2438        "clickup_checklist_delete_item" => {
2439            let checklist_id = args.get("checklist_id").and_then(|v| v.as_str())
2440                .ok_or("Missing required parameter: checklist_id")?;
2441            let item_id = args.get("item_id").and_then(|v| v.as_str())
2442                .ok_or("Missing required parameter: item_id")?;
2443            client.delete(&format!("/v2/checklist/{}/checklist_item/{}", checklist_id, item_id)).await.map_err(|e| e.to_string())?;
2444            Ok(json!({"message": format!("Checklist item {} deleted", item_id)}))
2445        }
2446
2447        "clickup_user_get" => {
2448            let team_id = resolve_workspace(args)?;
2449            let user_id = args.get("user_id").and_then(|v| v.as_i64())
2450                .ok_or("Missing required parameter: user_id")?;
2451            let resp = client.get(&format!("/v2/team/{}/user/{}", team_id, user_id)).await.map_err(|e| e.to_string())?;
2452            let member = resp.get("member").cloned().unwrap_or(resp);
2453            Ok(compact_items(&[member], &["user", "role"]))
2454        }
2455
2456        "clickup_workspace_seats" => {
2457            let team_id = resolve_workspace(args)?;
2458            let resp = client.get(&format!("/v2/team/{}/seats", team_id)).await.map_err(|e| e.to_string())?;
2459            Ok(json!(resp))
2460        }
2461
2462        "clickup_workspace_plan" => {
2463            let team_id = resolve_workspace(args)?;
2464            let resp = client.get(&format!("/v2/team/{}/plan", team_id)).await.map_err(|e| e.to_string())?;
2465            Ok(json!(resp))
2466        }
2467
2468        "clickup_tag_create" => {
2469            let space_id = args.get("space_id").and_then(|v| v.as_str())
2470                .ok_or("Missing required parameter: space_id")?;
2471            let name = args.get("name").and_then(|v| v.as_str())
2472                .ok_or("Missing required parameter: name")?;
2473            let mut tag = json!({"name": name});
2474            if let Some(fg) = args.get("tag_fg").and_then(|v| v.as_str()) { tag["tag_fg"] = json!(fg); }
2475            if let Some(bg) = args.get("tag_bg").and_then(|v| v.as_str()) { tag["tag_bg"] = json!(bg); }
2476            let body = json!({"tag": tag});
2477            client.post(&format!("/v2/space/{}/tag", space_id), &body).await.map_err(|e| e.to_string())?;
2478            Ok(json!({"message": format!("Tag '{}' created in space {}", name, space_id)}))
2479        }
2480
2481        "clickup_tag_update" => {
2482            let space_id = args.get("space_id").and_then(|v| v.as_str())
2483                .ok_or("Missing required parameter: space_id")?;
2484            let tag_name = args.get("tag_name").and_then(|v| v.as_str())
2485                .ok_or("Missing required parameter: tag_name")?;
2486            let mut tag = json!({});
2487            if let Some(name) = args.get("name").and_then(|v| v.as_str()) { tag["name"] = json!(name); }
2488            if let Some(fg) = args.get("tag_fg").and_then(|v| v.as_str()) { tag["tag_fg"] = json!(fg); }
2489            if let Some(bg) = args.get("tag_bg").and_then(|v| v.as_str()) { tag["tag_bg"] = json!(bg); }
2490            let body = json!({"tag": tag});
2491            client.put(&format!("/v2/space/{}/tag/{}", space_id, tag_name), &body).await.map_err(|e| e.to_string())?;
2492            Ok(json!({"message": format!("Tag '{}' updated", tag_name)}))
2493        }
2494
2495        "clickup_tag_delete" => {
2496            let space_id = args.get("space_id").and_then(|v| v.as_str())
2497                .ok_or("Missing required parameter: space_id")?;
2498            let tag_name = args.get("tag_name").and_then(|v| v.as_str())
2499                .ok_or("Missing required parameter: tag_name")?;
2500            client.delete(&format!("/v2/space/{}/tag/{}", space_id, tag_name)).await.map_err(|e| e.to_string())?;
2501            Ok(json!({"message": format!("Tag '{}' deleted from space {}", tag_name, space_id)}))
2502        }
2503
2504        "clickup_field_unset" => {
2505            let task_id = args.get("task_id").and_then(|v| v.as_str())
2506                .ok_or("Missing required parameter: task_id")?;
2507            let field_id = args.get("field_id").and_then(|v| v.as_str())
2508                .ok_or("Missing required parameter: field_id")?;
2509            client.delete(&format!("/v2/task/{}/field/{}", task_id, field_id)).await.map_err(|e| e.to_string())?;
2510            Ok(json!({"message": format!("Field {} unset on task {}", field_id, task_id)}))
2511        }
2512
2513        "clickup_attachment_list" => {
2514            let team_id = resolve_workspace(args)?;
2515            let task_id = args.get("task_id").and_then(|v| v.as_str())
2516                .ok_or("Missing required parameter: task_id")?;
2517            let resp = client.get(&format!("/v3/workspaces/{}/task/{}/attachments", team_id, task_id)).await.map_err(|e| e.to_string())?;
2518            let attachments = resp.get("attachments").and_then(|a| a.as_array()).cloned().unwrap_or_default();
2519            Ok(compact_items(&attachments, &["id", "title", "url", "date"]))
2520        }
2521
2522        "clickup_shared_list" => {
2523            let team_id = resolve_workspace(args)?;
2524            let resp = client.get(&format!("/v2/team/{}/shared", team_id)).await.map_err(|e| e.to_string())?;
2525            Ok(json!(resp))
2526        }
2527
2528        "clickup_group_list" => {
2529            let team_id = resolve_workspace(args)?;
2530            let mut qs = format!("team_id={}", team_id);
2531            if let Some(group_ids) = args.get("group_ids").and_then(|v| v.as_array()) {
2532                for id in group_ids {
2533                    if let Some(id) = id.as_str() {
2534                        qs.push_str(&format!("&group_ids[]={}", id));
2535                    }
2536                }
2537            }
2538            let resp = client.get(&format!("/v2/group?{}", qs)).await.map_err(|e| e.to_string())?;
2539            let groups = resp.get("groups").and_then(|g| g.as_array()).cloned().unwrap_or_default();
2540            Ok(compact_items(&groups, &["id", "name", "members"]))
2541        }
2542
2543        "clickup_group_create" => {
2544            let team_id = resolve_workspace(args)?;
2545            let name = args.get("name").and_then(|v| v.as_str())
2546                .ok_or("Missing required parameter: name")?;
2547            let mut body = json!({"name": name});
2548            if let Some(members) = args.get("member_ids") { body["members"] = members.clone(); }
2549            let resp = client.post(&format!("/v2/team/{}/group", team_id), &body).await.map_err(|e| e.to_string())?;
2550            Ok(compact_items(&[resp], &["id", "name"]))
2551        }
2552
2553        "clickup_group_update" => {
2554            let group_id = args.get("group_id").and_then(|v| v.as_str())
2555                .ok_or("Missing required parameter: group_id")?;
2556            let mut body = json!({});
2557            if let Some(name) = args.get("name").and_then(|v| v.as_str()) { body["name"] = json!(name); }
2558            if let Some(add) = args.get("add_members") {
2559                body["members"] = json!({"add": add, "rem": args.get("rem_members").cloned().unwrap_or(json!([]))});
2560            } else if let Some(rem) = args.get("rem_members") {
2561                body["members"] = json!({"add": [], "rem": rem});
2562            }
2563            let resp = client.put(&format!("/v2/group/{}", group_id), &body).await.map_err(|e| e.to_string())?;
2564            Ok(compact_items(&[resp], &["id", "name"]))
2565        }
2566
2567        "clickup_group_delete" => {
2568            let group_id = args.get("group_id").and_then(|v| v.as_str())
2569                .ok_or("Missing required parameter: group_id")?;
2570            client.delete(&format!("/v2/group/{}", group_id)).await.map_err(|e| e.to_string())?;
2571            Ok(json!({"message": format!("Group {} deleted", group_id)}))
2572        }
2573
2574        "clickup_role_list" => {
2575            let team_id = resolve_workspace(args)?;
2576            let resp = client.get(&format!("/v2/team/{}/customroles", team_id)).await.map_err(|e| e.to_string())?;
2577            let roles = resp.get("roles").and_then(|r| r.as_array()).cloned().unwrap_or_default();
2578            Ok(compact_items(&roles, &["id", "name"]))
2579        }
2580
2581        "clickup_guest_get" => {
2582            let team_id = resolve_workspace(args)?;
2583            let guest_id = args.get("guest_id").and_then(|v| v.as_i64())
2584                .ok_or("Missing required parameter: guest_id")?;
2585            let resp = client.get(&format!("/v2/team/{}/guest/{}", team_id, guest_id)).await.map_err(|e| e.to_string())?;
2586            let guest = resp.get("guest").cloned().unwrap_or(resp);
2587            Ok(compact_items(&[guest], &["user", "role"]))
2588        }
2589
2590        unknown => Err(format!("Unknown tool: {}", unknown)),
2591    }
2592}
2593
2594// ── Main server loop ──────────────────────────────────────────────────────────
2595
2596pub async fn serve() -> Result<(), Box<dyn std::error::Error>> {
2597    // Load config at startup
2598    let config = Config::load().map_err(|e| format!("Failed to load config: {}", e))?;
2599    let token = config.auth.token.clone();
2600    if token.is_empty() {
2601        return Err("No API token configured. Run `clickup setup` first.".into());
2602    }
2603    let workspace_id = config.defaults.workspace_id.clone();
2604
2605    let client = ClickUpClient::new(&token, 30)
2606        .map_err(|e| format!("Failed to create HTTP client: {}", e))?;
2607
2608    let stdin = tokio::io::stdin();
2609    let reader = BufReader::new(stdin);
2610    let mut lines = reader.lines();
2611
2612    while let Some(line) = lines.next_line().await? {
2613        let line = line.trim().to_string();
2614        if line.is_empty() {
2615            continue;
2616        }
2617
2618        let msg: Value = match serde_json::from_str(&line) {
2619            Ok(v) => v,
2620            Err(e) => {
2621                // Parse error — send error response with null id
2622                let resp = error_response(&Value::Null, -32700, &format!("Parse error: {}", e));
2623                println!("{}", resp);
2624                continue;
2625            }
2626        };
2627
2628        // Notifications have no id — don't respond
2629        let id = msg.get("id").cloned().unwrap_or(Value::Null);
2630        let method = msg.get("method").and_then(|v| v.as_str()).unwrap_or("");
2631
2632        if id.is_null() && method.starts_with("notifications/") {
2633            // Notification — no response needed
2634            continue;
2635        }
2636
2637        let resp = match method {
2638            "initialize" => {
2639                let version = msg
2640                    .get("params")
2641                    .and_then(|p| p.get("protocolVersion"))
2642                    .and_then(|v| v.as_str())
2643                    .unwrap_or("2024-11-05");
2644                ok_response(
2645                    &id,
2646                    json!({
2647                        "protocolVersion": version,
2648                        "capabilities": {"tools": {}},
2649                        "serverInfo": {
2650                            "name": "clickup-cli",
2651                            "version": env!("CARGO_PKG_VERSION")
2652                        }
2653                    }),
2654                )
2655            }
2656
2657            "tools/list" => ok_response(&id, json!({"tools": tool_list()})),
2658
2659            "tools/call" => {
2660                let params = msg.get("params").cloned().unwrap_or(json!({}));
2661                let tool_name = params
2662                    .get("name")
2663                    .and_then(|v| v.as_str())
2664                    .unwrap_or("");
2665                let arguments = params.get("arguments").cloned().unwrap_or(json!({}));
2666
2667                if tool_name.is_empty() {
2668                    let result = tool_error("Missing tool name".to_string());
2669                    ok_response(&id, result)
2670                } else {
2671                    let result = call_tool(tool_name, &arguments, &client, &workspace_id).await;
2672                    ok_response(&id, result)
2673                }
2674            }
2675
2676            other => {
2677                // Unknown method
2678                eprintln!("Unknown method: {}", other);
2679                error_response(&id, -32601, &format!("Method not found: {}", other))
2680            }
2681        };
2682
2683        println!("{}", resp);
2684    }
2685
2686    Ok(())
2687}