# Tools

The `@tool` decorator and tool primitives. For the conceptual model and the LLM-tool-loop interaction see [`concepts/behaviors`](https://docs.activegraph.ai/concepts/behaviors/index.md), and for tools inside a pack see the [authoring packs](https://docs.activegraph.ai/guides/authoring-packs/index.md) guide.

## Decorator + base

Register a function as a Tool.

The decorated function's signature is `(args: input_schema, ctx: ToolContext) -> output_schema`. The runtime validates `args` against `input_schema` before invocation and validates the return value against `output_schema` after.

Keyword-only on purpose — too many fields for safe positional binding.

Metadata plus the callable for a runtime-invokable tool.

`fn` runs as `fn(args, ctx)`: `args` is validated against `input_schema` before invocation and the return value against `output_schema` after (both Pydantic models). `cost_per_call` feeds the budget's cost dimension; `deterministic` declares whether replay may re-invoke (CONTRACT v0.7 #7 — False means replay must serve the recorded fixture or fail loud). Instances come from `@tool`; the runtime owns invocation and the `tool.requested` / `tool.responded` event pair.

## `to_definition()`

Provider-facing tool definition.

Sent in the `tools=` parameter to `LLMProvider.complete()`. Anthropic and OpenAI both accept a similar shape; the provider translates if needed.

What a tool function body sees. CONTRACT v0.7 #5.

Deliberately narrow: the triggering behavior and event id, the active frame, an `idempotency_key` to forward to external APIs (the runtime never uses it for dedupe — caching is the cache's job), the decorator's `timeout_seconds` (advisory; the runtime does not preempt), and a per-tool logger. No graph reference — tools that need graph state close over it explicitly at registration (see `make_graph_query_tool`). `external_io_mode` defaults to `"forbid"`; runtime dispatch supplies `"runtime_recorded"` and an intentional replay bypass must say `"live_unrecorded"`.

## Registry helpers

Return a copy of the global `@tool` registry, in registration order.

This is the list a new `Runtime` snapshots when `tools=[...]` is not passed. It is a copy: mutating the returned list neither registers nor unregisters anything — use the `@tool` decorator to add and :func:`clear_tool_registry` to reset.

Empty the global `@tool` registry. Test-isolation helper.

Decoration pushes into a module-level list that persists for the life of the process, so tests that register tools clear it between cases (mirroring the `@behavior` registry's isolation pattern). Runtimes constructed *before* the clear keep working — the registry is snapshotted at runtime construction, not read per-dispatch.
