The Tool System
How agents extend their capabilities. The interface between thinking and doing.
Evidence source: Amp Code v0.0.1769212917 (45+ tools, comprehensive permission system)
The Core Insight
An LLM alone can only generate text. Tools give it agency - the ability to:
- Read files and understand codebases
- Make edits that actually persist
- Run commands and see results
- Search the web for documentation
Without tools, you have a chatbot. With tools, you have an agent.
Tool Architecture
The Three-Part Structure
Every tool in Amp has three components:
┌─────────────────────────────────────────────────────────────────┐
│ TOOL DEFINITION │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ SPEC │ │ FUNCTION │ │ EXECUTION │ │
│ │ │ │ │ │ PROFILE │ │
│ │ • name │ │ • fn() │ │ │ │
│ │ • schema │ │ │ │ • serial? │ │
│ │ • description│ │ │ │ • resourceKeys() │ │
│ │ • aliases │ │ │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Tool Definition Interface
interface ToolDefinition {
spec: {
name: string; // Tool name constant
description: string; // Full description for the LLM
inputSchema: JSONSchema; // What arguments it accepts
source: "builtin" | "mcp" | "toolbox";
meta?: {
disableTimeout?: boolean; // Don't auto-timeout
};
executionProfile?: {
serial?: boolean; // Must run alone
resourceKeys?: (args: any) => ResourceKey[];
};
aliases?: ToolAlias[]; // Alternative schemas for modes
};
fn: ToolFunction; // The actual implementation
preprocessArgs?: (args: any, env: ToolEnv) => any;
}
interface ResourceKey {
key: string; // File path or resource identifier
mode: "read" | "write";
}
Why this structure?
spectells the LLM what the tool doesfnexecutes itexecutionProfileenables parallel execution by declaring conflicts
Tool Sources
Three Origins
| Prefix | Source | Example |
|---|---|---|
| (none) | Built-in native | Read, Grep, Bash |
mcp__ |
MCP server tools | mcp__github__search_code |
tb__ |
Toolbox custom | tb__custom_script |
Tool Name Resolution
Tools support aliases for backwards compatibility:
const TOOL_ALIASES = {
// Legacy names → current names
Read: "Read",
read: "Read",
read_file: "Read",
Write: "create_file",
write: "create_file",
write_file: "create_file",
Edit: "edit_file",
edit: "edit_file",
Delete: "delete_file",
delete: "delete_file",
run_terminal_command: "Bash"
};
Why aliases? Models trained on older documentation might use Write or edit. Aliases let them work without retraining.
Tool Categories
Amp organizes 45+ tools into functional categories:
File Operations
| Tool | Purpose | Permission |
|---|---|---|
Read |
Read files/directories | Always allowed |
edit_file |
Replace text in files | Requires approval |
create_file |
Create/overwrite files | Requires approval |
glob |
Find files by pattern | Always allowed |
undo_edit |
Revert last edit | Always allowed |
delete_file |
Delete files | Requires approval |
Search
| Tool | Purpose | Model Used |
|---|---|---|
Grep |
Regex search in files | Native (ripgrep) |
finder |
AI-powered code search | Gemini 3 Flash Preview |
web_search |
Search the web | Backend API |
Execution
| Tool | Purpose | Notes |
|---|---|---|
Bash |
Run shell commands | Serial, no timeout |
Check |
Run CI/CD checks | No timeout |
Subagents
| Tool | Purpose | Model |
|---|---|---|
Task |
Spawn subtasks | Claude Opus 4.5 |
finder |
Code search | Gemini 3 Flash Preview |
oracle |
Deep reasoning | GPT-5.2 |
librarian |
Multi-repo search | Claude Haiku 4.5 |
kraken |
Multi-file refactor | Claude Haiku 4.5 |
Tool Execution
The Execution Flow
Tool Request from LLM
│
▼
┌───────────────────┐
│ Permission Check │──── Denied ────▶ { status: "rejected-by-user" }
└─────────┬─────────┘
│ Allowed
▼
┌───────────────────┐
│ Preprocess Args │──── Transform args if needed
└─────────┬─────────┘
│
▼
┌───────────────────┐
│ Resolve Aliases │──── Map to actual tool name
└─────────┬─────────┘
│
▼
┌───────────────────┐
│ Execute Function │──── Run the tool
└─────────┬─────────┘
│
▼
┌───────────────────┐
│ Post-Process │──── Truncate, format result
└─────────┬─────────┘
│
▼
Tool Result
Tool Status States
type ToolStatus =
| "in-progress" // Currently executing
| "done" // Successful completion
| "error" // Execution failed
| "cancelled" // User cancelled
| "rejected-by-user" // Permission denied
| "blocked-on-user"; // Waiting for approval
Result Format
Success:
{
status: "done",
result: string | object,
trackFiles?: string[], // Files modified (for tracking)
}
Error:
{
status: "error",
error: {
message: string,
errorCode?: string,
absolutePath?: string, // For file operations
}
}
Permission System
Permission Classes
Always Allowed (no approval needed):
Read,glob,Grep,finderweb_search,read_web_pageread_thread,find_thread,task_list
Requires Approval:
Bash- Shell executioncreate_file,edit_file,delete_file- File modifications
Auto-Allowed Patterns:
- Writes to
.amp/directory - Common dev commands (
npm,git,python, etc.) - Format commands
Permission Check Logic
function checkToolPermission(
toolName: string,
modeConfig: ModeConfig,
toolboxConfig?: ToolboxConfig
): boolean {
// MCP tools
if (toolName.startsWith("mcp__")) {
return modeConfig.allowMcp ?? false;
}
// Toolbox tools
if (toolName.startsWith("tb__")) {
if (!(modeConfig.allowToolbox ?? false)) {
return false;
}
// Check subagent type restrictions
if (modeConfig.subagentType && toolboxConfig?.subagentTypes) {
return toolboxConfig.subagentTypes.includes("all") ||
toolboxConfig.subagentTypes.includes(modeConfig.subagentType);
}
return true;
}
// Native tools - check inclusion in mode's tool list
return modeConfig.includeTools.includes(toolName);
}
Execution Profiles & Batching
Why Execution Profiles?
When the LLM requests multiple tools at once:
Read file A
Read file B
Edit file A
Read file C
Can we run them in parallel? It depends on conflicts.
Resource Keys
Each tool declares what resources it touches:
// Read tool
executionProfile: {
serial: false,
resourceKeys: (args) => [{
key: args.path,
mode: "read"
}]
}
// edit_file tool
executionProfile: {
serial: false,
resourceKeys: (args) => [{
key: args.path,
mode: "write"
}]
}
Conflict Rules
- No profile = assume conflict (conservative)
- Serial tools = always conflict
- Same resource key + any write = conflict
- Read-read on same resource = no conflict
Batching Algorithm
def batch_tools_by_conflict(tool_uses):
batches = []
current_batch = []
for tool in tool_uses:
if conflicts_with_batch(tool, current_batch):
if current_batch:
batches.append(current_batch)
current_batch = []
current_batch.append(tool)
if current_batch:
batches.append(current_batch)
return batches
Example:
Input: [Read A, Read B, Edit A, Read C]
Batch 1: [Read A, Read B] # No conflict - parallel
Batch 2: [Edit A] # Conflicts with Read A - new batch
Batch 3: [Read C] # Could be in Batch 2, but after Edit A
Timeout Handling
Default Behavior
Tools timeout after 120 seconds unless meta.disableTimeout: true.
Tools with Disabled Timeout
Long-running tools that need to complete:
Bash- Shell commands can take timeTask- Subagents need to completefinder,oracle,librarian- Subagent toolsCheck- CI/CD can be slowkraken- Multi-file refactoring
Timeout Behavior
// Timeout resets on progress
// Long-running tools emit progress to stay alive
{
status: "error",
error: {
message: "Tool execution timed out after 120 seconds"
}
}
Result Truncation
Large tool results must be truncated before sending to the model:
Truncation Limits
| Tool | Limit | Notes |
|---|---|---|
Read |
65,536 bytes | Per file |
Bash |
50,000 chars | Output only |
Grep |
100 matches | Total results |
read_web_page |
262,144 bytes | Content |
| General | 102,400 bytes | All tool results |
Truncation Implementation
const TRUNCATION_LIMIT = 102400; // 100KB
function truncateResult(content: string): string {
if (content.length > TRUNCATION_LIMIT) {
return `${content.slice(0, TRUNCATION_LIMIT)}
[Tool result truncated: ${Math.round(content.length / 1024)}KB exceeds limit. Please refine the query.]`;
}
return content;
}
Tool Input Schema
JSON Schema Format
Tools declare their inputs using JSON Schema:
{
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "Absolute path to the file"
},
"read_range": {
"type": "array",
"items": { "type": "number" },
"minItems": 2,
"maxItems": 2,
"description": "Line range [start, end], 1-indexed"
}
},
"required": ["path"]
}
Best Practices for Schemas
- Required fields first - Most important args should be required
- Clear descriptions - The LLM reads these
- Sensible defaults - Don't require everything
- Validation - Schema validates before execution
Implementing Your Own Tools
Minimal Tool
def create_tool(name: str, description: str, schema: dict, fn: callable):
return {
"spec": {
"name": name,
"description": description,
"inputSchema": schema,
"source": "builtin"
},
"fn": fn
}
# Example: Simple file counter
file_counter = create_tool(
name="count_files",
description="Count files matching a pattern",
schema={
"type": "object",
"properties": {
"pattern": {"type": "string", "description": "Glob pattern"}
},
"required": ["pattern"]
},
fn=lambda args, env: {
"status": "done",
"result": len(glob.glob(args["pattern"], recursive=True))
}
)
Tool with Execution Profile
file_writer = {
"spec": {
"name": "write_file",
"description": "Write content to a file",
"inputSchema": {
"type": "object",
"properties": {
"path": {"type": "string"},
"content": {"type": "string"}
},
"required": ["path", "content"]
},
"source": "builtin",
"executionProfile": {
"serial": False,
"resourceKeys": lambda args: [
{"key": args["path"], "mode": "write"}
]
}
},
"fn": write_file_impl
}
Implementation Checklist
Building your own tool system? Ensure:
Tool Registration
- Schema validation
- Name aliasing
- Source tracking (builtin/mcp/toolbox)
Execution
- Permission checking
- Argument preprocessing
- Timeout handling
- Result truncation
Batching
- Execution profiles
- Resource key extraction
- Conflict detection
- Parallel execution within batches
Error Handling
- Standard error format
- Error codes for common failures
- Graceful degradation
What's Next
Now you understand the tool system. Let's look at the specific tools you need.
→ 05-core-tools.md - Read, edit_file, create_file, Bash, glob, Grep specifications
For reconstruction-grade detail:
→ 04-tool-system.spec.md - Full tool definitions and schemas