1use crate::types::*;
3use std::future::Future;
4
5pub use crate::types::{ToolDefinition, ToolInputSchema};
6
7pub type ToolFuture =
10 std::pin::Pin<Box<dyn Future<Output = Result<ToolResult, crate::error::AgentError>> + Send>>;
11
12pub trait Tool {
13 fn name(&self) -> &str;
14 fn description(&self) -> &str;
15 fn input_schema(&self) -> ToolInputSchema;
16 fn execute(
17 &self,
18 input: serde_json::Value,
19 context: &ToolContext,
20 ) -> impl Future<Output = Result<ToolResult, crate::error::AgentError>> + Send;
21}
22
23fn bash_schema() -> ToolInputSchema {
24 ToolInputSchema {
25 schema_type: "object".to_string(),
26 properties: serde_json::json!({
27 "command": {
28 "type": "string",
29 "description": "The shell command to execute"
30 }
31 }),
32 required: Some(vec!["command".to_string()]),
33 }
34}
35
36fn file_read_schema() -> ToolInputSchema {
37 ToolInputSchema {
38 schema_type: "object".to_string(),
39 properties: serde_json::json!({
40 "path": {
41 "type": "string",
42 "description": "The file path to read"
43 }
44 }),
45 required: Some(vec!["path".to_string()]),
46 }
47}
48
49fn file_write_schema() -> ToolInputSchema {
50 ToolInputSchema {
51 schema_type: "object".to_string(),
52 properties: serde_json::json!({
53 "path": {
54 "type": "string",
55 "description": "The file path to write to"
56 },
57 "content": {
58 "type": "string",
59 "description": "The content to write"
60 }
61 }),
62 required: Some(vec!["path".to_string(), "content".to_string()]),
63 }
64}
65
66fn glob_schema() -> ToolInputSchema {
67 ToolInputSchema {
68 schema_type: "object".to_string(),
69 properties: serde_json::json!({
70 "pattern": {
71 "type": "string",
72 "description": "The glob pattern to match"
73 }
74 }),
75 required: Some(vec!["pattern".to_string()]),
76 }
77}
78
79fn grep_schema() -> ToolInputSchema {
80 ToolInputSchema {
81 schema_type: "object".to_string(),
82 properties: serde_json::json!({
83 "pattern": {
84 "type": "string",
85 "description": "The regex pattern to search for"
86 },
87 "path": {
88 "type": "string",
89 "description": "The file or directory to search in"
90 }
91 }),
92 required: Some(vec!["pattern".to_string()]),
93 }
94}
95
96fn file_edit_schema() -> ToolInputSchema {
97 ToolInputSchema {
98 schema_type: "object".to_string(),
99 properties: serde_json::json!({
100 "file_path": {
101 "type": "string",
102 "description": "The absolute path to the file to modify"
103 },
104 "old_string": {
105 "type": "string",
106 "description": "The exact text to find and replace"
107 },
108 "new_string": {
109 "type": "string",
110 "description": "The replacement text"
111 },
112 "replace_all": {
113 "type": "boolean",
114 "description": "Replace all occurrences (default false)"
115 }
116 }),
117 required: Some(vec![
118 "file_path".to_string(),
119 "old_string".to_string(),
120 "new_string".to_string(),
121 ]),
122 }
123}
124
125fn notebook_edit_schema() -> ToolInputSchema {
126 ToolInputSchema {
127 schema_type: "object".to_string(),
128 properties: serde_json::json!({
129 "file_path": {
130 "type": "string",
131 "description": "Path to the .ipynb file"
132 },
133 "command": {
134 "type": "string",
135 "enum": ["insert", "replace", "delete"],
136 "description": "The edit operation to perform"
137 },
138 "cell_number": {
139 "type": "number",
140 "description": "Cell index (0-based) to operate on"
141 },
142 "cell_type": {
143 "type": "string",
144 "enum": ["code", "markdown"],
145 "description": "Type of cell (for insert/replace)"
146 },
147 "source": {
148 "type": "string",
149 "description": "Cell content (for insert/replace)"
150 }
151 }),
152 required: Some(vec![
153 "file_path".to_string(),
154 "command".to_string(),
155 "cell_number".to_string(),
156 ]),
157 }
158}
159
160fn web_fetch_schema() -> ToolInputSchema {
161 ToolInputSchema {
162 schema_type: "object".to_string(),
163 properties: serde_json::json!({
164 "url": {
165 "type": "string",
166 "description": "The URL to fetch content from"
167 },
168 "headers": {
169 "type": "object",
170 "description": "Optional HTTP headers",
171 "additionalProperties": {
172 "type": "string"
173 }
174 }
175 }),
176 required: Some(vec!["url".to_string()]),
177 }
178}
179
180fn web_search_schema() -> ToolInputSchema {
181 ToolInputSchema {
182 schema_type: "object".to_string(),
183 properties: serde_json::json!({
184 "query": {
185 "type": "string",
186 "description": "The search query"
187 },
188 "num_results": {
189 "type": "number",
190 "description": "Number of results to return (default: 5)"
191 }
192 }),
193 required: Some(vec!["query".to_string()]),
194 }
195}
196
197const ALL_TOOLS: &[(&str, &str, fn() -> ToolDefinition)] = &[
198 ("Bash", "Execute shell commands", || ToolDefinition {
199 name: "Bash".to_string(),
200 description: "Execute shell commands".to_string(),
201 input_schema: bash_schema(),
202 annotations: None,
203 }),
204 ("FileRead", "Read files from filesystem", || {
205 ToolDefinition {
206 name: "FileRead".to_string(),
207 description: "Read files from filesystem".to_string(),
208 input_schema: file_read_schema(),
209 annotations: None,
210 }
211 }),
212 ("FileWrite", "Write content to files", || ToolDefinition {
213 name: "FileWrite".to_string(),
214 description: "Write content to files".to_string(),
215 input_schema: file_write_schema(),
216 annotations: None,
217 }),
218 ("Glob", "Find files by pattern", || ToolDefinition {
219 name: "Glob".to_string(),
220 description: "Find files by pattern".to_string(),
221 input_schema: glob_schema(),
222 annotations: None,
223 }),
224 ("Grep", "Search file contents", || ToolDefinition {
225 name: "Grep".to_string(),
226 description: "Search file contents".to_string(),
227 input_schema: grep_schema(),
228 annotations: None,
229 }),
230 (
231 "FileEdit",
232 "Edit files by performing exact string replacements",
233 || ToolDefinition {
234 name: "FileEdit".to_string(),
235 description: "Edit files by performing exact string replacements".to_string(),
236 input_schema: file_edit_schema(),
237 annotations: None,
238 },
239 ),
240 (
241 "NotebookEdit",
242 "Edit Jupyter notebook (.ipynb) cells",
243 || ToolDefinition {
244 name: "NotebookEdit".to_string(),
245 description: "Edit Jupyter notebook (.ipynb) cells".to_string(),
246 input_schema: notebook_edit_schema(),
247 annotations: None,
248 },
249 ),
250 (
251 "WebFetch",
252 "Fetch content from a URL and return it as text",
253 || {
254 ToolDefinition {
255 name: "WebFetch".to_string(),
256 description: "Fetch content from a URL and return it as text. Supports HTML pages, JSON APIs, and plain text. Strips HTML tags for readability.".to_string(),
257 input_schema: web_fetch_schema(),
258 annotations: None,
259 }
260 },
261 ),
262 ("WebSearch", "Search the web for information", || {
263 ToolDefinition {
264 name: "WebSearch".to_string(),
265 description: "Search the web for information. Returns search results with titles, URLs, and snippets.".to_string(),
266 input_schema: web_search_schema(),
267 annotations: None,
268 }
269 }),
270 (
271 "Agent",
272 "Launch a new agent to handle complex multi-step tasks",
273 || {
274 ToolDefinition {
275 name: "Agent".to_string(),
276 description: "Launch a new agent to handle complex, multi-step tasks autonomously. Use this tool to spawn specialized subagents.".to_string(),
277 input_schema: agent_schema(),
278 annotations: None,
279 }
280 },
281 ),
282 ("TaskCreate", "Create a new task in the task list", || {
283 ToolDefinition {
284 name: "TaskCreate".to_string(),
285 description: "Create a new task in the task list".to_string(),
286 input_schema: task_create_schema(),
287 annotations: None,
288 }
289 }),
290 ("TaskList", "List all tasks in the task list", || {
291 ToolDefinition {
292 name: "TaskList".to_string(),
293 description: "List all tasks in the task list".to_string(),
294 input_schema: task_list_schema(),
295 annotations: None,
296 }
297 }),
298 ("TaskUpdate", "Update an existing task", || ToolDefinition {
299 name: "TaskUpdate".to_string(),
300 description: "Update an existing task's status or details".to_string(),
301 input_schema: task_update_schema(),
302 annotations: None,
303 }),
304 ("TaskGet", "Get details of a specific task", || {
305 ToolDefinition {
306 name: "TaskGet".to_string(),
307 description: "Get details of a specific task by ID".to_string(),
308 input_schema: task_get_schema(),
309 annotations: None,
310 }
311 }),
312 (
313 "TeamCreate",
314 "Create a team of agents for parallel work",
315 || ToolDefinition {
316 name: "TeamCreate".to_string(),
317 description: "Create a team of agents that can work in parallel".to_string(),
318 input_schema: team_create_schema(),
319 annotations: None,
320 },
321 ),
322 ("TeamDelete", "Delete a team of agents", || ToolDefinition {
323 name: "TeamDelete".to_string(),
324 description: "Delete a previously created team".to_string(),
325 input_schema: team_delete_schema(),
326 annotations: None,
327 }),
328 ("SendMessage", "Send a message to another agent", || {
329 ToolDefinition {
330 name: "SendMessage".to_string(),
331 description: "Send a message to another agent".to_string(),
332 input_schema: send_message_schema(),
333 annotations: None,
334 }
335 }),
336 ("EnterWorktree", "Create and enter a git worktree", || {
337 ToolDefinition {
338 name: "EnterWorktree".to_string(),
339 description: "Create and enter a git worktree for isolated work".to_string(),
340 input_schema: enter_worktree_schema(),
341 annotations: None,
342 }
343 }),
344 (
345 "ExitWorktree",
346 "Exit a worktree and return to original directory",
347 || ToolDefinition {
348 name: "ExitWorktree".to_string(),
349 description: "Exit a worktree and return to the original working directory".to_string(),
350 input_schema: exit_worktree_schema(),
351 annotations: None,
352 },
353 ),
354 ("EnterPlanMode", "Enter structured planning mode", || {
355 ToolDefinition {
356 name: "EnterPlanMode".to_string(),
357 description: "Enter structured planning mode to explore and design implementation"
358 .to_string(),
359 input_schema: enter_plan_mode_schema(),
360 annotations: None,
361 }
362 }),
363 ("ExitPlanMode", "Exit planning mode", || ToolDefinition {
364 name: "ExitPlanMode".to_string(),
365 description: "Exit planning mode and present the plan for approval".to_string(),
366 input_schema: exit_plan_mode_schema(),
367 annotations: None,
368 }),
369 (
370 "AskUserQuestion",
371 "Ask the user a question with multiple choice options",
372 || ToolDefinition {
373 name: "AskUserQuestion".to_string(),
374 description: "Ask the user a question with multiple choice options".to_string(),
375 input_schema: ask_user_question_schema(),
376 annotations: None,
377 },
378 ),
379 ("ToolSearch", "Search for available tools", || {
380 ToolDefinition {
381 name: "ToolSearch".to_string(),
382 description: "Search for available tools by name or description".to_string(),
383 input_schema: tool_search_schema(),
384 annotations: None,
385 }
386 }),
387 ("CronCreate", "Create a scheduled task", || ToolDefinition {
388 name: "CronCreate".to_string(),
389 description: "Create a scheduled task that runs on a cron schedule".to_string(),
390 input_schema: cron_create_schema(),
391 annotations: None,
392 }),
393 ("CronDelete", "Delete a scheduled task", || ToolDefinition {
394 name: "CronDelete".to_string(),
395 description: "Delete a previously created scheduled task".to_string(),
396 input_schema: cron_delete_schema(),
397 annotations: None,
398 }),
399 ("CronList", "List all scheduled tasks", || ToolDefinition {
400 name: "CronList".to_string(),
401 description: "List all scheduled tasks".to_string(),
402 input_schema: cron_list_schema(),
403 annotations: None,
404 }),
405 ("Config", "Read or update configuration", || {
406 ToolDefinition {
407 name: "Config".to_string(),
408 description: "Read or update dynamic configuration".to_string(),
409 input_schema: config_schema(),
410 annotations: None,
411 }
412 }),
413 ("TodoWrite", "Write todo list items", || ToolDefinition {
414 name: "TodoWrite".to_string(),
415 description: "Write todo list items for the session".to_string(),
416 input_schema: todo_write_schema(),
417 annotations: None,
418 }),
419 ("Skill", "Invoke a skill by name", || ToolDefinition {
420 name: "Skill".to_string(),
421 description: "Invoke a skill by name to execute its commands".to_string(),
422 input_schema: skill_schema(),
423 annotations: None,
424 }),
425];
426
427fn agent_schema() -> ToolInputSchema {
428 ToolInputSchema {
429 schema_type: "object".to_string(),
430 properties: serde_json::json!({
431 "description": {
432 "type": "string",
433 "description": "A short description (3-5 words) summarizing what the agent will do"
434 },
435 "subagent_type": {
436 "type": "string",
437 "description": "The type of subagent to use. If omitted, uses the general-purpose agent."
438 },
439 "prompt": {
440 "type": "string",
441 "description": "The task prompt for the subagent to execute"
442 },
443 "model": {
444 "type": "string",
445 "description": "Optional model override for this subagent"
446 },
447 "max_turns": {
448 "type": "number",
449 "description": "Maximum number of turns for this subagent (default: 10)"
450 },
451 "run_in_background": {
452 "type": "boolean",
453 "description": "Whether to run the agent in the background (default: false)"
454 },
455 "isolation": {
456 "type": "string",
457 "enum": ["worktree", "remote"],
458 "description": "Isolation mode: 'worktree' for git worktree, 'remote' for remote CCR"
459 }
460 }),
461 required: Some(vec!["description".to_string(), "prompt".to_string()]),
462 }
463}
464
465fn task_create_schema() -> ToolInputSchema {
467 ToolInputSchema {
468 schema_type: "object".to_string(),
469 properties: serde_json::json!({
470 "subject": { "type": "string", "description": "A brief title for the task" },
471 "description": { "type": "string", "description": "What needs to be done" },
472 "activeForm": { "type": "string", "description": "Spinner text when in_progress" }
473 }),
474 required: Some(vec!["subject".to_string(), "description".to_string()]),
475 }
476}
477
478fn task_list_schema() -> ToolInputSchema {
479 ToolInputSchema {
480 schema_type: "object".to_string(),
481 properties: serde_json::json!({}),
482 required: None,
483 }
484}
485
486fn task_update_schema() -> ToolInputSchema {
487 ToolInputSchema {
488 schema_type: "object".to_string(),
489 properties: serde_json::json!({
490 "taskId": { "type": "string", "description": "The ID of the task to update" },
491 "subject": { "type": "string", "description": "New subject for the task" },
492 "description": { "type": "string", "description": "New description" },
493 "status": { "type": "string", "enum": ["pending", "in_progress", "completed", "deleted"], "description": "New status" },
494 "activeForm": { "type": "string", "description": "New spinner text" }
495 }),
496 required: Some(vec!["taskId".to_string()]),
497 }
498}
499
500fn task_get_schema() -> ToolInputSchema {
501 ToolInputSchema {
502 schema_type: "object".to_string(),
503 properties: serde_json::json!({
504 "taskId": { "type": "string", "description": "The ID of the task to retrieve" }
505 }),
506 required: Some(vec!["taskId".to_string()]),
507 }
508}
509
510fn team_create_schema() -> ToolInputSchema {
512 ToolInputSchema {
513 schema_type: "object".to_string(),
514 properties: serde_json::json!({
515 "name": { "type": "string", "description": "Name of the team" },
516 "description": { "type": "string", "description": "Description of what the team does" },
517 "agents": { "type": "array", "items": serde_json::json!({}), "description": "List of agents in the team" }
518 }),
519 required: Some(vec!["name".to_string()]),
520 }
521}
522
523fn team_delete_schema() -> ToolInputSchema {
524 ToolInputSchema {
525 schema_type: "object".to_string(),
526 properties: serde_json::json!({
527 "name": { "type": "string", "description": "Name of the team to delete" }
528 }),
529 required: Some(vec!["name".to_string()]),
530 }
531}
532
533fn send_message_schema() -> ToolInputSchema {
534 ToolInputSchema {
535 schema_type: "object".to_string(),
536 properties: serde_json::json!({
537 "to": { "type": "string", "description": "Agent name to send message to" },
538 "message": { "type": "string", "description": "Message content" }
539 }),
540 required: Some(vec!["to".to_string(), "message".to_string()]),
541 }
542}
543
544fn enter_worktree_schema() -> ToolInputSchema {
546 ToolInputSchema {
547 schema_type: "object".to_string(),
548 properties: serde_json::json!({
549 "name": { "type": "string", "description": "Optional name for the worktree" }
550 }),
551 required: None,
552 }
553}
554
555fn exit_worktree_schema() -> ToolInputSchema {
556 ToolInputSchema {
557 schema_type: "object".to_string(),
558 properties: serde_json::json!({
559 "action": { "type": "string", "enum": ["keep", "remove"], "description": "What to do with the worktree" },
560 "discardChanges": { "type": "boolean", "description": "Discard uncommitted changes before removing" }
561 }),
562 required: None,
563 }
564}
565
566fn enter_plan_mode_schema() -> ToolInputSchema {
568 ToolInputSchema {
569 schema_type: "object".to_string(),
570 properties: serde_json::json!({
571 "allowedPrompts": { "type": "array", "items": { "type": "string" }, "description": "Prompt-based permissions" }
572 }),
573 required: None,
574 }
575}
576
577fn exit_plan_mode_schema() -> ToolInputSchema {
578 ToolInputSchema {
579 schema_type: "object".to_string(),
580 properties: serde_json::json!({}),
581 required: None,
582 }
583}
584
585fn ask_user_question_schema() -> ToolInputSchema {
587 ToolInputSchema {
588 schema_type: "object".to_string(),
589 properties: serde_json::json!({
590 "question": { "type": "string", "description": "The question to ask the user" },
591 "header": { "type": "string", "description": "Short label displayed as a chip/tag" },
592 "options": { "type": "array", "items": serde_json::json!({}), "description": "Available choices" },
593 "multiSelect": { "type": "boolean", "description": "Allow multiple answers" }
594 }),
595 required: Some(vec![
596 "question".to_string(),
597 "header".to_string(),
598 "options".to_string(),
599 ]),
600 }
601}
602
603fn tool_search_schema() -> ToolInputSchema {
605 ToolInputSchema {
606 schema_type: "object".to_string(),
607 properties: serde_json::json!({
608 "query": { "type": "string", "description": "Search query to find relevant tools" }
609 }),
610 required: Some(vec!["query".to_string()]),
611 }
612}
613
614fn cron_create_schema() -> ToolInputSchema {
616 ToolInputSchema {
617 schema_type: "object".to_string(),
618 properties: serde_json::json!({
619 "cron": { "type": "string", "description": "5-field cron expression" },
620 "prompt": { "type": "string", "description": "The prompt to execute" },
621 "recurring": { "type": "boolean", "description": "true = repeat, false = one-shot" },
622 "durable": { "type": "boolean", "description": "true = persist across restarts" }
623 }),
624 required: Some(vec!["cron".to_string(), "prompt".to_string()]),
625 }
626}
627
628fn cron_delete_schema() -> ToolInputSchema {
629 ToolInputSchema {
630 schema_type: "object".to_string(),
631 properties: serde_json::json!({
632 "id": { "type": "string", "description": "Job ID returned by CronCreate" }
633 }),
634 required: Some(vec!["id".to_string()]),
635 }
636}
637
638fn cron_list_schema() -> ToolInputSchema {
639 ToolInputSchema {
640 schema_type: "object".to_string(),
641 properties: serde_json::json!({}),
642 required: None,
643 }
644}
645
646fn config_schema() -> ToolInputSchema {
648 ToolInputSchema {
649 schema_type: "object".to_string(),
650 properties: serde_json::json!({
651 "action": { "type": "string", "enum": ["get", "set", "list"], "description": "Action to perform" },
652 "key": { "type": "string", "description": "Configuration key" },
653 "value": { "type": "string", "description": "Configuration value" }
654 }),
655 required: Some(vec!["action".to_string()]),
656 }
657}
658
659fn todo_write_schema() -> ToolInputSchema {
661 ToolInputSchema {
662 schema_type: "object".to_string(),
663 properties: serde_json::json!({
664 "todos": { "type": "array", "items": serde_json::json!({}), "description": "List of todo items" }
665 }),
666 required: Some(vec!["todos".to_string()]),
667 }
668}
669
670fn skill_schema() -> ToolInputSchema {
672 ToolInputSchema {
673 schema_type: "object".to_string(),
674 properties: serde_json::json!({
675 "skill": { "type": "string", "description": "The name of the skill to invoke" }
676 }),
677 required: Some(vec!["skill".to_string()]),
678 }
679}
680
681pub fn get_all_base_tools() -> Vec<ToolDefinition> {
682 ALL_TOOLS.iter().map(|f| f.2()).collect()
683}
684
685pub fn filter_tools(
686 tools: Vec<ToolDefinition>,
687 allowed: Option<Vec<String>>,
688 disallowed: Option<Vec<String>>,
689) -> Vec<ToolDefinition> {
690 let mut result = tools;
691 if let Some(allowed) = allowed {
692 let allowed_set: std::collections::HashSet<_> = allowed.into_iter().collect();
693 result.retain(|t| allowed_set.contains(&t.name));
694 }
695 if let Some(disallowed) = disallowed {
696 let disallowed_set: std::collections::HashSet<_> = disallowed.into_iter().collect();
697 result.retain(|t| !disallowed_set.contains(&t.name));
698 }
699 result
700}
701
702#[derive(Debug, Clone)]
708pub struct ToolWithMetadata {
709 pub name: String,
710 pub aliases: Option<Vec<String>>,
711}
712
713pub fn tool_matches_name(tool: &ToolWithMetadata, name: &str) -> bool {
715 tool.name == name
716 || tool
717 .aliases
718 .as_ref()
719 .map_or(false, |a| a.contains(&name.to_string()))
720}
721
722pub fn find_tool_by_name<'a>(
724 tools: &'a [ToolDefinition],
725 name: &str,
726) -> Option<&'a ToolDefinition> {
727 tools.iter().find(|t| t.name == name)
728}
729
730pub struct PartialToolDefinition {
732 pub name: String,
733 pub description: Option<String>,
734 pub input_schema: Option<ToolInputSchema>,
735 pub aliases: Option<Vec<String>>,
736 pub search_hint: Option<String>,
737 pub max_result_size_chars: Option<usize>,
738 pub should_defer: Option<bool>,
739 pub always_load: Option<bool>,
740 pub is_enabled: Option<Box<dyn Fn() -> bool + Send + Sync>>,
741 pub is_concurrency_safe: Option<Box<dyn Fn(&serde_json::Value) -> bool + Send + Sync>>,
742 pub is_read_only: Option<Box<dyn Fn(&serde_json::Value) -> bool + Send + Sync>>,
743 pub is_destructive: Option<Box<dyn Fn(&serde_json::Value) -> bool + Send + Sync>>,
744 pub interrupt_behavior: Option<Box<dyn Fn() -> InterruptBehavior + Send + Sync>>,
745 pub is_search_or_read_command:
746 Option<Box<dyn Fn(&serde_json::Value) -> SearchOrReadCommand + Send + Sync>>,
747 pub is_open_world: Option<Box<dyn Fn(&serde_json::Value) -> bool + Send + Sync>>,
748 pub requires_user_interaction: Option<Box<dyn Fn() -> bool + Send + Sync>>,
749 pub is_mcp: Option<bool>,
750 pub is_lsp: Option<bool>,
751 pub user_facing_name: Option<Box<dyn Fn(Option<&serde_json::Value>) -> String + Send + Sync>>,
752}
753
754impl Default for PartialToolDefinition {
755 fn default() -> Self {
756 Self {
757 name: String::new(),
758 description: None,
759 input_schema: None,
760 aliases: None,
761 search_hint: None,
762 max_result_size_chars: None,
763 should_defer: None,
764 always_load: None,
765 is_enabled: None,
766 is_concurrency_safe: None,
767 is_read_only: None,
768 is_destructive: None,
769 interrupt_behavior: None,
770 is_search_or_read_command: None,
771 is_open_world: None,
772 requires_user_interaction: None,
773 is_mcp: None,
774 is_lsp: None,
775 user_facing_name: None,
776 }
777 }
778}
779
780#[derive(Debug, Clone, Copy, PartialEq)]
782pub enum InterruptBehavior {
783 Cancel,
785 Block,
787}
788
789impl Default for InterruptBehavior {
790 fn default() -> Self {
791 InterruptBehavior::Block
792 }
793}
794
795#[derive(Debug, Clone, Default)]
797pub struct SearchOrReadCommand {
798 pub is_search: bool,
799 pub is_read: bool,
800 pub is_list: Option<bool>,
801}
802
803pub fn build_tool(def: PartialToolDefinition) -> ToolDefinition {
805 ToolDefinition {
806 name: def.name.clone(),
807 description: def.description.unwrap_or_default(),
808 input_schema: def.input_schema.unwrap_or_default(),
809 annotations: Some(ToolAnnotations {
810 read_only: Some(
811 def.is_read_only
812 .map_or(false, |f| f(&serde_json::json!({}))),
813 ),
814 destructive: Some(
815 def.is_destructive
816 .as_ref()
817 .map_or(false, |f| f(&serde_json::json!({}))),
818 ),
819 concurrency_safe: Some(
820 def.is_concurrency_safe
821 .as_ref()
822 .map_or(false, |f| f(&serde_json::json!({}))),
823 ),
824 open_world: None,
825 idempotent: None,
826 }),
827 }
828}