# `Alloy.Tool`
[🔗](https://github.com/alloy-ex/alloy/blob/v0.10.1/lib/alloy/tool.ex#L1)

Behaviour for tools that agents can call.

## Required Callbacks

Every tool must implement `name/0`, `description/0`, `input_schema/0`,
and `execute/2`.

## Optional Callbacks

Tools may optionally implement:

- `allowed_callers/0` — declares which callers may invoke this tool
  (`:human`, `:code_execution`). Defaults to `[:human]`.
- `result_type/0` — declares whether the tool returns `:text` or
  `:structured` data. Defaults to `:text`.

## Structured Results

Tools can return a 3-tuple `{:ok, text, data}` where `text` is the
human-readable result and `data` is a map of structured data for
programmatic consumption (e.g., by a code execution sandbox).

## Path Security

Set `:allowed_paths` in the agent context to restrict file access:

    Alloy.run("Read the config",
      context: %{allowed_paths: ["/home/user/project"]},
      tools: [Alloy.Tool.Core.Read]
    )

When configured, `resolve_path/2` returns `{:error, reason}` for
paths outside the allowed directories.

## Example

    defmodule MyApp.Tools.Weather do
      @behaviour Alloy.Tool

      @impl true
      def name, do: "get_weather"

      @impl true
      def description, do: "Get current weather for a location"

      @impl true
      def input_schema do
        %{
          type: "object",
          properties: %{location: %{type: "string", description: "City name"}},
          required: ["location"]
        }
      end

      @impl true
      def execute(%{"location" => loc}, _context) do
        {:ok, "72°F, sunny in #{loc}", %{temp: 72, condition: "sunny", location: loc}}
      end

      @impl true
      def allowed_callers, do: [:human, :code_execution]

      @impl true
      def result_type, do: :structured
    end

# `allowed_callers`
*optional* 

```elixir
@callback allowed_callers() :: [:human | :code_execution]
```

Declares which callers may invoke this tool.

- `:human` — the tool can be called by the model during normal conversation
- `:code_execution` — the tool can be called from a code execution sandbox

Defaults to `[:human]` when not implemented. Providers that support
`allowed_callers` (e.g., Anthropic) include this in the tool definition
sent to the API.

# `concurrent?`
*optional* 

```elixir
@callback concurrent?() :: boolean()
```

Whether this tool is safe to run concurrently with other tools.
State-mutating tools (file write, bash) should return false.
Defaults to `true` when not implemented.

# `description`

```elixir
@callback description() :: String.t()
```

Human-readable description of what the tool does.

# `execute`

```elixir
@callback execute(input :: map(), context :: map()) ::
  {:ok, String.t()} | {:ok, String.t(), map()} | {:error, String.t()}
```

Execute the tool with the given input.

Context is a map that may contain:
- `:working_directory` - base path for file operations
- `:config` - agent config struct
- `:allowed_paths` - list of allowed directory prefixes for file access
- any custom keys added by middleware

Returns `{:ok, string}` or `{:error, string}` for text-only results.
Optionally returns `{:ok, string, map}` to include structured data
alongside the text (for programmatic consumption by code execution).

# `input_schema`

```elixir
@callback input_schema() :: map()
```

JSON Schema defining the tool's input parameters.

# `max_result_chars`
*optional* 

```elixir
@callback max_result_chars() :: pos_integer() | :unlimited
```

Maximum characters in the tool result before truncation.
The executor truncates results exceeding this, keeping head + tail.
Defaults to `:unlimited` when not implemented.

# `name`

```elixir
@callback name() :: String.t()
```

Unique tool name (used in API calls).

# `result_type`
*optional* 

```elixir
@callback result_type() :: :text | :structured
```

Declares the tool's result type.

- `:text` — tool returns `{:ok, String.t()}` (the default)
- `:structured` — tool returns `{:ok, String.t(), map()}`

Used by the executor and downstream consumers to know whether
structured data is available in the tool result metadata.

# `resolve_path`

```elixir
@spec resolve_path(String.t(), map()) :: {:ok, String.t()} | {:error, String.t()}
```

Resolve a file path against the working directory from context.

Absolute paths are returned as-is. Relative paths are joined with
the `:working_directory` from context, or expanded from cwd if not set.

When `:allowed_paths` is set in context, validates the resolved path
falls within one of the allowed directories. Returns `{:error, reason}`
if the path is outside the allowed directories.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
