# `Planck.Agent.Tool`
[🔗](https://github.com/alexdesousa/planck/blob/v0.2.2/lib/planck/agent/tool.ex#L1)

An executable tool for use in agent turns.

Extends `Planck.AI.Tool` with an `execute_fn` — the function called when the
LLM requests a tool invocation. The AI tool schema (name, description, parameters)
is extracted when building `Planck.AI.Context`; `execute_fn` stays agent-side.

# `execute_fn`

```elixir
@type execute_fn() :: (agent_id :: String.t(), id :: String.t(), args :: map() -&gt;
                   {:ok, String.t()}
                   | {:ok, String.t(), %{ui: ui_content()}}
                   | {:error, String.t()})
```

The function invoked when the LLM requests this tool.

Receives the calling `agent_id`, the tool call `id` (an opaque string from
the provider, used to correlate results), and `args` (the JSON-decoded
arguments map).

Returns one of:

- `{:ok, result}` — the common case. `result` is placed directly into the
  model's context as tool result text.
- `{:ok, result, %{ui: ui_content()}}` — same, plus a UI side effect
  invisible to the model. The third element is a **map**, not a keyword
  list. The `%{ui: ...}` wrapper is stripped before the text reaches the
  LLM — see `Planck.Agent`'s tool-result handling.
- `{:error, reason}` — `reason` is placed into the model's context the same
  way `result` would be.

All text values must be strings. Exceptions and exit signals are caught by
the agent and converted to error strings automatically.

# `t`

```elixir
@type t() :: %Planck.Agent.Tool{
  description: String.t(),
  execute_fn: execute_fn(),
  name: String.t(),
  parameters: map(),
  widget: module() | nil
}
```

A fully-specified tool: schema fields understood by the LLM plus the
`execute_fn` that runs when the model calls it.

- `:name` — identifier the model uses to call the tool; must be unique within
  an agent's tool set
- `:description` — natural-language description sent to the model; quality
  here directly affects how reliably the model uses the tool
- `:parameters` — JSON Schema object describing the accepted arguments
- `:execute_fn` — the function called with the agent id, tool call id, and decoded args
- `:widget` — optional module implementing `Planck.Agent.Widget`, pairing this
  tool with a UI widget. `nil` for most tools. See `Planck.Agent.Sidecar.list_widgets/0`,
  which derives the sidecar's widget list from tools that set this field.

# `ui_content`

```elixir
@type ui_content() ::
  %{kind: :text, text: String.t()}
  | %{kind: :widget, label: String.t(), widget: String.t(), data: term()}
```

A UI side effect attached to a tool result — see `execute_fn`'s 3-element
return form. Deliberately not just "the widget to open": what shows up in
the chat transcript to represent this side effect is a different thing
from what the widget itself contains (`c:Planck.Agent.Widget.render/1`), and
not every UI side effect involves a widget at all.

- `%{kind: :text, text: text}` — a UI-only note shown in the chat, invisible
  to the LLM, no widget involved.
- `%{kind: :widget, label: label, widget: widget_id, data: data}` — a button
  in the chat labeled `label` (sidecar-authored and human-readable — the
  tool decides what invites the click; the widget decides what's inside
  once opened). `widget_id` names a `Planck.Agent.Widget` (its `id/0`,
  paired via a `Planck.Agent.Tool`'s `:widget` field). `data` is an opaque
  initial snapshot for that widget's first paint, avoiding a round-trip
  through `render/1` to open it — optional, may be `nil`.

# `new`

```elixir
@spec new(keyword()) :: t()
```

Build a `Planck.Agent.Tool` from keyword options.

## Examples

    iex> Tool.new(
    ...>   name: "read_file",
    ...>   description: "Read a file",
    ...>   parameters: %{"type" => "object", "properties" => %{"path" => %{"type" => "string"}}, "required" => ["path"]},
    ...>   execute_fn: fn _agent_id, _id, %{"path" => path} -> File.read(path) end
    ...> )
    %Planck.Agent.Tool{name: "read_file", ...}

# `to_ai_tool`

```elixir
@spec to_ai_tool(t()) :: Planck.AI.Tool.t()
```

Convert to a `Planck.AI.Tool` for use in `Planck.AI.Context` (drops `execute_fn`).

# `validate_args`

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

Validate `args` against the tool's JSON schema using `ExJsonSchema`.

Returns `:ok` or `{:error, message}` suitable for returning directly from an
`execute_fn`. Called automatically by the agent before invoking `execute_fn`,
so individual tools do not need to duplicate this check.

---

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