Skip to main content

ai_agent/tools/
types.rs

1// Source: /data/home/swei/claudecode/openclaudecode/src/utils/filePersistence/types.ts
2use crate::types::*;
3use std::future::Future;
4
5pub use crate::types::{ToolDefinition, ToolInputSchema};
6
7// Schema functions for all tools
8fn sleep_schema() -> ToolInputSchema {
9    ToolInputSchema {
10        schema_type: "object".to_string(),
11        properties: serde_json::json!({
12            "duration": {
13                "type": "number",
14                "description": "Duration to sleep in seconds (default: 60)"
15            }
16        }),
17        required: None,
18    }
19}
20
21fn powershell_schema() -> ToolInputSchema {
22    ToolInputSchema {
23        schema_type: "object".to_string(),
24        properties: serde_json::json!({
25            "command": {
26                "type": "string",
27                "description": "PowerShell command to execute"
28            },
29            "timeout": {
30                "type": "number",
31                "description": "Optional timeout in milliseconds (default: 120000, max: 600000)"
32            },
33            "description": {
34                "type": "string",
35                "description": "Brief description of what this command does"
36            },
37            "run_in_background": {
38                "type": "boolean",
39                "description": "Run the command in the background (default: false)"
40            }
41        }),
42        required: Some(vec!["command".to_string()]),
43    }
44}
45
46fn monitor_schema() -> ToolInputSchema {
47    ToolInputSchema {
48        schema_type: "object".to_string(),
49        properties: serde_json::json!({}),
50        required: None,
51    }
52}
53
54fn send_user_file_schema() -> ToolInputSchema {
55    ToolInputSchema {
56        schema_type: "object".to_string(),
57        properties: serde_json::json!({}),
58        required: None,
59    }
60}
61
62fn web_browser_schema() -> ToolInputSchema {
63    ToolInputSchema {
64        schema_type: "object".to_string(),
65        properties: serde_json::json!({}),
66        required: None,
67    }
68}
69
70fn brief_schema() -> ToolInputSchema {
71    ToolInputSchema {
72        schema_type: "object".to_string(),
73        properties: serde_json::json!({
74            "message": {
75                "type": "string",
76                "description": "The message for the user. Supports markdown formatting."
77            },
78            "attachments": {
79                "type": "array",
80                "items": {"type": "string"},
81                "description": "Optional file paths to attach"
82            },
83            "status": {
84                "type": "string",
85                "enum": ["normal", "proactive"],
86                "description": "Use 'proactive' when surfacing something the user hasn't asked for"
87            }
88        }),
89        required: Some(vec!["message".to_string()]),
90    }
91}
92
93fn synthetic_output_schema() -> ToolInputSchema {
94    ToolInputSchema {
95        schema_type: "object".to_string(),
96        properties: serde_json::json!({}),
97        required: None,
98    }
99}
100
101/// A boxed async future that returns a ToolResult or AgentError.
102/// This is the standard return type for tool executor functions.
103pub type ToolFuture =
104    std::pin::Pin<Box<dyn Future<Output = Result<ToolResult, crate::error::AgentError>> + Send>>;
105
106pub trait Tool {
107    fn name(&self) -> &str;
108    fn description(&self) -> &str;
109    fn input_schema(&self) -> ToolInputSchema;
110    fn execute(
111        &self,
112        input: serde_json::Value,
113        context: &ToolContext,
114    ) -> impl Future<Output = Result<ToolResult, crate::error::AgentError>> + Send;
115    /// Backfill observable input before observers see it (SDK stream, transcript, hooks).
116    /// Mutates in place to add legacy/derived fields. Must be idempotent.
117    /// The original API-bound input is never mutated (preserves prompt cache).
118    fn backfill_observable_input(&self, _input: &mut serde_json::Value) {}
119}
120
121fn bash_schema() -> ToolInputSchema {
122    ToolInputSchema {
123        schema_type: "object".to_string(),
124        properties: serde_json::json!({
125            "command": {
126                "type": "string",
127                "description": "The command to execute"
128            },
129            "timeout": {
130                "type": "number",
131                "description": "Optional timeout in milliseconds"
132            },
133            "description": {
134                "type": "string",
135                "description": "Clear, concise description of what this command does in active voice"
136            },
137            "run_in_background": {
138                "type": "boolean",
139                "description": "Set to true to run this command in the background. Use Read to read the output later."
140            },
141            "dangerouslyDisableSandbox": {
142                "type": "boolean",
143                "description": "Set this to true to dangerously override sandbox mode and run commands without sandboxing."
144            }
145        }),
146        required: Some(vec!["command".to_string()]),
147    }
148}
149
150fn file_read_schema() -> ToolInputSchema {
151    ToolInputSchema {
152        schema_type: "object".to_string(),
153        properties: serde_json::json!({
154            "file_path": {
155                "type": "string",
156                "description": "The absolute path to the file to read"
157            },
158            "offset": {
159                "type": "integer",
160                "description": "The line number to start reading from. Only provide if the file is too large to read at once"
161            },
162            "limit": {
163                "type": "integer",
164                "description": "The number of lines to read. Only provide if the file is too large to read at once."
165            },
166            "pages": {
167                "type": "string",
168                "description": "Page range for PDF files (e.g., \"1-5\", \"3\", \"10-20\"). Only applicable to PDF files. Maximum 20 pages per request."
169            }
170        }),
171        required: Some(vec!["file_path".to_string()]),
172    }
173}
174
175fn file_write_schema() -> ToolInputSchema {
176    ToolInputSchema {
177        schema_type: "object".to_string(),
178        properties: serde_json::json!({
179            "file_path": {
180                "type": "string",
181                "description": "The absolute path to the file to write (must be absolute, not relative)"
182            },
183            "content": {
184                "type": "string",
185                "description": "The content to write to the file"
186            }
187        }),
188        required: Some(vec!["file_path".to_string(), "content".to_string()]),
189    }
190}
191
192fn glob_schema() -> ToolInputSchema {
193    ToolInputSchema {
194        schema_type: "object".to_string(),
195        properties: serde_json::json!({
196            "pattern": {
197                "type": "string",
198                "description": "The glob pattern to match files against"
199            },
200            "path": {
201                "type": "string",
202                "description": "The directory to search in. If not specified, the current working directory will be used."
203            }
204        }),
205        required: Some(vec!["pattern".to_string()]),
206    }
207}
208
209fn grep_schema() -> ToolInputSchema {
210    ToolInputSchema {
211        schema_type: "object".to_string(),
212        properties: serde_json::json!({
213            "pattern": {
214                "type": "string",
215                "description": "The regex pattern to search for"
216            },
217            "path": {
218                "type": "string",
219                "description": "The file or directory to search in"
220            }
221        }),
222        required: Some(vec!["pattern".to_string()]),
223    }
224}
225
226fn file_edit_schema() -> ToolInputSchema {
227    ToolInputSchema {
228        schema_type: "object".to_string(),
229        properties: serde_json::json!({
230            "file_path": {
231                "type": "string",
232                "description": "The absolute path to the file to modify"
233            },
234            "old_string": {
235                "type": "string",
236                "description": "The exact text to find and replace"
237            },
238            "new_string": {
239                "type": "string",
240                "description": "The replacement text"
241            },
242            "replace_all": {
243                "type": "boolean",
244                "description": "Replace all occurrences (default false)"
245            }
246        }),
247        required: Some(vec![
248            "file_path".to_string(),
249            "old_string".to_string(),
250            "new_string".to_string(),
251        ]),
252    }
253}
254
255fn notebook_edit_schema() -> ToolInputSchema {
256    ToolInputSchema {
257        schema_type: "object".to_string(),
258        properties: serde_json::json!({
259            "notebook_path": {
260                "type": "string",
261                "description": "The absolute path to the Jupyter notebook file to edit (must be absolute, not relative)"
262            },
263            "cell_id": {
264                "type": "string",
265                "description": "The ID of the cell to edit. When inserting a new cell, the new cell will be inserted after the cell with this ID."
266            },
267            "new_source": {
268                "type": "string",
269                "description": "The new source for the cell"
270            },
271            "cell_type": {
272                "type": "string",
273                "enum": ["code", "markdown"],
274                "description": "The type of the cell (code or markdown). If not specified, defaults to the current cell type."
275            },
276            "edit_mode": {
277                "type": "string",
278                "enum": ["replace", "insert", "delete"],
279                "description": "The type of edit to make (replace, insert, delete). Defaults to replace."
280            }
281        }),
282        required: Some(vec!["notebook_path".to_string(), "new_source".to_string()]),
283    }
284}
285
286fn web_fetch_schema() -> ToolInputSchema {
287    ToolInputSchema {
288        schema_type: "object".to_string(),
289        properties: serde_json::json!({
290            "url": {
291                "type": "string",
292                "description": "The URL to fetch content from"
293            },
294            "headers": {
295                "type": "object",
296                "description": "Optional HTTP headers",
297                "additionalProperties": {
298                    "type": "string"
299                }
300            },
301            "prompt": {
302                "type": "string",
303                "description": "Optional prompt for LLM-based content extraction"
304            }
305        }),
306        required: Some(vec!["url".to_string()]),
307    }
308}
309
310fn web_search_schema() -> ToolInputSchema {
311    ToolInputSchema {
312        schema_type: "object".to_string(),
313        properties: serde_json::json!({
314            "query": {
315                "type": "string",
316                "description": "The search query to use"
317            },
318            "allowed_domains": {
319                "type": "array",
320                "items": {"type": "string"},
321                "description": "Only include search results from these domains"
322            },
323            "blocked_domains": {
324                "type": "array",
325                "items": {"type": "string"},
326                "description": "Never include search results from these domains"
327            }
328        }),
329        required: Some(vec!["query".to_string()]),
330    }
331}
332
333const ALL_TOOLS: &[(&str, &str, fn() -> ToolDefinition)] = &[
334    ("Bash", "Execute shell commands", || ToolDefinition {
335        name: "Bash".to_string(),
336        description: "Execute shell commands".to_string(),
337        input_schema: bash_schema(),
338        annotations: None,
339        should_defer: None,
340        always_load: None,
341        is_mcp: None,
342        search_hint: None,
343        aliases: None,
344        user_facing_name: None,
345        interrupt_behavior: None,
346    }),
347    ("Read", "Read files, images, PDFs, notebooks", || {
348        ToolDefinition {
349            name: "Read".to_string(),
350            description: "Read files from filesystem. Supports text files, images (PNG, JPG, GIF, WebP), PDFs, and Jupyter notebooks. Use offset and limit for large files.".to_string(),
351            input_schema: file_read_schema(),
352            annotations: Some(ToolAnnotations::concurrency_safe()),
353            should_defer: Some(false),
354            always_load: Some(true),
355            is_mcp: None,
356            search_hint: Some("read files, images, PDFs, notebooks".to_string()),
357        aliases: None,
358    user_facing_name: None,
359            interrupt_behavior: None,
360        }
361    }),
362    ("Write", "Write content to files", || ToolDefinition {
363        name: "Write".to_string(),
364        description: "Write content to files".to_string(),
365        input_schema: file_write_schema(),
366        annotations: None,
367        should_defer: None,
368        always_load: None,
369        is_mcp: None,
370        search_hint: None,
371        aliases: None,
372        user_facing_name: None,
373        interrupt_behavior: None,
374    }),
375    ("Glob", "Find files by name pattern or wildcard", || {
376        ToolDefinition {
377            name: "Glob".to_string(),
378            description: "Find files by glob pattern (glob pattern matching for file discovery)"
379                .to_string(),
380            input_schema: glob_schema(),
381            annotations: Some(ToolAnnotations::concurrency_safe()),
382            should_defer: Some(false),
383            always_load: Some(true),
384            is_mcp: None,
385            search_hint: Some("find files by name pattern or wildcard".to_string()),
386            aliases: None,
387            user_facing_name: None,
388            interrupt_behavior: None,
389        }
390    }),
391    ("Grep", "Search file contents using regex", || {
392        ToolDefinition {
393        name: "Grep".to_string(),
394        description: "Search file contents using regex patterns. Uses ripgrep (rg) if available, falls back to grep.".to_string(),
395        input_schema: grep_schema(),
396        annotations: Some(ToolAnnotations::concurrency_safe()),
397        should_defer: Some(false),
398        always_load: Some(true),
399        is_mcp: None,
400        search_hint: Some("search file contents using regex".to_string()),
401    aliases: None,
402    user_facing_name: None,
403        interrupt_behavior: None,
404    }
405    }),
406    (
407        "FileEdit",
408        "Edit files by performing exact string replacements",
409        || ToolDefinition {
410            name: "FileEdit".to_string(),
411            description: "Edit files by performing exact string replacements".to_string(),
412            input_schema: file_edit_schema(),
413            annotations: None,
414            should_defer: None,
415            always_load: None,
416            is_mcp: None,
417            search_hint: None,
418            aliases: None,
419            user_facing_name: None,
420            interrupt_behavior: None,
421        },
422    ),
423    (
424        "NotebookEdit",
425        "Edit Jupyter notebook cells (.ipynb)",
426        || ToolDefinition {
427            name: "NotebookEdit".to_string(),
428            description:
429                "Edit Jupyter notebook (.ipynb) cells: replace, insert, or delete cell content"
430                    .to_string(),
431            input_schema: notebook_edit_schema(),
432            annotations: None,
433            should_defer: Some(true),
434            always_load: None,
435            is_mcp: None,
436            search_hint: Some("edit Jupyter notebook cells (.ipynb)".to_string()),
437            aliases: None,
438            user_facing_name: None,
439            interrupt_behavior: None,
440        },
441    ),
442    (
443        "WebFetch",
444        "Fetch content from a URL and return it as text",
445        || {
446            ToolDefinition {
447        name: "WebFetch".to_string(),
448        description: "Fetch content from a URL and return it as text. Supports HTML pages, JSON APIs, and plain text. Strips HTML tags for readability. Preapproved hosts can be fetched without additional permission.".to_string(),
449        input_schema: web_fetch_schema(),
450        annotations: None,
451        should_defer: Some(true),
452        always_load: None,
453        is_mcp: None,
454        search_hint: Some("fetch web pages and URLs".to_string()),
455    aliases: None,
456    user_facing_name: None,
457        interrupt_behavior: None,
458    }
459        },
460    ),
461    ("WebSearch", "Search the web for information", || {
462        ToolDefinition {
463        name: "WebSearch".to_string(),
464        description: "Search the web for information. Returns search results with titles, URLs, and snippets.".to_string(),
465        input_schema: web_search_schema(),
466        annotations: Some(ToolAnnotations::concurrency_safe()),
467        should_defer: Some(true),
468        always_load: None,
469        is_mcp: None,
470        search_hint: Some("web search for information".to_string()),
471    aliases: None,
472    user_facing_name: None,
473        interrupt_behavior: None,
474    }
475    }),
476    (
477        "Agent",
478        "Launch a new agent to handle complex multi-step tasks",
479        || {
480            ToolDefinition {
481        name: "Agent".to_string(),
482        description: "Launch a new agent to handle complex, multi-step tasks autonomously. Use this tool to spawn specialized subagents.".to_string(),
483        input_schema: agent_schema(),
484        annotations: None,
485        should_defer: None,
486        always_load: None,
487        is_mcp: None,
488        search_hint: None,
489    aliases: None,
490    user_facing_name: None,
491        interrupt_behavior: None,
492    }
493        },
494    ),
495    ("TaskCreate", "Create a new task in the task list", || {
496        ToolDefinition {
497            name: "TaskCreate".to_string(),
498            description: "Create a new task in the task list".to_string(),
499            input_schema: task_create_schema(),
500            annotations: None,
501            should_defer: None,
502            always_load: None,
503            is_mcp: None,
504            search_hint: None,
505            aliases: None,
506            user_facing_name: None,
507            interrupt_behavior: None,
508        }
509    }),
510    ("TaskList", "List all tasks in the task list", || {
511        ToolDefinition {
512            name: "TaskList".to_string(),
513            description: "List all tasks in the task list".to_string(),
514            input_schema: task_list_schema(),
515            annotations: Some(ToolAnnotations::concurrency_safe()),
516            should_defer: None,
517            always_load: None,
518            is_mcp: None,
519            search_hint: None,
520            aliases: None,
521            user_facing_name: None,
522            interrupt_behavior: None,
523        }
524    }),
525    ("TaskUpdate", "Update an existing task", || ToolDefinition {
526        name: "TaskUpdate".to_string(),
527        description: "Update an existing task's status or details".to_string(),
528        input_schema: task_update_schema(),
529        annotations: None,
530        should_defer: None,
531        always_load: None,
532        is_mcp: None,
533        search_hint: None,
534        aliases: None,
535        user_facing_name: None,
536        interrupt_behavior: None,
537    }),
538    ("TaskGet", "Get details of a specific task", || {
539        ToolDefinition {
540            name: "TaskGet".to_string(),
541            description: "Get details of a specific task by ID".to_string(),
542            input_schema: task_get_schema(),
543            annotations: Some(ToolAnnotations::concurrency_safe()),
544            should_defer: None,
545            always_load: None,
546            is_mcp: None,
547            search_hint: None,
548            aliases: None,
549            user_facing_name: None,
550            interrupt_behavior: None,
551        }
552    }),
553    (
554        "TeamCreate",
555        "Create a team of agents for parallel work",
556        || ToolDefinition {
557            name: "TeamCreate".to_string(),
558            description: "Create a team of agents that can work in parallel".to_string(),
559            input_schema: team_create_schema(),
560            annotations: None,
561            should_defer: None,
562            always_load: None,
563            is_mcp: None,
564            search_hint: None,
565            aliases: None,
566            user_facing_name: None,
567            interrupt_behavior: None,
568        },
569    ),
570    ("TeamDelete", "Delete a team of agents", || ToolDefinition {
571        name: "TeamDelete".to_string(),
572        description: "Delete a previously created team".to_string(),
573        input_schema: team_delete_schema(),
574        annotations: None,
575        should_defer: None,
576        always_load: None,
577        is_mcp: None,
578        search_hint: None,
579        aliases: None,
580        user_facing_name: None,
581        interrupt_behavior: None,
582    }),
583    ("SendMessage", "Send a message to another agent", || {
584        ToolDefinition {
585            name: "SendMessage".to_string(),
586            description: "Send a message to another agent".to_string(),
587            input_schema: send_message_schema(),
588            annotations: None,
589            should_defer: None,
590            always_load: None,
591            is_mcp: None,
592            search_hint: None,
593            aliases: None,
594            user_facing_name: None,
595            interrupt_behavior: None,
596        }
597    }),
598    ("EnterWorktree", "Create and enter a git worktree", || {
599        ToolDefinition {
600            name: "EnterWorktree".to_string(),
601            description: "Create and enter a git worktree for isolated work".to_string(),
602            input_schema: enter_worktree_schema(),
603            annotations: None,
604            should_defer: None,
605            always_load: None,
606            is_mcp: None,
607            search_hint: None,
608            aliases: None,
609            user_facing_name: None,
610            interrupt_behavior: None,
611        }
612    }),
613    (
614        "ExitWorktree",
615        "Exit a worktree and return to original directory",
616        || ToolDefinition {
617            name: "ExitWorktree".to_string(),
618            description: "Exit a worktree and return to the original working directory".to_string(),
619            input_schema: exit_worktree_schema(),
620            annotations: None,
621            should_defer: None,
622            always_load: None,
623            is_mcp: None,
624            search_hint: None,
625            aliases: None,
626            user_facing_name: None,
627            interrupt_behavior: None,
628        },
629    ),
630    ("EnterPlanMode", "Enter structured planning mode", || {
631        ToolDefinition {
632            name: "EnterPlanMode".to_string(),
633            description: "Enter structured planning mode to explore and design implementation"
634                .to_string(),
635            input_schema: enter_plan_mode_schema(),
636            annotations: None,
637            should_defer: None,
638            always_load: None,
639            is_mcp: None,
640            search_hint: None,
641            aliases: None,
642            user_facing_name: None,
643            interrupt_behavior: None,
644        }
645    }),
646    ("ExitPlanMode", "Exit planning mode", || ToolDefinition {
647        name: "ExitPlanMode".to_string(),
648        description: "Exit planning mode and present the plan for approval".to_string(),
649        input_schema: exit_plan_mode_schema(),
650        annotations: None,
651        should_defer: None,
652        always_load: None,
653        is_mcp: None,
654        search_hint: None,
655        aliases: None,
656        user_facing_name: None,
657        interrupt_behavior: None,
658    }),
659    (
660        "AskUserQuestion",
661        "Ask the user a question with multiple choice options",
662        || ToolDefinition {
663            name: "AskUserQuestion".to_string(),
664            description: "Ask the user a question with multiple choice options".to_string(),
665            input_schema: ask_user_question_schema(),
666            annotations: Some(ToolAnnotations::concurrency_safe()),
667            should_defer: None,
668            always_load: None,
669            is_mcp: None,
670            search_hint: None,
671            aliases: None,
672            user_facing_name: None,
673            interrupt_behavior: None,
674        },
675    ),
676    ("ToolSearch", "Search for available tools", || {
677        ToolDefinition {
678            name: "ToolSearch".to_string(),
679            description: "Search for available tools by name or description".to_string(),
680            input_schema: tool_search_schema(),
681            annotations: Some(ToolAnnotations::concurrency_safe()),
682            should_defer: None,
683            always_load: None,
684            is_mcp: None,
685            search_hint: None,
686            aliases: None,
687            user_facing_name: None,
688            interrupt_behavior: None,
689        }
690    }),
691    ("CronCreate", "Create a scheduled task", || ToolDefinition {
692        name: "CronCreate".to_string(),
693        description: "Create a scheduled task that runs on a cron schedule".to_string(),
694        input_schema: cron_create_schema(),
695        annotations: None,
696        should_defer: None,
697        always_load: None,
698        is_mcp: None,
699        search_hint: None,
700        aliases: None,
701        user_facing_name: None,
702        interrupt_behavior: None,
703    }),
704    ("CronDelete", "Delete a scheduled task", || ToolDefinition {
705        name: "CronDelete".to_string(),
706        description: "Delete a previously created scheduled task".to_string(),
707        input_schema: cron_delete_schema(),
708        annotations: Some(ToolAnnotations::concurrency_safe()),
709        should_defer: None,
710        always_load: None,
711        is_mcp: None,
712        search_hint: None,
713        aliases: None,
714        user_facing_name: None,
715        interrupt_behavior: None,
716    }),
717    ("CronList", "List all scheduled tasks", || ToolDefinition {
718        name: "CronList".to_string(),
719        description: "List all scheduled tasks".to_string(),
720        input_schema: cron_list_schema(),
721        annotations: Some(ToolAnnotations::concurrency_safe()),
722        should_defer: None,
723        always_load: None,
724        is_mcp: None,
725        search_hint: None,
726        aliases: None,
727        user_facing_name: None,
728        interrupt_behavior: None,
729    }),
730    ("Config", "Read or update configuration", || {
731        ToolDefinition {
732            name: "Config".to_string(),
733            description: "Read or update dynamic configuration".to_string(),
734            input_schema: config_schema(),
735            annotations: Some(ToolAnnotations::concurrency_safe()),
736            should_defer: None,
737            always_load: None,
738            is_mcp: None,
739            search_hint: None,
740            aliases: None,
741            user_facing_name: None,
742            interrupt_behavior: None,
743        }
744    }),
745    ("TodoWrite", "Manage the session task checklist", || {
746        ToolDefinition {
747            name: "TodoWrite".to_string(),
748            description:
749                "Update the todo list for this session. Provide the complete updated list of todos."
750                    .to_string(),
751            input_schema: todo_write_schema(),
752            annotations: Some(ToolAnnotations::concurrency_safe()),
753            should_defer: Some(true),
754            always_load: None,
755            is_mcp: None,
756            search_hint: Some("manage the session task checklist".to_string()),
757            aliases: None,
758            user_facing_name: None,
759            interrupt_behavior: None,
760        }
761    }),
762    ("Skill", "Invoke a skill by name", || {
763        ToolDefinition {
764        name: "Skill".to_string(),
765        description: "Invoke a skill by name. Skills are pre-built workflows or commands that can be executed to accomplish specific tasks.".to_string(),
766        input_schema: skill_schema(),
767        annotations: Some(ToolAnnotations::concurrency_safe()),
768        should_defer: None,
769        always_load: None,
770        is_mcp: None,
771        search_hint: Some("invoke skills and workflows".to_string()),
772    aliases: None,
773    user_facing_name: None,
774        interrupt_behavior: None,
775    }
776    }),
777    ("TaskStop", "Stop a running background task", || {
778        ToolDefinition {
779        name: "TaskStop".to_string(),
780        description: "Stop a running background task by ID. Also accepts shell_id for backward compatibility with the deprecated KillShell tool.".to_string(),
781        input_schema: task_stop_schema(),
782        annotations: Some(ToolAnnotations::concurrency_safe()),
783        should_defer: Some(true),
784        always_load: None,
785        is_mcp: None,
786        search_hint: Some("kill a running background task".to_string()),
787    aliases: None,
788    user_facing_name: None,
789        interrupt_behavior: None,
790    }
791    }),
792    ("TaskOutput", "Retrieve output from background tasks", || {
793        ToolDefinition {
794        name: "TaskOutput".to_string(),
795        description: "Retrieve output from a running or completed background task (bash command, agent, etc.). Supports blocking wait for completion with configurable timeout.".to_string(),
796        input_schema: task_output_schema(),
797        annotations: Some(ToolAnnotations::concurrency_safe()),
798        should_defer: None,
799        always_load: None,
800        is_mcp: None,
801        search_hint: Some("get task output and results".to_string()),
802    aliases: None,
803    user_facing_name: None,
804        interrupt_behavior: None,
805    }
806    }),
807    ("Monitor", "Monitor system resources", || ToolDefinition {
808        name: "Monitor".to_string(),
809        description: "Monitor system resources and performance".to_string(),
810        input_schema: monitor_schema(),
811        annotations: None,
812        should_defer: None,
813        always_load: None,
814        is_mcp: None,
815        search_hint: None,
816        aliases: None,
817        user_facing_name: None,
818        interrupt_behavior: None,
819    }),
820    ("send_user_file", "Send a file from user to agent", || {
821        ToolDefinition {
822            name: "send_user_file".to_string(),
823            description: "Send a file from the user to the agent".to_string(),
824            input_schema: send_user_file_schema(),
825            annotations: None,
826            should_defer: None,
827            always_load: None,
828            is_mcp: None,
829            search_hint: None,
830            aliases: None,
831            user_facing_name: None,
832            interrupt_behavior: None,
833        }
834    }),
835    ("WebBrowser", "Control a web browser", || ToolDefinition {
836        name: "WebBrowser".to_string(),
837        description: "Control a web browser for automation".to_string(),
838        input_schema: web_browser_schema(),
839        annotations: None,
840        should_defer: None,
841        always_load: None,
842        is_mcp: None,
843        search_hint: None,
844        aliases: None,
845        user_facing_name: None,
846        interrupt_behavior: None,
847    }),
848    (
849        "LSP",
850        "Code intelligence via Language Server Protocol",
851        || {
852            ToolDefinition {
853        name: "LSP".to_string(),
854        description: "Interact with Language Server Protocol servers for code intelligence (definitions, references, symbols, hover, call hierarchy)".to_string(),
855        input_schema: lsp_schema(),
856        annotations: Some(ToolAnnotations::concurrency_safe()),
857        should_defer: None,
858        always_load: None,
859        is_mcp: None,
860        search_hint: None,
861    aliases: None,
862    user_facing_name: None,
863        interrupt_behavior: None,
864    }
865        },
866    ),
867    (
868        "RemoteTrigger",
869        "Manage remote Claude Code agents via CCR API",
870        || ToolDefinition {
871            name: "RemoteTrigger".to_string(),
872            description:
873                "Manage scheduled remote Claude Code agents (triggers) via the claude.ai CCR API"
874                    .to_string(),
875            input_schema: remote_trigger_schema(),
876            annotations: None,
877            should_defer: None,
878            always_load: None,
879            is_mcp: None,
880            search_hint: None,
881            aliases: None,
882            user_facing_name: None,
883            interrupt_behavior: None,
884        },
885    ),
886    ("ListMcpResourcesTool", "List MCP server resources", || {
887        ToolDefinition {
888            name: "ListMcpResourcesTool".to_string(),
889            description: "List available resources from configured MCP servers".to_string(),
890            input_schema: list_mcp_resources_schema(),
891            annotations: Some(ToolAnnotations::concurrency_safe()),
892            should_defer: None,
893            always_load: None,
894            is_mcp: None,
895            search_hint: None,
896            aliases: None,
897            user_facing_name: None,
898            interrupt_behavior: None,
899        }
900    }),
901    (
902        "ReadMcpResourceTool",
903        "Read MCP server resources by URI",
904        || ToolDefinition {
905            name: "ReadMcpResourceTool".to_string(),
906            description: "Read a specific resource from an MCP server by URI".to_string(),
907            input_schema: read_mcp_resource_schema(),
908            annotations: Some(ToolAnnotations::concurrency_safe()),
909            should_defer: None,
910            always_load: None,
911            is_mcp: None,
912            search_hint: None,
913            aliases: None,
914            user_facing_name: None,
915            interrupt_behavior: None,
916        },
917    ),
918    (
919        "MCPTool",
920        "Execute a tool on an MCP server",
921        || ToolDefinition {
922            name: "MCPTool".to_string(),
923            description: "Execute a tool on an MCP server. MCP tools define their own schemas and are registered dynamically.".to_string(),
924            input_schema: mcp_tool_schema(),
925            annotations: None,
926            should_defer: None,
927            always_load: None,
928            is_mcp: Some(true),
929            search_hint: Some("execute MCP server tools".to_string()),
930            aliases: None,
931            user_facing_name: None,
932            interrupt_behavior: None,
933        },
934    ),
935    (
936        "McpAuth",
937        "Authenticate an MCP server",
938        || ToolDefinition {
939            name: "McpAuth".to_string(),
940            description: "Authenticate an MCP server that requires OAuth. Returns an authorization URL for the user to complete the flow.".to_string(),
941            input_schema: mcp_auth_schema(),
942            annotations: None,
943            should_defer: None,
944            always_load: None,
945            is_mcp: Some(true),
946            search_hint: Some("authenticate MCP server OAuth".to_string()),
947            aliases: None,
948            user_facing_name: None,
949            interrupt_behavior: None,
950        },
951    ),
952    (
953        "SendUserMessage",
954        "Send a message to the user",
955        || ToolDefinition {
956            name: "SendUserMessage".to_string(),
957            description: "Send a message to the user that they will actually read. Text outside this tool is visible in the detail view, but most won't open it -- the answer lives here.".to_string(),
958            input_schema: brief_schema(),
959            annotations: Some(ToolAnnotations::concurrency_safe()),
960            should_defer: None,
961            always_load: None,
962            is_mcp: None,
963            search_hint: Some("send message to user".to_string()),
964            aliases: None,
965            user_facing_name: None,
966            interrupt_behavior: None,
967        },
968    ),
969    (
970        "StructuredOutput",
971        "Return structured output in the requested format",
972        || ToolDefinition {
973            name: "StructuredOutput".to_string(),
974            description: "Return structured output in the requested format. You MUST call this tool exactly once at the end of your response to provide the structured output.".to_string(),
975            input_schema: synthetic_output_schema(),
976            annotations: Some(ToolAnnotations::concurrency_safe()),
977            should_defer: None,
978            always_load: None,
979            is_mcp: None,
980            search_hint: Some("return the final response as structured JSON".to_string()),
981            aliases: None,
982            user_facing_name: None,
983            interrupt_behavior: None,
984        },
985    ),
986    (
987        "Sleep",
988        "Wait for a specified duration",
989        || ToolDefinition {
990            name: "Sleep".to_string(),
991            description: "Wait for a specified duration. The user can interrupt the sleep at any time. Prefer this over Bash(sleep ...) — it doesn't hold a shell process.".to_string(),
992            input_schema: sleep_schema(),
993            annotations: Some(ToolAnnotations::concurrency_safe()),
994            should_defer: None,
995            always_load: None,
996            is_mcp: None,
997            search_hint: None,
998            aliases: None,
999            user_facing_name: None,
1000            interrupt_behavior: None,
1001        },
1002    ),
1003    (
1004        "PowerShell",
1005        "Execute PowerShell commands",
1006        || ToolDefinition {
1007            name: "PowerShell".to_string(),
1008            description: "Execute a PowerShell command. Windows-only tool for PowerShell cmdlets and native executable execution".to_string(),
1009            input_schema: powershell_schema(),
1010            annotations: None,
1011            should_defer: None,
1012            always_load: None,
1013            is_mcp: None,
1014            search_hint: None,
1015            aliases: None,
1016            user_facing_name: None,
1017            interrupt_behavior: None,
1018        },
1019    ),
1020    (
1021        "OverflowTest",
1022        "Test overflow behavior",
1023        || ToolDefinition {
1024            name: "OverflowTest".to_string(),
1025            description: "Test overflow behavior (not implemented)".to_string(),
1026            input_schema: overflow_test_schema(),
1027            annotations: None,
1028            should_defer: None,
1029            always_load: None,
1030            is_mcp: None,
1031            search_hint: None,
1032            aliases: None,
1033            user_facing_name: None,
1034            interrupt_behavior: None,
1035        },
1036    ),
1037    (
1038        "ReviewArtifact",
1039        "Review artifacts",
1040        || ToolDefinition {
1041            name: "ReviewArtifact".to_string(),
1042            description: "Review artifacts (not implemented)".to_string(),
1043            input_schema: review_artifact_schema(),
1044            annotations: None,
1045            should_defer: None,
1046            always_load: None,
1047            is_mcp: None,
1048            search_hint: None,
1049            aliases: None,
1050            user_facing_name: None,
1051            interrupt_behavior: None,
1052        },
1053    ),
1054    (
1055        "Workflow",
1056        "Manage workflows",
1057        || ToolDefinition {
1058            name: "Workflow".to_string(),
1059            description: "Manage workflows (not implemented)".to_string(),
1060            input_schema: workflow_schema(),
1061            annotations: None,
1062            should_defer: None,
1063            always_load: None,
1064            is_mcp: None,
1065            search_hint: None,
1066            aliases: None,
1067            user_facing_name: None,
1068            interrupt_behavior: None,
1069        },
1070    ),
1071    (
1072        "Snip",
1073        "Compaction tool",
1074        || ToolDefinition {
1075            name: "Snip".to_string(),
1076            description: "Model-callable compaction tool (not implemented)".to_string(),
1077            input_schema: snip_schema(),
1078            annotations: None,
1079            should_defer: None,
1080            always_load: None,
1081            is_mcp: None,
1082            search_hint: None,
1083            aliases: None,
1084            user_facing_name: None,
1085            interrupt_behavior: None,
1086        },
1087    ),
1088    (
1089        "DiscoverSkills",
1090        "Discover available skills",
1091        || ToolDefinition {
1092            name: "DiscoverSkills".to_string(),
1093            description: "On-demand skill discovery (not implemented)".to_string(),
1094            input_schema: discover_skills_schema(),
1095            annotations: None,
1096            should_defer: None,
1097            always_load: None,
1098            is_mcp: None,
1099            search_hint: None,
1100            aliases: None,
1101            user_facing_name: None,
1102            interrupt_behavior: None,
1103        },
1104    ),
1105    (
1106        "TerminalCapture",
1107        "Capture terminal screen",
1108        || ToolDefinition {
1109            name: "TerminalCapture".to_string(),
1110            description: "Terminal screen capture (not implemented)".to_string(),
1111            input_schema: terminal_capture_schema(),
1112            annotations: None,
1113            should_defer: None,
1114            always_load: None,
1115            is_mcp: None,
1116            search_hint: None,
1117            aliases: None,
1118            user_facing_name: None,
1119            interrupt_behavior: None,
1120        },
1121    ),
1122];
1123
1124fn agent_schema() -> ToolInputSchema {
1125    ToolInputSchema {
1126        schema_type: "object".to_string(),
1127        properties: serde_json::json!({
1128            "description": {
1129                "type": "string",
1130                "description": "A short description (3-5 words) summarizing what the agent will do"
1131            },
1132            "subagent_type": {
1133                "type": "string",
1134                "description": "The type of subagent to use. If omitted, uses the general-purpose agent."
1135            },
1136            "prompt": {
1137                "type": "string",
1138                "description": "The task prompt for the subagent to execute"
1139            },
1140            "model": {
1141                "type": "string",
1142                "description": "Optional model override for this subagent"
1143            },
1144            "max_turns": {
1145                "type": "number",
1146                "description": "Maximum number of turns for this subagent (default: 10)"
1147            },
1148            "run_in_background": {
1149                "type": "boolean",
1150                "description": "Whether to run the agent in the background (default: false)"
1151            },
1152            "isolation": {
1153                "type": "string",
1154                "enum": ["worktree", "remote"],
1155                "description": "Isolation mode: 'worktree' for git worktree, 'remote' for remote CCR"
1156            }
1157        }),
1158        required: Some(vec!["description".to_string(), "prompt".to_string()]),
1159    }
1160}
1161
1162// Task tool schemas
1163fn task_create_schema() -> ToolInputSchema {
1164    ToolInputSchema {
1165        schema_type: "object".to_string(),
1166        properties: serde_json::json!({
1167            "subject": { "type": "string", "description": "A brief title for the task" },
1168            "description": { "type": "string", "description": "What needs to be done" },
1169            "activeForm": { "type": "string", "description": "Present continuous form shown in spinner when in_progress (e.g., \"Running tests\")" },
1170            "metadata": {
1171                "type": "object",
1172                "description": "Arbitrary metadata to attach to the task"
1173            }
1174        }),
1175        required: Some(vec!["subject".to_string(), "description".to_string()]),
1176    }
1177}
1178
1179fn task_list_schema() -> ToolInputSchema {
1180    ToolInputSchema {
1181        schema_type: "object".to_string(),
1182        properties: serde_json::json!({}),
1183        required: None,
1184    }
1185}
1186
1187fn task_update_schema() -> ToolInputSchema {
1188    ToolInputSchema {
1189        schema_type: "object".to_string(),
1190        properties: serde_json::json!({
1191            "taskId": { "type": "string", "description": "The ID of the task to update" },
1192            "subject": { "type": "string", "description": "New subject for the task" },
1193            "description": { "type": "string", "description": "New description for the task" },
1194            "status": { "type": "string", "enum": ["pending", "in_progress", "completed", "deleted"], "description": "New status for the task" },
1195            "activeForm": { "type": "string", "description": "Present continuous form shown in spinner when in_progress (e.g., \"Running tests\")" },
1196            "addBlocks": { "type": "array", "items": {"type": "string"}, "description": "Task IDs that this task blocks" },
1197            "addBlockedBy": { "type": "array", "items": {"type": "string"}, "description": "Task IDs that block this task" },
1198            "owner": { "type": "string", "description": "New owner for the task" },
1199            "metadata": {
1200                "type": "object",
1201                "description": "Metadata keys to merge into the task. Set a key to null to delete it."
1202            }
1203        }),
1204        required: Some(vec!["taskId".to_string()]),
1205    }
1206}
1207
1208fn task_get_schema() -> ToolInputSchema {
1209    ToolInputSchema {
1210        schema_type: "object".to_string(),
1211        properties: serde_json::json!({
1212            "taskId": { "type": "string", "description": "The ID of the task to retrieve" }
1213        }),
1214        required: Some(vec!["taskId".to_string()]),
1215    }
1216}
1217
1218// Team tool schemas
1219fn team_create_schema() -> ToolInputSchema {
1220    ToolInputSchema {
1221        schema_type: "object".to_string(),
1222        properties: serde_json::json!({
1223            "name": { "type": "string", "description": "Name of the team" },
1224            "description": { "type": "string", "description": "Description of what the team does" },
1225            "agents": { "type": "array", "items": serde_json::json!({}), "description": "List of agents in the team" }
1226        }),
1227        required: Some(vec!["name".to_string()]),
1228    }
1229}
1230
1231fn team_delete_schema() -> ToolInputSchema {
1232    ToolInputSchema {
1233        schema_type: "object".to_string(),
1234        properties: serde_json::json!({
1235            "name": { "type": "string", "description": "Name of the team to delete" }
1236        }),
1237        required: Some(vec!["name".to_string()]),
1238    }
1239}
1240
1241fn send_message_schema() -> ToolInputSchema {
1242    ToolInputSchema {
1243        schema_type: "object".to_string(),
1244        properties: serde_json::json!({
1245            "to": { "type": "string", "description": "Agent name to send message to" },
1246            "message": { "type": "string", "description": "Message content" }
1247        }),
1248        required: Some(vec!["to".to_string(), "message".to_string()]),
1249    }
1250}
1251
1252// Worktree tool schemas
1253fn enter_worktree_schema() -> ToolInputSchema {
1254    ToolInputSchema {
1255        schema_type: "object".to_string(),
1256        properties: serde_json::json!({
1257            "name": { "type": "string", "description": "Optional name for the worktree" }
1258        }),
1259        required: None,
1260    }
1261}
1262
1263fn exit_worktree_schema() -> ToolInputSchema {
1264    ToolInputSchema {
1265        schema_type: "object".to_string(),
1266        properties: serde_json::json!({
1267            "action": { "type": "string", "enum": ["keep", "remove"], "description": "What to do with the worktree" },
1268            "discardChanges": { "type": "boolean", "description": "Discard uncommitted changes before removing" }
1269        }),
1270        required: None,
1271    }
1272}
1273
1274// Plan mode tool schemas
1275fn enter_plan_mode_schema() -> ToolInputSchema {
1276    ToolInputSchema {
1277        schema_type: "object".to_string(),
1278        properties: serde_json::json!({
1279            "allowedPrompts": { "type": "array", "items": { "type": "string" }, "description": "Prompt-based permissions" }
1280        }),
1281        required: None,
1282    }
1283}
1284
1285fn exit_plan_mode_schema() -> ToolInputSchema {
1286    ToolInputSchema {
1287        schema_type: "object".to_string(),
1288        properties: serde_json::json!({}),
1289        required: None,
1290    }
1291}
1292
1293// Ask user question schema
1294fn ask_user_question_schema() -> ToolInputSchema {
1295    ToolInputSchema {
1296        schema_type: "object".to_string(),
1297        properties: serde_json::json!({
1298            "question": { "type": "string", "description": "The complete question to ask the user" },
1299            "header": { "type": "string", "description": "Very short label displayed as a chip/tag (max 12 chars)" },
1300            "options": { "type": "array", "items": serde_json::json!({}), "description": "Available choices for this question. Must have 2-4 options." },
1301            "multiSelect": { "type": "boolean", "description": "Set to true to allow the user to select multiple options instead of just one" },
1302            "preview": { "type": "object", "properties": { "type": { "type": "string", "enum": ["html", "markdown"] }, "content": { "type": "string" } }, "description": "Optional HTML or Markdown preview to show the user alongside the question" }
1303        }),
1304        required: Some(vec![
1305            "question".to_string(),
1306            "header".to_string(),
1307            "options".to_string(),
1308        ]),
1309    }
1310}
1311
1312// ToolSearch schema
1313fn tool_search_schema() -> ToolInputSchema {
1314    ToolInputSchema {
1315        schema_type: "object".to_string(),
1316        properties: serde_json::json!({
1317            "query": { "type": "string", "description": "Query to find deferred tools. Use \"select:<tool_name>\" for direct selection, or keywords to search." },
1318            "max_results": { "type": "number", "description": "Maximum number of results to return (default: 5)" }
1319        }),
1320        required: Some(vec!["query".to_string()]),
1321    }
1322}
1323
1324// TaskStop schema
1325fn task_stop_schema() -> ToolInputSchema {
1326    ToolInputSchema {
1327        schema_type: "object".to_string(),
1328        properties: serde_json::json!({
1329            "task_id": { "type": "string", "description": "The ID of the background task to stop" },
1330            "shell_id": { "type": "string", "description": "Deprecated: use task_id instead" }
1331        }),
1332        required: None,
1333    }
1334}
1335
1336// TaskOutput schema
1337fn task_output_schema() -> ToolInputSchema {
1338    ToolInputSchema {
1339        schema_type: "object".to_string(),
1340        properties: serde_json::json!({
1341            "task_id": { "type": "string", "description": "The task ID to get output from" },
1342            "block": { "type": "boolean", "description": "Whether to wait for completion. Default: true" },
1343            "timeout": { "type": "number", "description": "Max wait time in ms. Default: 30000, max: 600000" }
1344        }),
1345        required: Some(vec!["task_id".to_string()]),
1346    }
1347}
1348
1349// Cron tool schemas
1350fn cron_create_schema() -> ToolInputSchema {
1351    ToolInputSchema {
1352        schema_type: "object".to_string(),
1353        properties: serde_json::json!({
1354            "cron": { "type": "string", "description": "5-field cron expression" },
1355            "prompt": { "type": "string", "description": "The prompt to execute" },
1356            "recurring": { "type": "boolean", "description": "true = repeat, false = one-shot" },
1357            "durable": { "type": "boolean", "description": "true = persist across restarts" }
1358        }),
1359        required: Some(vec!["cron".to_string(), "prompt".to_string()]),
1360    }
1361}
1362
1363fn cron_delete_schema() -> ToolInputSchema {
1364    ToolInputSchema {
1365        schema_type: "object".to_string(),
1366        properties: serde_json::json!({
1367            "id": { "type": "string", "description": "Job ID returned by CronCreate" }
1368        }),
1369        required: Some(vec!["id".to_string()]),
1370    }
1371}
1372
1373fn cron_list_schema() -> ToolInputSchema {
1374    ToolInputSchema {
1375        schema_type: "object".to_string(),
1376        properties: serde_json::json!({}),
1377        required: None,
1378    }
1379}
1380
1381// Config schema
1382fn config_schema() -> ToolInputSchema {
1383    ToolInputSchema {
1384        schema_type: "object".to_string(),
1385        properties: serde_json::json!({
1386            "action": { "type": "string", "enum": ["get", "set", "list"], "description": "Action to perform" },
1387            "key": { "type": "string", "description": "Configuration key" },
1388            "value": { "type": "string", "description": "Configuration value" }
1389        }),
1390        required: Some(vec!["action".to_string()]),
1391    }
1392}
1393
1394// TodoWrite schema
1395fn todo_write_schema() -> ToolInputSchema {
1396    ToolInputSchema {
1397        schema_type: "object".to_string(),
1398        properties: serde_json::json!({
1399            "todos": { "type": "array", "items": serde_json::json!({}), "description": "List of todo items" }
1400        }),
1401        required: Some(vec!["todos".to_string()]),
1402    }
1403}
1404
1405// Skill schema
1406fn skill_schema() -> ToolInputSchema {
1407    ToolInputSchema {
1408        schema_type: "object".to_string(),
1409        properties: serde_json::json!({
1410            "skill": { "type": "string", "description": "The name of the skill to invoke" }
1411        }),
1412        required: Some(vec!["skill".to_string()]),
1413    }
1414}
1415
1416// LSP tool schema
1417fn lsp_schema() -> ToolInputSchema {
1418    ToolInputSchema {
1419        schema_type: "object".to_string(),
1420        properties: serde_json::json!({
1421            "operation": {
1422                "type": "string",
1423                "enum": ["goToDefinition", "findReferences", "hover", "documentSymbol", "workspaceSymbol", "goToImplementation", "prepareCallHierarchy", "incomingCalls", "outgoingCalls"],
1424                "description": "The LSP operation to perform"
1425            },
1426            "filePath": { "type": "string", "description": "The file to operate on" },
1427            "line": { "type": "integer", "description": "Line number (1-based)" },
1428            "character": { "type": "integer", "description": "Character offset (1-based)" }
1429        }),
1430        required: Some(vec!["operation".to_string(), "filePath".to_string()]),
1431    }
1432}
1433
1434// RemoteTrigger tool schema
1435fn remote_trigger_schema() -> ToolInputSchema {
1436    ToolInputSchema {
1437        schema_type: "object".to_string(),
1438        properties: serde_json::json!({
1439            "action": { "type": "string", "enum": ["list", "get", "create", "update", "run"], "description": "The action to perform" },
1440            "trigger_id": { "type": "string", "description": "Required for get, update, and run" },
1441            "body": { "type": "object", "description": "JSON body for create and update" }
1442        }),
1443        required: Some(vec!["action".to_string()]),
1444    }
1445}
1446
1447// ListMcpResourcesTool schema
1448fn list_mcp_resources_schema() -> ToolInputSchema {
1449    ToolInputSchema {
1450        schema_type: "object".to_string(),
1451        properties: serde_json::json!({
1452            "server": { "type": "string", "description": "Optional server name to filter resources by" }
1453        }),
1454        required: None,
1455    }
1456}
1457
1458// ReadMcpResourceTool schema
1459fn read_mcp_resource_schema() -> ToolInputSchema {
1460    ToolInputSchema {
1461        schema_type: "object".to_string(),
1462        properties: serde_json::json!({
1463            "server": { "type": "string", "description": "The MCP server name" },
1464            "uri": { "type": "string", "description": "The resource URI to read" }
1465        }),
1466        required: Some(vec!["server".to_string(), "uri".to_string()]),
1467    }
1468}
1469
1470// MCPTool schema (generic MCP tool execution)
1471fn mcp_tool_schema() -> ToolInputSchema {
1472    ToolInputSchema {
1473        schema_type: "object".to_string(),
1474        properties: serde_json::json!({
1475            "server": {
1476                "type": "string",
1477                "description": "The MCP server name"
1478            },
1479            "tool": {
1480                "type": "string",
1481                "description": "The tool name to execute on the server"
1482            },
1483            "arguments": {
1484                "type": "object",
1485                "description": "Arguments to pass to the MCP tool"
1486            }
1487        }),
1488        required: Some(vec!["server".to_string(), "tool".to_string()]),
1489    }
1490}
1491
1492// McpAuthTool schema (authenticate MCP server)
1493fn mcp_auth_schema() -> ToolInputSchema {
1494    ToolInputSchema {
1495        schema_type: "object".to_string(),
1496        properties: serde_json::json!({
1497            "server": {
1498                "type": "string",
1499                "description": "The MCP server name to authenticate"
1500            }
1501        }),
1502        required: Some(vec!["server".to_string()]),
1503    }
1504}
1505
1506// OverflowTest tool schema
1507fn overflow_test_schema() -> ToolInputSchema {
1508    ToolInputSchema {
1509        schema_type: "object".to_string(),
1510        properties: serde_json::json!({}),
1511        required: None,
1512    }
1513}
1514
1515// ReviewArtifact tool schema
1516fn review_artifact_schema() -> ToolInputSchema {
1517    ToolInputSchema {
1518        schema_type: "object".to_string(),
1519        properties: serde_json::json!({}),
1520        required: None,
1521    }
1522}
1523
1524// Workflow tool schema
1525fn workflow_schema() -> ToolInputSchema {
1526    ToolInputSchema {
1527        schema_type: "object".to_string(),
1528        properties: serde_json::json!({}),
1529        required: None,
1530    }
1531}
1532
1533// Snip tool schema
1534fn snip_schema() -> ToolInputSchema {
1535    ToolInputSchema {
1536        schema_type: "object".to_string(),
1537        properties: serde_json::json!({}),
1538        required: None,
1539    }
1540}
1541
1542// DiscoverSkills tool schema
1543fn discover_skills_schema() -> ToolInputSchema {
1544    ToolInputSchema {
1545        schema_type: "object".to_string(),
1546        properties: serde_json::json!({}),
1547        required: None,
1548    }
1549}
1550
1551// TerminalCapture tool schema
1552fn terminal_capture_schema() -> ToolInputSchema {
1553    ToolInputSchema {
1554        schema_type: "object".to_string(),
1555        properties: serde_json::json!({}),
1556        required: None,
1557    }
1558}
1559
1560pub fn get_all_base_tools() -> Vec<ToolDefinition> {
1561    ALL_TOOLS.iter().map(|f| f.2()).collect()
1562}
1563
1564pub fn filter_tools(
1565    tools: Vec<ToolDefinition>,
1566    allowed: Option<Vec<String>>,
1567    disallowed: Option<Vec<String>>,
1568) -> Vec<ToolDefinition> {
1569    let mut result = tools;
1570    if let Some(allowed) = allowed {
1571        let allowed_set: std::collections::HashSet<_> = allowed.into_iter().collect();
1572        result.retain(|t| allowed_set.contains(&t.name));
1573    }
1574    if let Some(disallowed) = disallowed {
1575        let disallowed_set: std::collections::HashSet<_> = disallowed.into_iter().collect();
1576        result.retain(|t| !disallowed_set.contains(&t.name));
1577    }
1578    result
1579}
1580
1581// --------------------------------------------------------------------------
1582// Tool Helper Functions (translated from Tool.ts)
1583// --------------------------------------------------------------------------
1584
1585/// Tool with metadata for matching
1586#[derive(Debug, Clone)]
1587pub struct ToolWithMetadata {
1588    pub name: String,
1589    pub aliases: Option<Vec<String>>,
1590}
1591
1592/// Checks if a tool matches the given name (primary name or alias)
1593pub fn tool_matches_name(tool: &ToolWithMetadata, name: &str) -> bool {
1594    tool.name == name
1595        || tool
1596            .aliases
1597            .as_ref()
1598            .map_or(false, |a| a.contains(&name.to_string()))
1599}
1600
1601/// Finds a tool by name or alias from a list of tools
1602pub fn find_tool_by_name<'a>(
1603    tools: &'a [ToolDefinition],
1604    name: &str,
1605) -> Option<&'a ToolDefinition> {
1606    tools.iter().find(|t| t.name == name)
1607}
1608
1609/// Tool definition with optional fields (like ToolDef in TypeScript)
1610pub struct PartialToolDefinition {
1611    pub name: String,
1612    pub description: Option<String>,
1613    pub input_schema: Option<ToolInputSchema>,
1614    pub aliases: Option<Vec<String>>,
1615    pub search_hint: Option<String>,
1616    pub max_result_size_chars: Option<usize>,
1617    pub should_defer: Option<bool>,
1618    pub always_load: Option<bool>,
1619    pub is_enabled: Option<Box<dyn Fn() -> bool + Send + Sync>>,
1620    pub is_concurrency_safe: Option<Box<dyn Fn(&serde_json::Value) -> bool + Send + Sync>>,
1621    pub is_read_only: Option<Box<dyn Fn(&serde_json::Value) -> bool + Send + Sync>>,
1622    pub is_destructive: Option<Box<dyn Fn(&serde_json::Value) -> bool + Send + Sync>>,
1623    pub interrupt_behavior: Option<Box<dyn Fn() -> InterruptBehavior + Send + Sync>>,
1624    pub is_search_or_read_command:
1625        Option<Box<dyn Fn(&serde_json::Value) -> SearchOrReadCommand + Send + Sync>>,
1626    pub is_open_world: Option<Box<dyn Fn(&serde_json::Value) -> bool + Send + Sync>>,
1627    pub requires_user_interaction: Option<Box<dyn Fn() -> bool + Send + Sync>>,
1628    pub is_mcp: Option<bool>,
1629    pub is_lsp: Option<bool>,
1630    pub user_facing_name: Option<Box<dyn Fn(Option<&serde_json::Value>) -> String + Send + Sync>>,
1631}
1632
1633impl Default for PartialToolDefinition {
1634    fn default() -> Self {
1635        Self {
1636            name: String::new(),
1637            description: None,
1638            input_schema: None,
1639            aliases: None,
1640            user_facing_name: None,
1641            search_hint: None,
1642            max_result_size_chars: None,
1643            should_defer: None,
1644            always_load: None,
1645            is_enabled: None,
1646            is_concurrency_safe: None,
1647            is_read_only: None,
1648            is_destructive: None,
1649            interrupt_behavior: None,
1650            is_search_or_read_command: None,
1651            is_open_world: None,
1652            requires_user_interaction: None,
1653            is_mcp: None,
1654            is_lsp: None,
1655        }
1656    }
1657}
1658
1659/// Interrupt behavior when user submits new message while tool is running
1660#[derive(Debug, Clone, Copy, PartialEq)]
1661pub enum InterruptBehavior {
1662    /// Stop the tool and discard its result
1663    Cancel,
1664    /// Keep running; the new message waits
1665    Block,
1666}
1667
1668impl Default for InterruptBehavior {
1669    fn default() -> Self {
1670        InterruptBehavior::Block
1671    }
1672}
1673
1674/// Search or read command result
1675#[derive(Debug, Clone, Default)]
1676pub struct SearchOrReadCommand {
1677    pub is_search: bool,
1678    pub is_read: bool,
1679    pub is_list: Option<bool>,
1680}
1681
1682/// Build a complete `ToolDefinition` from a partial definition
1683pub fn build_tool(def: PartialToolDefinition) -> ToolDefinition {
1684    ToolDefinition {
1685        name: def.name.clone(),
1686        description: def.description.unwrap_or_default(),
1687        input_schema: def.input_schema.unwrap_or_default(),
1688        annotations: Some(ToolAnnotations {
1689            read_only: Some(
1690                def.is_read_only
1691                    .map_or(false, |f| f(&serde_json::json!({}))),
1692            ),
1693            destructive: Some(
1694                def.is_destructive
1695                    .as_ref()
1696                    .map_or(false, |f| f(&serde_json::json!({}))),
1697            ),
1698            concurrency_safe: Some(
1699                def.is_concurrency_safe
1700                    .as_ref()
1701                    .map_or(false, |f| f(&serde_json::json!({}))),
1702            ),
1703            open_world: None,
1704            idempotent: None,
1705        }),
1706        should_defer: None,
1707        always_load: None,
1708        is_mcp: None,
1709        search_hint: def.search_hint,
1710        aliases: def.aliases,
1711        user_facing_name: def.user_facing_name.map(|f| f(None)),
1712        interrupt_behavior: None,
1713    }
1714}