Tools
Tools extend the agent’s capabilities with actions like running commands, searching the web, and reading files.
Overview
Tools are the primary way Ash interacts with the world. Each tool:
- Has a unique name and description
- Defines input parameters via JSON Schema
- Executes asynchronously
- Returns structured results
Tool Interface
Location: src/ash/tools/base.py
from abc import ABC, abstractmethodfrom typing import Any
class Tool(ABC): @property @abstractmethod def name(self) -> str: """Unique identifier for this tool."""
@property @abstractmethod def description(self) -> str: """Description for the LLM."""
@property @abstractmethod def input_schema(self) -> dict[str, Any]: """JSON Schema for input parameters."""
@abstractmethod async def execute( self, input_data: dict[str, Any], context: ToolContext, ) -> ToolResult: """Execute the tool and return result."""ToolContext
Context provided to tool execution:
@dataclassclass ToolContext: session_id: str | None = None user_id: str | None = None chat_id: str | None = None provider: str | None = None metadata: dict[str, Any] = field(default_factory=dict) env: dict[str, str] = field(default_factory=dict)ToolResult
Tool execution result:
@dataclassclass ToolResult: content: str is_error: bool = False metadata: dict[str, Any] = field(default_factory=dict)
@classmethod def success(cls, content: str, **metadata) -> "ToolResult": """Create a successful result."""
@classmethod def error(cls, message: str, **metadata) -> "ToolResult": """Create an error result."""Built-in Tools
Bash Tool
Location: src/ash/tools/builtin/bash.py
Executes commands in the Docker sandbox:
class BashTool(Tool): name = "bash" description = "Execute bash commands in a sandboxed environment" input_schema = { "type": "object", "properties": { "command": { "type": "string", "description": "The bash command to execute.", }, }, "required": ["command"], }All commands run in isolated Docker containers. See Sandbox for security details.
Web Search Tool
Location: src/ash/tools/builtin/web_search.py
Searches the web using Brave Search:
class WebSearchTool(Tool): name = "web_search" description = "Search the web for information" input_schema = { "type": "object", "properties": { "query": { "type": "string", "description": "The search query.", }, "max_results": { "type": "integer", "description": "Maximum results to return (default: 10).", }, }, "required": ["query"], }Requires Brave Search API configuration:
[brave_search]api_key = "BSA..."Web Fetch Tool
Location: src/ash/tools/builtin/web_fetch.py
Fetches and extracts content from URLs:
class WebFetchTool(Tool): name = "web_fetch" description = "Fetch and read the content of a web page" input_schema = { "type": "object", "properties": { "url": { "type": "string", "description": "The URL to fetch (http or https).", }, "extract_mode": { "type": "string", "enum": ["text", "markdown"], "description": "Content format: 'markdown' preserves structure.", }, }, "required": ["url"], }Features:
- Extracts readable content from HTML pages
- Converts to markdown-like format (links, headings, lists)
- Handles redirects (up to 5 hops)
- Supports text/HTML/JSON content types
Read File Tool
Location: src/ash/tools/builtin/files.py
Reads file contents from the workspace:
class ReadFileTool(Tool): name = "read_file" description = "Read file contents from the workspace" input_schema = { "type": "object", "properties": { "file_path": { "type": "string", "description": "Path to the file (relative to workspace).", }, "offset": { "type": "integer", "description": "Line number to start from (1-indexed).", }, "limit": { "type": "integer", "description": "Maximum lines to read.", }, }, "required": ["file_path"], }Features:
- Returns content with line numbers
- Pagination via offset/limit
- Automatic truncation for large files
Write File Tool
Location: src/ash/tools/builtin/files.py
Writes content to files in the workspace:
class WriteFileTool(Tool): name = "write_file" description = "Write content to a file in the workspace" input_schema = { "type": "object", "properties": { "file_path": { "type": "string", "description": "Path to write to (relative to workspace).", }, "content": { "type": "string", "description": "Content to write to the file.", }, }, "required": ["file_path", "content"], }Features:
- Creates files if they don’t exist
- Creates parent directories automatically
- Size limits enforced
Use Agent Tool
Location: src/ash/tools/builtin/agents.py
Invokes built-in agents for complex tasks:
class UseAgentTool(Tool): name = "use_agent" description = "Run a specialized agent for complex tasks" input_schema = { "type": "object", "properties": { "agent": { "type": "string", "description": "Name of the agent to run.", }, "message": { "type": "string", "description": "Message/task for the agent.", }, "input": { "type": "object", "description": "Additional input data (optional).", }, }, "required": ["agent", "message"], }Built-in agents include:
- research - Multi-step research tasks
- skill-writer - Creates new skills autonomously
See Agents for details.
Tool Registry
Location: src/ash/tools/registry.py
Tools are discovered and registered:
registry = ToolRegistry()registry.register(BashTool(executor))registry.register(WebSearchTool(cache=cache))
tools = registry.all()tool = registry.get("bash")Tool Execution
Location: src/ash/tools/executor.py
The executor handles tool calls from the LLM:
class ToolExecutor: async def execute( self, tool_call: ToolCall, context: ToolContext, ) -> ToolResult: tool = self.registry.get(tool_call.name) return await tool.execute(tool_call.input, context)Configuration
Brave Search
Enable web search capabilities:
[brave_search]api_key = "BSA..."Get an API key from Brave Search API.
Workspace
Configure the workspace directory for file operations:
workspace = "~/.ash/workspace"See Configuration Reference for all options.
Creating Custom Tools
- Create a class implementing
Tool - Define
input_schemaas a JSON Schema dict - Implement async
execute()method - Register with the tool registry
Example:
from typing import Anyfrom ash.tools.base import Tool, ToolContext, ToolResult
class WeatherTool(Tool): @property def name(self) -> str: return "weather"
@property def description(self) -> str: return "Get current weather for a location"
@property def input_schema(self) -> dict[str, Any]: return { "type": "object", "properties": { "location": { "type": "string", "description": "City name or coordinates.", }, }, "required": ["location"], }
async def execute( self, input_data: dict[str, Any], context: ToolContext, ) -> ToolResult: location = input_data.get("location") # Implementation here return ToolResult.success(f"Weather for {location}: 72°F, sunny")Register the tool:
from ash.tools.registry import ToolRegistry
registry = ToolRegistry()registry.register(WeatherTool())