use crate::types::*;
use std::future::Future;
pub use crate::types::{ToolDefinition, ToolInputSchema};
pub trait Tool {
fn name(&self) -> &str;
fn description(&self) -> &str;
fn input_schema(&self) -> ToolInputSchema;
fn execute(
&self,
input: serde_json::Value,
context: &ToolContext,
) -> impl Future<Output = Result<ToolResult, crate::error::AgentError>> + Send;
}
fn bash_schema() -> ToolInputSchema {
ToolInputSchema {
schema_type: "object".to_string(),
properties: serde_json::json!({
"command": {
"type": "string",
"description": "The shell command to execute"
}
}),
required: Some(vec!["command".to_string()]),
}
}
fn file_read_schema() -> ToolInputSchema {
ToolInputSchema {
schema_type: "object".to_string(),
properties: serde_json::json!({
"path": {
"type": "string",
"description": "The file path to read"
}
}),
required: Some(vec!["path".to_string()]),
}
}
fn file_write_schema() -> ToolInputSchema {
ToolInputSchema {
schema_type: "object".to_string(),
properties: serde_json::json!({
"path": {
"type": "string",
"description": "The file path to write to"
},
"content": {
"type": "string",
"description": "The content to write"
}
}),
required: Some(vec!["path".to_string(), "content".to_string()]),
}
}
fn glob_schema() -> ToolInputSchema {
ToolInputSchema {
schema_type: "object".to_string(),
properties: serde_json::json!({
"pattern": {
"type": "string",
"description": "The glob pattern to match"
}
}),
required: Some(vec!["pattern".to_string()]),
}
}
fn grep_schema() -> ToolInputSchema {
ToolInputSchema {
schema_type: "object".to_string(),
properties: serde_json::json!({
"pattern": {
"type": "string",
"description": "The regex pattern to search for"
},
"path": {
"type": "string",
"description": "The file or directory to search in"
}
}),
required: Some(vec!["pattern".to_string()]),
}
}
fn file_edit_schema() -> ToolInputSchema {
ToolInputSchema {
schema_type: "object".to_string(),
properties: serde_json::json!({
"file_path": {
"type": "string",
"description": "The absolute path to the file to modify"
},
"old_string": {
"type": "string",
"description": "The exact text to find and replace"
},
"new_string": {
"type": "string",
"description": "The replacement text"
},
"replace_all": {
"type": "boolean",
"description": "Replace all occurrences (default false)"
}
}),
required: Some(vec!["file_path".to_string(), "old_string".to_string(), "new_string".to_string()]),
}
}
fn notebook_edit_schema() -> ToolInputSchema {
ToolInputSchema {
schema_type: "object".to_string(),
properties: serde_json::json!({
"file_path": {
"type": "string",
"description": "Path to the .ipynb file"
},
"command": {
"type": "string",
"enum": ["insert", "replace", "delete"],
"description": "The edit operation to perform"
},
"cell_number": {
"type": "number",
"description": "Cell index (0-based) to operate on"
},
"cell_type": {
"type": "string",
"enum": ["code", "markdown"],
"description": "Type of cell (for insert/replace)"
},
"source": {
"type": "string",
"description": "Cell content (for insert/replace)"
}
}),
required: Some(vec!["file_path".to_string(), "command".to_string(), "cell_number".to_string()]),
}
}
fn web_fetch_schema() -> ToolInputSchema {
ToolInputSchema {
schema_type: "object".to_string(),
properties: serde_json::json!({
"url": {
"type": "string",
"description": "The URL to fetch content from"
},
"headers": {
"type": "object",
"description": "Optional HTTP headers",
"additionalProperties": {
"type": "string"
}
}
}),
required: Some(vec!["url".to_string()]),
}
}
fn web_search_schema() -> ToolInputSchema {
ToolInputSchema {
schema_type: "object".to_string(),
properties: serde_json::json!({
"query": {
"type": "string",
"description": "The search query"
},
"num_results": {
"type": "number",
"description": "Number of results to return (default: 5)"
}
}),
required: Some(vec!["query".to_string()]),
}
}
const ALL_TOOLS: &[(&str, &str, fn() -> ToolDefinition)] = &[
("Bash", "Execute shell commands", || ToolDefinition {
name: "Bash".to_string(),
description: "Execute shell commands".to_string(),
input_schema: bash_schema(),
}),
("FileRead", "Read files from filesystem", || ToolDefinition {
name: "FileRead".to_string(),
description: "Read files from filesystem".to_string(),
input_schema: file_read_schema(),
}),
("FileWrite", "Write content to files", || ToolDefinition {
name: "FileWrite".to_string(),
description: "Write content to files".to_string(),
input_schema: file_write_schema(),
}),
("Glob", "Find files by pattern", || ToolDefinition {
name: "Glob".to_string(),
description: "Find files by pattern".to_string(),
input_schema: glob_schema(),
}),
("Grep", "Search file contents", || ToolDefinition {
name: "Grep".to_string(),
description: "Search file contents".to_string(),
input_schema: grep_schema(),
}),
("FileEdit", "Edit files by performing exact string replacements", || ToolDefinition {
name: "FileEdit".to_string(),
description: "Edit files by performing exact string replacements".to_string(),
input_schema: file_edit_schema(),
}),
("NotebookEdit", "Edit Jupyter notebook (.ipynb) cells", || ToolDefinition {
name: "NotebookEdit".to_string(),
description: "Edit Jupyter notebook (.ipynb) cells".to_string(),
input_schema: notebook_edit_schema(),
}),
("WebFetch", "Fetch content from a URL and return it as text", || ToolDefinition {
name: "WebFetch".to_string(),
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(),
input_schema: web_fetch_schema(),
}),
("WebSearch", "Search the web for information", || ToolDefinition {
name: "WebSearch".to_string(),
description: "Search the web for information. Returns search results with titles, URLs, and snippets.".to_string(),
input_schema: web_search_schema(),
}),
("Agent", "Launch a new agent to handle complex multi-step tasks", || ToolDefinition {
name: "Agent".to_string(),
description: "Launch a new agent to handle complex, multi-step tasks autonomously. Use this tool to spawn specialized subagents.".to_string(),
input_schema: agent_schema(),
}),
("TaskCreate", "Create a new task in the task list", || ToolDefinition {
name: "TaskCreate".to_string(),
description: "Create a new task in the task list".to_string(),
input_schema: task_create_schema(),
}),
("TaskList", "List all tasks in the task list", || ToolDefinition {
name: "TaskList".to_string(),
description: "List all tasks in the task list".to_string(),
input_schema: task_list_schema(),
}),
("TaskUpdate", "Update an existing task", || ToolDefinition {
name: "TaskUpdate".to_string(),
description: "Update an existing task's status or details".to_string(),
input_schema: task_update_schema(),
}),
("TaskGet", "Get details of a specific task", || ToolDefinition {
name: "TaskGet".to_string(),
description: "Get details of a specific task by ID".to_string(),
input_schema: task_get_schema(),
}),
("TeamCreate", "Create a team of agents for parallel work", || ToolDefinition {
name: "TeamCreate".to_string(),
description: "Create a team of agents that can work in parallel".to_string(),
input_schema: team_create_schema(),
}),
("TeamDelete", "Delete a team of agents", || ToolDefinition {
name: "TeamDelete".to_string(),
description: "Delete a previously created team".to_string(),
input_schema: team_delete_schema(),
}),
("SendMessage", "Send a message to another agent", || ToolDefinition {
name: "SendMessage".to_string(),
description: "Send a message to another agent".to_string(),
input_schema: send_message_schema(),
}),
("EnterWorktree", "Create and enter a git worktree", || ToolDefinition {
name: "EnterWorktree".to_string(),
description: "Create and enter a git worktree for isolated work".to_string(),
input_schema: enter_worktree_schema(),
}),
("ExitWorktree", "Exit a worktree and return to original directory", || ToolDefinition {
name: "ExitWorktree".to_string(),
description: "Exit a worktree and return to the original working directory".to_string(),
input_schema: exit_worktree_schema(),
}),
("EnterPlanMode", "Enter structured planning mode", || ToolDefinition {
name: "EnterPlanMode".to_string(),
description: "Enter structured planning mode to explore and design implementation".to_string(),
input_schema: enter_plan_mode_schema(),
}),
("ExitPlanMode", "Exit planning mode", || ToolDefinition {
name: "ExitPlanMode".to_string(),
description: "Exit planning mode and present the plan for approval".to_string(),
input_schema: exit_plan_mode_schema(),
}),
("AskUserQuestion", "Ask the user a question with multiple choice options", || ToolDefinition {
name: "AskUserQuestion".to_string(),
description: "Ask the user a question with multiple choice options".to_string(),
input_schema: ask_user_question_schema(),
}),
("ToolSearch", "Search for available tools", || ToolDefinition {
name: "ToolSearch".to_string(),
description: "Search for available tools by name or description".to_string(),
input_schema: tool_search_schema(),
}),
("CronCreate", "Create a scheduled task", || ToolDefinition {
name: "CronCreate".to_string(),
description: "Create a scheduled task that runs on a cron schedule".to_string(),
input_schema: cron_create_schema(),
}),
("CronDelete", "Delete a scheduled task", || ToolDefinition {
name: "CronDelete".to_string(),
description: "Delete a previously created scheduled task".to_string(),
input_schema: cron_delete_schema(),
}),
("CronList", "List all scheduled tasks", || ToolDefinition {
name: "CronList".to_string(),
description: "List all scheduled tasks".to_string(),
input_schema: cron_list_schema(),
}),
("Config", "Read or update configuration", || ToolDefinition {
name: "Config".to_string(),
description: "Read or update dynamic configuration".to_string(),
input_schema: config_schema(),
}),
("TodoWrite", "Write todo list items", || ToolDefinition {
name: "TodoWrite".to_string(),
description: "Write todo list items for the session".to_string(),
input_schema: todo_write_schema(),
}),
("Skill", "Invoke a skill by name", || ToolDefinition {
name: "Skill".to_string(),
description: "Invoke a skill by name to execute its commands".to_string(),
input_schema: skill_schema(),
}),
];
fn agent_schema() -> ToolInputSchema {
ToolInputSchema {
schema_type: "object".to_string(),
properties: serde_json::json!({
"description": {
"type": "string",
"description": "A short description (3-5 words) summarizing what the agent will do"
},
"subagent_type": {
"type": "string",
"description": "The type of subagent to use. If omitted, uses the general-purpose agent."
},
"prompt": {
"type": "string",
"description": "The task prompt for the subagent to execute"
},
"model": {
"type": "string",
"description": "Optional model override for this subagent"
},
"max_turns": {
"type": "number",
"description": "Maximum number of turns for this subagent (default: 10)"
},
"run_in_background": {
"type": "boolean",
"description": "Whether to run the agent in the background (default: false)"
},
"isolation": {
"type": "string",
"enum": ["worktree", "remote"],
"description": "Isolation mode: 'worktree' for git worktree, 'remote' for remote CCR"
}
}),
required: Some(vec!["description".to_string(), "prompt".to_string()]),
}
}
fn task_create_schema() -> ToolInputSchema {
ToolInputSchema {
schema_type: "object".to_string(),
properties: serde_json::json!({
"subject": { "type": "string", "description": "A brief title for the task" },
"description": { "type": "string", "description": "What needs to be done" },
"activeForm": { "type": "string", "description": "Spinner text when in_progress" }
}),
required: Some(vec!["subject".to_string(), "description".to_string()]),
}
}
fn task_list_schema() -> ToolInputSchema {
ToolInputSchema {
schema_type: "object".to_string(),
properties: serde_json::json!({}),
required: None,
}
}
fn task_update_schema() -> ToolInputSchema {
ToolInputSchema {
schema_type: "object".to_string(),
properties: serde_json::json!({
"taskId": { "type": "string", "description": "The ID of the task to update" },
"subject": { "type": "string", "description": "New subject for the task" },
"description": { "type": "string", "description": "New description" },
"status": { "type": "string", "enum": ["pending", "in_progress", "completed", "deleted"], "description": "New status" },
"activeForm": { "type": "string", "description": "New spinner text" }
}),
required: Some(vec!["taskId".to_string()]),
}
}
fn task_get_schema() -> ToolInputSchema {
ToolInputSchema {
schema_type: "object".to_string(),
properties: serde_json::json!({
"taskId": { "type": "string", "description": "The ID of the task to retrieve" }
}),
required: Some(vec!["taskId".to_string()]),
}
}
fn team_create_schema() -> ToolInputSchema {
ToolInputSchema {
schema_type: "object".to_string(),
properties: serde_json::json!({
"name": { "type": "string", "description": "Name of the team" },
"description": { "type": "string", "description": "Description of what the team does" },
"agents": { "type": "array", "items": serde_json::json!({}), "description": "List of agents in the team" }
}),
required: Some(vec!["name".to_string()]),
}
}
fn team_delete_schema() -> ToolInputSchema {
ToolInputSchema {
schema_type: "object".to_string(),
properties: serde_json::json!({
"name": { "type": "string", "description": "Name of the team to delete" }
}),
required: Some(vec!["name".to_string()]),
}
}
fn send_message_schema() -> ToolInputSchema {
ToolInputSchema {
schema_type: "object".to_string(),
properties: serde_json::json!({
"to": { "type": "string", "description": "Agent name to send message to" },
"message": { "type": "string", "description": "Message content" }
}),
required: Some(vec!["to".to_string(), "message".to_string()]),
}
}
fn enter_worktree_schema() -> ToolInputSchema {
ToolInputSchema {
schema_type: "object".to_string(),
properties: serde_json::json!({
"name": { "type": "string", "description": "Optional name for the worktree" }
}),
required: None,
}
}
fn exit_worktree_schema() -> ToolInputSchema {
ToolInputSchema {
schema_type: "object".to_string(),
properties: serde_json::json!({
"action": { "type": "string", "enum": ["keep", "remove"], "description": "What to do with the worktree" },
"discardChanges": { "type": "boolean", "description": "Discard uncommitted changes before removing" }
}),
required: None,
}
}
fn enter_plan_mode_schema() -> ToolInputSchema {
ToolInputSchema {
schema_type: "object".to_string(),
properties: serde_json::json!({
"allowedPrompts": { "type": "array", "items": { "type": "string" }, "description": "Prompt-based permissions" }
}),
required: None,
}
}
fn exit_plan_mode_schema() -> ToolInputSchema {
ToolInputSchema {
schema_type: "object".to_string(),
properties: serde_json::json!({}),
required: None,
}
}
fn ask_user_question_schema() -> ToolInputSchema {
ToolInputSchema {
schema_type: "object".to_string(),
properties: serde_json::json!({
"question": { "type": "string", "description": "The question to ask the user" },
"header": { "type": "string", "description": "Short label displayed as a chip/tag" },
"options": { "type": "array", "items": serde_json::json!({}), "description": "Available choices" },
"multiSelect": { "type": "boolean", "description": "Allow multiple answers" }
}),
required: Some(vec!["question".to_string(), "header".to_string(), "options".to_string()]),
}
}
fn tool_search_schema() -> ToolInputSchema {
ToolInputSchema {
schema_type: "object".to_string(),
properties: serde_json::json!({
"query": { "type": "string", "description": "Search query to find relevant tools" }
}),
required: Some(vec!["query".to_string()]),
}
}
fn cron_create_schema() -> ToolInputSchema {
ToolInputSchema {
schema_type: "object".to_string(),
properties: serde_json::json!({
"cron": { "type": "string", "description": "5-field cron expression" },
"prompt": { "type": "string", "description": "The prompt to execute" },
"recurring": { "type": "boolean", "description": "true = repeat, false = one-shot" },
"durable": { "type": "boolean", "description": "true = persist across restarts" }
}),
required: Some(vec!["cron".to_string(), "prompt".to_string()]),
}
}
fn cron_delete_schema() -> ToolInputSchema {
ToolInputSchema {
schema_type: "object".to_string(),
properties: serde_json::json!({
"id": { "type": "string", "description": "Job ID returned by CronCreate" }
}),
required: Some(vec!["id".to_string()]),
}
}
fn cron_list_schema() -> ToolInputSchema {
ToolInputSchema {
schema_type: "object".to_string(),
properties: serde_json::json!({}),
required: None,
}
}
fn config_schema() -> ToolInputSchema {
ToolInputSchema {
schema_type: "object".to_string(),
properties: serde_json::json!({
"action": { "type": "string", "enum": ["get", "set", "list"], "description": "Action to perform" },
"key": { "type": "string", "description": "Configuration key" },
"value": { "type": "string", "description": "Configuration value" }
}),
required: Some(vec!["action".to_string()]),
}
}
fn todo_write_schema() -> ToolInputSchema {
ToolInputSchema {
schema_type: "object".to_string(),
properties: serde_json::json!({
"todos": { "type": "array", "items": serde_json::json!({}), "description": "List of todo items" }
}),
required: Some(vec!["todos".to_string()]),
}
}
fn skill_schema() -> ToolInputSchema {
ToolInputSchema {
schema_type: "object".to_string(),
properties: serde_json::json!({
"skill": { "type": "string", "description": "The name of the skill to invoke" }
}),
required: Some(vec!["skill".to_string()]),
}
}
pub fn get_all_base_tools() -> Vec<ToolDefinition> {
ALL_TOOLS.iter().map(|f| f.2()).collect()
}
pub fn filter_tools(
tools: Vec<ToolDefinition>,
allowed: Option<Vec<String>>,
disallowed: Option<Vec<String>>,
) -> Vec<ToolDefinition> {
let mut result = tools;
if let Some(allowed) = allowed {
let allowed_set: std::collections::HashSet<_> = allowed.into_iter().collect();
result.retain(|t| allowed_set.contains(&t.name));
}
if let Some(disallowed) = disallowed {
let disallowed_set: std::collections::HashSet<_> = disallowed.into_iter().collect();
result.retain(|t| !disallowed_set.contains(&t.name));
}
result
}