# `WardrikCore.Tool`

A tool an agent may call.

    WardrikCore.Tool.new!(
      name: "order_status",
      description: "Say where an order is.",
      schema: [order_id: [type: :string, required: true]],
      run: fn %{order_id: id}, _context -> {:ok, MyApp.Orders.status(id)} end,
      annotations: [read_only: true]
    )

## Return values

`run` takes the checked arguments and the `WardrikCore.Context`, and returns:

  * `{:ok, answer}` or `{:error, reason}`.
  * `{:ok, answer, context}` or `{:error, reason, context}` - with the context changed, to
    report on the step. A call that failed after paying returns the error form.
  * `{:park, reason}` - waits for a person.

For example, to report what a paid API cost:

    run: fn %{email: email}, context ->
      found = Enrich.lookup(email)
      {:ok, found, WardrikCore.Context.measure(context, :usd, 0.01)}
    end

## Labels

An answer can carry labels in its metadata, such as `"outside"` for text nobody on the agent's
side wrote. The run carries the label from then on. See `WardrikCore.Context.labels/1`.

    {:ok, %ReqLLM.ToolResult{output: page, metadata: %{labels: ["outside"]}}}

## Annotations

Facts about a tool that any package can read. Give them as `:annotations` to `new!/1`, or with
`annotate/2`.

  * `:read_only` - it changes nothing.
  * `:destructive` - what it changes can't be undone.
  * `:idempotent` - a call is safe to repeat, such as a payment with an idempotency key.
  * `:open_world` - it reaches things outside the agent's own systems, such as the web.
  * `:timeout` - how long a call may take, in milliseconds.
  * `:resources` - what a call acts on, as a function of its arguments, such as
    `fn args -> [args["path"]] end`.
  * `:labels` - labels every answer carries, such as `["outside"]` for a web search.

The first four are MCP's tool annotations. A tool that doesn't set them has none of them.

A call to a tool that's `read_only` or `idempotent` is retried when it fails, and repeated when
it's lost in a crash. Any other call lost that way parks as `:in_doubt` until a person answers.
See `repeatable?/1`.

# `answer`

```elixir
@type answer() ::
  {:ok, term()}
  | {:ok, term(), WardrikCore.Context.t()}
  | {:error, term()}
  | {:error, term(), WardrikCore.Context.t()}
  | {:park, term()}
```

# `t`

```elixir
@type t() :: %WardrikCore.Tool{
  annotations: %{optional(atom()) =&gt; term()},
  description: String.t(),
  name: String.t(),
  private: map(),
  req_llm: ReqLLM.Tool.t(),
  run: (map(), WardrikCore.Context.t() -&gt; answer()),
  schema: keyword() | map()
}
```

# `annotate`

```elixir
@spec annotate(t(), keyword() | map()) :: t()
```

Adds `annotations`, such as `read_only: true`. Replaces any with the same key.

# `annotation`

```elixir
@spec annotation(t(), atom(), term()) :: term()
```

Returns the annotation `key`, or `default` if the tool doesn't set it.

# `call`

```elixir
@spec call([t()], WardrikCore.ToolCall.t(), WardrikCore.Context.t()) :: answer()
```

Runs the tool `call` names, after checking its arguments against the tool's schema.

Returns `{:error, reason}` if no tool has that name or the arguments don't match.

# `get_private`

```elixir
@spec get_private(t(), term(), term()) :: term()
```

Returns the private value under `key`, or `default`.

# `new!`

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

Builds a tool.

## Options

  * `:name` - required.
  * `:description` - required. What the model reads to decide when to call it.
  * `:run` - required. A 2-arity function of the arguments and the context.
  * `:schema` - the arguments, as a NimbleOptions-style keyword list or a JSON Schema map.
    `[]` unless set.
  * `:annotations` - see above.

Raises if a required option is missing.

# `put_private`

```elixir
@spec put_private(t(), term(), term()) :: t()
```

Puts `value` in the private data under `key`. Use your own module as the key.

# `repeatable?`

```elixir
@spec repeatable?(t()) :: boolean()
```

Returns whether a call to the tool is safe to repeat: it's `read_only` or `idempotent`.

# `update_private`

```elixir
@spec update_private(t(), term(), term(), (term() -&gt; term())) :: t()
```

Updates the private value under `key` with `fun`. Puts `default` if there's none yet.

---

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