Skip to main content

Tools & Actions

Tools are how agents interact with the outside world. Learn how to design, implement, and use agent tools.

SDK Focus allowedTools disallowedTools tools config canUseTool tool_call tool_result

What are Tools?

Tools are interfaces that let agents take real actions. Without tools, an agent can only generate text; with tools, an agent can:

  • Read and write files
  • Execute code
  • Call APIs
  • Search information
  • Interact with databases
  • Send messages

Function Calling

Modern LLMs support function calling, which is the foundation of tool use. The flow looks like this:

1. Define the tool schema
{
  "name": "read_file",
  "description": "Read the contents of a file",
  "parameters": {
    "type": "object",
    "properties": {
      "path": {
        "type": "string",
        "description": "Path to the file"
      }
    },
    "required": ["path"]
  }
}

2. The LLM decides to call
{
  "tool_call": {
    "name": "read_file",
    "arguments": {"path": "config.json"}
  }
}

3. Execute the tool and return the result
{
  "content": "{\"name\": \"my-app\", ...}"
}
Claude Agent SDK tool-calling.ts
import { query } from "@anthropic-ai/claude-agent-sdk";

const response = query({
  prompt: "Read package.json and tell me the project name",
  options: {
    // Control which tools the agent can use
    allowedTools: ["Read", "Glob"],

    // Or use preset + overrides
    // tools: { type: 'preset', preset: 'claude_code' },
    // disallowedTools: ["Write", "Bash"]
  }
});

for await (const message of response) {
  if (message.type === "tool_call") {
    console.log(`Tool: ${message.tool_name}`);
    console.log(`Input: ${JSON.stringify(message.tool_input)}`);
  }
  if (message.type === "tool_result") {
    console.log(`Result: ${message.content.slice(0, 100)}...`);
  }
}

Tool Categories

File Tools

Tool Function
read_file Read file contents
write_file Create or overwrite a file
edit_file Edit part of a file
list_directory List directory contents

Shell Tools

bash({
  "command": "npm install",
  "working_dir": "/project",
  "timeout": 60
})

Search Tools

  • grep - Text search
  • glob - Filename matching
  • semantic_search - Semantic search
  • web_search - Web search

API Tools

  • HTTP requests (GET/POST/PUT/DELETE)
  • Database queries
  • Third-party service integrations (GitHub, Slack, etc.)

Tool Design Principles

1. Atomicity

Each tool should do only one thing:

// BAD: too many responsibilities
create_and_run_test(file, test_content, run_args)

// GOOD: split into atomic operations
write_file(path, content)
run_command("pytest " + path)

2. Clear Descriptions

Descriptions should be detailed enough for the LLM to know when to use them:

// BAD
"description": "Search files"

// GOOD
"description": "Search for files by name pattern using glob syntax.
Use this when you need to find files matching a pattern like '*.py'
or 'src/**/*.ts'. Returns a list of matching file paths."

3. Reasonable Parameters

{
  "name": "edit_file",
  "parameters": {
    "path": "string (required) - File to edit",
    "old_string": "string (required) - Exact text to replace",
    "new_string": "string (required) - Replacement text",
    "expected_count": "number (optional) - Expected match count"
  }
}

4. Helpful Error Messages

// BAD
{"error": "Failed"}

// GOOD
{
  "error": "FileNotFoundError",
  "message": "File 'config.json' not found. Available files in current directory: package.json, tsconfig.json, src/"
}
Permission Control can-use-tool.ts
const response = query({
  prompt: "Clean up the temp directory",
  options: {
    allowedTools: ["Read", "Bash", "Glob"],

    // Custom permission gate
    canUseTool: async (toolName, input) => {
      // Allow read-only operations
      if (["Read", "Glob", "Grep"].includes(toolName)) {
        return { behavior: "allow" };
      }

      // Block destructive commands
      if (toolName === "Bash") {
        const dangerous = ["rm -rf", "dd if=", "> /dev/"];
        if (dangerous.some(p => input.command?.includes(p))) {
          return {
            behavior: "deny",
            message: "Destructive command blocked"
          };
        }
      }

      // Ask user for anything else
      return {
        behavior: "ask",
        message: `Allow ${toolName}?`
      };
    }
  }
});

SDK Insight: Three Permission Behaviors

canUseTool returns one of three decisions: allow (auto-execute), deny (block with message), or ask (request human confirmation). This is the foundation of safe agent design — you'll expand this pattern extensively in P11 (HITL).

Model Context Protocol (MCP)

MCP is a tool standardization protocol proposed by Anthropic that lets different agents share tools:

// MCP Tool Definition
{
  "name": "github_create_issue",
  "server": "github-mcp",
  "description": "Create a GitHub issue",
  "inputSchema": {
    "type": "object",
    "properties": {
      "repo": {"type": "string"},
      "title": {"type": "string"},
      "body": {"type": "string"}
    }
  }
}

MCP advantages:

  • Reusable tools
  • Cross-agent compatibility
  • Standardized interface
  • Rich ecosystem

Safety Considerations

  • Sandboxed execution - Limit the tool runtime environment
  • Permission control - Distinguish read-only vs writable tools
  • Confirmation mechanism - Dangerous actions require user confirmation
  • Audit logs - Record all tool calls
  • Rate limits - Prevent runaway loops

Next Steps

Try It: Tool Permission Gate

Build a permission-controlled agent that logs every tool call and blocks dangerous operations.

  1. Create an agent with allowedTools: ["Read", "Bash", "Glob"]
  2. Implement canUseTool that logs every tool request to console
  3. Block any Bash command containing rm or sudo
  4. Ask the agent to "list all .ts files and count their lines"
  5. Observe the tool_call → tool_result cycle in the stream
Gate: P2 Complete — Tool calls are logged, canUseTool blocks dangerous ops, multi-tool sequences observed in stream.