> For the complete documentation index, see [llms.txt](/llms.txt).
> A full single-fetch corpus is available at [llms-full.txt](/llms-full.txt).
---
title: Tools
description: Give agents typed callable actions for APIs, code execution, search, and provider-hosted capabilities.
last_verified: 2026-07-24
---



Tools give your agent the ability to take actions: call an API, run code, or search the web. The agent decides when to call a tool based on the conversation, runs it, then loops back with the result until it has a final answer.

---

## Custom tools



**Python:**

Define a tool with the `@tool` decorator. The SDK reads your type hints and docstring to build the JSON schema the model uses to call it correctly.

```python
from agnt5 import Agent
from agnt5.context import Context
from agnt5.tool import tool

@tool
async def get_weather(ctx: Context, city: str) -> str:
    """Get the current weather for a city.

    Args:
        city: Name of the city to check.
    """
    # call your weather API here
    return f"Sunny, 22°C in {city}"

agent = Agent(
    name="assistant",
    model="openai/gpt-4o-mini",
    instructions="You help users with weather queries.",
    tools=[get_weather],
)

result = await agent.run("What is the weather in Paris?")
print(result.output)
```

**Things to know**

- The first parameter must be `ctx: Context` (injected automatically, not exposed to the model).
- Every other parameter becomes a field the model can fill in. Type hints (`str`, `int`, `bool`, `list`) and docstring `Args:` descriptions are used to build the schema.
- Sync functions are wrapped in a thread pool automatically.
- The tool is registered globally at import time and can be reused across agents.

`@tool` decorator options:

| Parameter | Type | Default | Description |
|---|---|---|---|
| `name` | `str` | function's `__name__` | How the tool appears to the model |
| `description` | `str` | first line of docstring | What the model reads to decide when to call this tool |
| `confirmation` | `bool` | `False` | Reserved for future human-approval flow |





**TypeScript:**

Define a tool with `tool(name, options, handler)`. Unlike the Python SDK, there's no type-hint inference — you write the JSON Schema for the model explicitly in `inputSchema`.

```typescript
export const getWeather = tool(
  'get_weather',
  {
    description: 'Get the current weather for a city.',
    inputSchema: {
      type: 'object',
      properties: {
        city: { type: 'string', description: 'Name of the city to check.' },
      },
      required: ['city'],
    },
  },
  async (ctx: Context, args: { city: string }): Promise<string> => {
    // call your weather API here
    return `Sunny, 22°C in ${args.city}`;
  },
);

const agent = new Agent({
  name: 'assistant',
  model: LM.openai(),
  modelName: 'openai/gpt-4o-mini',
  instructions: 'You help users with weather queries.',
  tools: [getWeather],
});

const result = await agent.run('What is the weather in Paris?');
console.log(result.output);
```

**Things to know**

- The handler's first parameter is always `ctx: Context` (injected automatically, not exposed to the model).
- `inputSchema` is a plain JSON Schema object — it's what the model sees, so field `description`s matter.
- The tool is registered globally at call time and can be reused across agents.

`tool(name, options, handler)` options:

| Option | Type | Default | Description |
|---|---|---|---|
| `description` | `string` | tool `name` | What the model reads to decide when to call this tool |
| `inputSchema` | `JSONSchema` | `{ type: 'object', properties: {}, required: [] }` | The schema the model fills in when calling the tool |
| `confirmation` | `boolean` | `false` | Reserved for future human-approval flow |





**Go:**

Define a tool with `agnt5.NewTool(name, handler, opts...)`. There's no type-hint or reflection-based schema inference — write the JSON Schema explicitly with `WithToolSchema`.

```go
console.log(result.output);    // final answer
console.log(result.toolCalls); // [{ name: 'get_weather', arguments: '{"city":"Paris"}', iteration: 1 }]
```





**Go:**

After `agent.Run()`, `result.ToolCalls` is the number of tool calls made; `result.ToolCallDetails` lists each one.

```go
result, err := agent.Run(ctx, agnt5.AgentInput{Message: "What is the weather in Paris?"})

fmt.Println(result.Response)         // final answer
fmt.Println(result.ToolCalls)        // 1
fmt.Println(result.ToolCallDetails)  // []AgentToolCall{{Name: "get_weather", Arguments: map[string]any{"city": "Paris"}, Iteration: 1, Result: ...}}
```



---

## Agents as tools



**Python:**

Pass another `Agent` directly in the `tools` list. The SDK wraps it automatically. The calling agent invokes it like any other tool and gets the specialist's response back before continuing its own reasoning.

```python
from agnt5 import Agent

lookup_agent = Agent(
    name="lookup",
    model="openai/gpt-4o-mini",
    instructions="You look up factual information and return concise answers.",
)

orchestrator = Agent(
    name="orchestrator",
    model="openai/gpt-4o-mini",
    instructions="Use lookup to find information, then summarize it for the user.",
    tools=[lookup_agent],   # auto-wrapped as ask_lookup
)

result = await orchestrator.run("What is the capital of Japan?")
print(result.output)
```





**TypeScript:**

Pass another `Agent` directly in the `tools` list. The SDK wraps it automatically. The calling agent invokes it like any other tool and gets the specialist's response back before continuing its own reasoning.

```typescript
const lookupAgent = new Agent({
  name: 'lookup',
  model: LM.openai(),
  modelName: 'openai/gpt-4o-mini',
  instructions: 'You look up factual information and return concise answers.',
});

const orchestrator = new Agent({
  name: 'orchestrator',
  model: LM.openai(),
  modelName: 'openai/gpt-4o-mini',
  instructions: 'Use lookup to find information, then summarize it for the user.',
  tools: [lookupAgent], // auto-wrapped as ask_lookup
});

const result = await orchestrator.run('What is the capital of Japan?');
console.log(result.output);
```





**Go:**

`WithAgentTools` only accepts `Tool` values, not `*Agent` directly — there's no auto-wrapping. Wrap the specialist's `Run` call in `agnt5.NewTool` yourself.

```go
lookupAgent, err := agnt5.NewAgent("lookup",
    agnt5.WithAgentModel(model),
    agnt5.WithAgentInstructions("You look up factual information and return concise answers."),
)

askLookup, err := agnt5.NewTool("ask_lookup", func(_ context.Context, args map[string]any) (any, error) {
    question, _ := args["question"].(string)
    result, err := lookupAgent.Run(ctx, agnt5.AgentInput{Message: question})
    return result.Response, err
}, agnt5.WithToolDescription("Ask the lookup specialist a factual question."))

orchestrator, err := agnt5.NewAgent("orchestrator",
    agnt5.WithAgentModel(model),
    agnt5.WithAgentInstructions("Use lookup to find information, then summarize it for the user."),
    agnt5.WithAgentTools(askLookup),
    agnt5.WithAgentMaxTurns(3),
)

result, err := orchestrator.Run(ctx, agnt5.AgentInput{Message: "What is the capital of Japan?"})
fmt.Println(result.Response)
```



---

## Sandbox workspaces

Pass a **[sandbox](/docs/build/sandboxes.md)** to an agent when it needs an isolated file and code execution workspace. AGNT5 adds the standard sandbox capabilities to the agent, and your custom tools can use the same workspace through `ctx.sandbox`.



**Python:**

```python
from agnt5 import Agent, Sandbox

agent = Agent(
    name="coder",
    model="openai/gpt-4o-mini",
    instructions=(
        "Use the sandbox workspace to write files, inspect files, "
        "and run code before returning an answer."
    ),
    sandbox=Sandbox(),
)

result = await agent.run("Calculate the first 10 fibonacci numbers in Python")
print(result.output)
```





**TypeScript:**

```typescript
const agent = new Agent({
  name: 'coder',
  model: LM.openai(),
  modelName: 'openai/gpt-4o-mini',
  instructions:
    'Use the sandbox workspace to write files, inspect files, and run code before returning an answer.',
  sandbox: new Sandbox(),
});

const result = await agent.run('Calculate the first 10 fibonacci numbers in Python');
console.log(result.output);
```





**Go:**

There's no `sandbox` option on `NewAgent` yet, so a sandbox isn't auto-attached with a set of standard tools the way it is in Python/TypeScript. Instead, attach a sandbox to the invocation's `*agnt5.Context` with `ctx.SetSandbox(...)`, and give the agent custom tools whose handlers call `ctx.Sandbox()`:

```go
sandbox := agnt5.NewInMemorySandbox()
ctx.SetSandbox(sandbox)

runCode, err := agnt5.NewTool("run_code", func(_ context.Context, args map[string]any) (any, error) {
    code, _ := args["code"].(string)
    return ctx.Sandbox().ExecuteCode(ctx, "python", code)
}, agnt5.WithToolDescription("Run Python code in the sandbox workspace."))

agent, err := agnt5.NewAgent("coder",
    agnt5.WithAgentModel(model),
    agnt5.WithAgentInstructions("Use run_code to write and execute code before returning an answer."),
    agnt5.WithAgentTools(runCode),
    agnt5.WithAgentMaxTurns(3),
)

result, err := agent.Run(ctx, agnt5.AgentInput{Message: "Calculate the first 10 fibonacci numbers in Python"})
fmt.Println(result.Response)
```



The agent can use the sandbox to:

- write files to the workspace
- read files from the workspace
- list files and directories
- execute `python`, `javascript`, or `bash` code

See [Use sandboxes with agents](/docs/build/sandboxes.md) for provider selection, lifecycle, and custom tools that read from `ctx.sandbox`.

---

## Built-in tools

Built-in tools run entirely on the provider's infrastructure. You enable them and the provider handles execution. Results are already included in the model's response; no local code runs.



**Python:**

```python
from agnt5 import Agent
from agnt5.lm import BuiltInTool

agent = Agent(
    name="researcher",
    model="openai/gpt-4o-mini",
    instructions=(
        "You are a research assistant. Always use web_search to find current "
        "information, never answer from training knowledge alone."
    ),
    built_in_tools=[BuiltInTool.WEB_SEARCH],
)

result = await agent.run("Who won the most recent Formula 1 championship?")
print(result.output)
```

Available built-in tools:

| Tool | What it does | Provider |
|---|---|---|
| `BuiltInTool.WEB_SEARCH` | Live web search with cited results | OpenAI, Anthropic |
| `BuiltInTool.CODE_INTERPRETER` | Run code in a provider-hosted sandbox | OpenAI only |
| `BuiltInTool.FILE_SEARCH` | Search over files uploaded to the provider | OpenAI only |
| `BuiltInTool.WEB_FETCH` | Fetch the content of a specific URL | Anthropic only |

You can mix built-in tools with sandbox workspaces and custom tools on the same agent:

```python
from agnt5 import Agent, Sandbox
from agnt5.lm import BuiltInTool

agent = Agent(
    name="assistant",
    model="openai/gpt-4o-mini",
    instructions="Search the web for information and run code when needed.",
    built_in_tools=[BuiltInTool.WEB_SEARCH],
    sandbox=Sandbox(),
)
```





**TypeScript:**

```typescript
const agent = new Agent({
  name: 'researcher',
  model: LM.openai(),
  modelName: 'openai/gpt-4o-mini',
  instructions:
    'You are a research assistant. Always use web_search to find current information, never answer from training knowledge alone.',
  builtInTools: ['web_search'],
});

const result = await agent.run('Who won the most recent Formula 1 championship?');
console.log(result.output);
```

Available built-in tools (`BuiltInTool` is a string union, not an enum):

| Tool | What it does | Provider |
|---|---|---|
| `'web_search'` | Live web search with cited results | OpenAI, Anthropic |
| `'code_interpreter'` | Run code in a provider-hosted sandbox | OpenAI only |
| `'file_search'` | Search over files uploaded to the provider | OpenAI only |
| `'web_fetch'` | Fetch the content of a specific URL | Anthropic only |

You can mix built-in tools with sandbox workspaces and custom tools on the same agent:

```typescript
const agent = new Agent({
  name: 'assistant',
  model: LM.openai(),
  modelName: 'openai/gpt-4o-mini',
  instructions: 'Search the web for information and run code when needed.',
  builtInTools: ['web_search'],
  sandbox: new Sandbox(),
});
```





**Go:**

Not yet available in the Go SDK — there's no `built_in_tools`/`builtInTools` equivalent on `NewAgent`, and no provider-hosted web search, code interpreter, file search, or URL fetch tool. Reach for a custom tool (see [Custom tools](#custom-tools)) that calls the provider's API directly, or use an [MCP tool](#mcp-tools) if one exposes the capability you need.



---

## MCP tools

Use [Model Context Protocol](/docs/build/mcp.md) when a tool already exists in an external MCP server. `MCPClient` connects to the server, discovers tools, and returns AGNT5 `Tool` objects that you pass to an agent.



**Python:**

```python
from agnt5 import Agent
from agnt5.mcp import MCPClient

mcp = MCPClient(id="repo-tools")
mcp.add_streamable_http_server("deepwiki", "https://mcp.deepwiki.com/mcp")
await mcp.connect()

agent = Agent(
    name="repository_researcher",
    model="openai/gpt-4o-mini",
    instructions="Use available tools to answer questions about code repositories.",
    tools=mcp.get_tools(),
)
```





**TypeScript:**

```typescript
const mcp = new MCPClient('repo-tools');
mcp.addStreamableHttpServer('deepwiki', 'https://mcp.deepwiki.com/mcp');
await mcp.connect();

const agent = new Agent({
  name: 'repository_researcher',
  model: LM.openai(),
  modelName: 'openai/gpt-4o-mini',
  instructions: 'Use available tools to answer questions about code repositories.',
  tools: mcp.getTools(),
});
```





**Go:**

`agnt5.NewMCPClient(transport)` connects to a server and lists tools, but `MCPTool` (name, description, input schema) isn't a drop-in `agnt5.Tool` — it has no `Handler`. Wrap each one you want to expose with `agnt5.NewTool`, forwarding calls through `client.CallTool`:

```go
export const agentWithHitl = workflow(
  'agent_with_hitl',
  async (ctx: Context, input: { task: string }) => {
    const agent = new Agent({
      name: 'assistant',
      model: LM.openai(),
      modelName: 'openai/gpt-4o-mini',
      instructions:
        'Ask the user for clarification when the request is ambiguous. Always request approval before making any changes.',
      // AskUserTool/RequestApprovalTool require the concrete ContextImpl the runtime passes in.
      tools: [new AskUserTool(ctx as ContextImpl), new RequestApprovalTool(ctx as ContextImpl)],
    });
    const result = await agent.run(input.task, ctx);
    return { response: result.output };
  },
);
```

| Tool | Behaviour |
|---|---|
| `new AskUserTool(ctx)` | Agent calls `ask_user` with a question. Workflow pauses until the user types a response |
| `new RequestApprovalTool(ctx)` | Agent calls `request_approval` with an action description. Workflow pauses and shows Approve / Reject to the user |





**Go:**

There's no bundled `AskUserTool`/`RequestApprovalTool` class — write a custom tool whose handler calls `ctx.AskUser` or `ctx.RequestApproval` directly. Both durably suspend the workflow while waiting, the same as calling them anywhere else in a workflow handler.

```go
err := agnt5.RegisterWorkflow(worker, "agent_with_hitl", func(ctx *agnt5.Context, in TaskInput) (TaskOutput, error) {
    askUser, err := agnt5.NewTool("ask_user", func(_ context.Context, args map[string]any) (any, error) {
        question, _ := args["question"].(string)
        return ctx.AskUser(agnt5.UserInputRequest{Prompt: question, Type: agnt5.HITLText})
    }, agnt5.WithToolDescription("Ask the user a clarifying question."))
    if err != nil {
        return TaskOutput{}, err
    }

    requestApproval, err := agnt5.NewTool("request_approval", func(_ context.Context, args map[string]any) (any, error) {
        action, _ := args["action"].(string)
        return ctx.RequestApproval(action, nil)
    }, agnt5.WithToolDescription("Request approval before making a change."))
    if err != nil {
        return TaskOutput{}, err
    }

    agent, err := agnt5.NewAgent("assistant",
        agnt5.WithAgentModel(model),
        agnt5.WithAgentInstructions("Ask the user for clarification when the request is ambiguous. Always request approval before making any changes."),
        agnt5.WithAgentTools(askUser, requestApproval),
        agnt5.WithAgentMaxTurns(5),
    )
    if err != nil {
        return TaskOutput{}, err
    }

    result, err := agent.Run(ctx, agnt5.AgentInput{Message: in.Task})
    return TaskOutput{Response: result.Response}, err
})
```

| Tool | Behavior |
|---|---|
| Custom tool calling `ctx.AskUser` | Agent asks the user a question. Workflow pauses until the user types a response |
| Custom tool calling `ctx.RequestApproval` | Agent requests approval for an action. Workflow pauses and shows Approve / Reject to the user |


