# Tool Configuration `HasToolModel::toolConfig()` returns a `ToolConfigBuilder` for one Eloquent model. `ToolBuilder` freezes all registered builders into one resolved registry and then creates the actual tool instances. ## What The Builder Controls - `label(string)`: human-readable label used in tool names and descriptions - `toolKey(string)`: extra name segment to avoid collisions, for example `read_cms_articles` - `modelDescription(string)`: free-text description added to the tool description - `allowRead(bool = true)`: opt in to generating the read tool for this model - `reading(callable)`: per-request policy callback returning `Decision` or `bool` ## Limits And Defaults The fluent builder defaults are stricter than the raw `ToolConfig` value object defaults: - `maxLimit(25)` by default on `ToolConfigBuilder` - `maxOffset(10000)` by default - `whereMaxDepth(3)` by default - `whereMaxConditionsPerGroup(25)` by default - `relationPathMaxDepth(5)` by default Important runtime defaults: - request `limit` defaults to `10` when omitted - request `offset` defaults to `0` when omitted `maxOffset(0)` is a valid way to disallow paging past the first page. ## Fields The array passed into `newToolConfig([...])`, usually from inside your model's `toolConfig()` method, is a field-description map, not the actual runtime allowlist. The runtime column set is derived from the live model schema: - database columns are discovered from the model's table - casts refine the detected data types - enum columns expose their known enum values - `visible` and `hidden` filter which columns are available That means field descriptions improve the generated tool description, but the actual queryable columns come from the model schema snapshot. ## Relations `relations([...])` is explicit opt-in for relation-aware features. Configured relations drive: - `with` - `withAggregate` - nested eager-load paths derived from related model configs - dotted relation-path columns in `where`, `select`, `groupBy`, `orderBy`, and `expressions` where the relation kind supports them Those dotted relation-path columns trigger join planning automatically. The current tool does not expose a user-specified top-level `join` request field. Important constraint: - if a related model is not also registered in the same `ToolBuilder`, the relation is dropped from the resolved schema and a warning is logged ## Scopes `scopes([...])` exposes named local scopes the AI may apply. Those scopes are applied: - on the root query for the current model - on related queries used for eager loading - on related queries used for `withAggregate` - on auto-derived relation-path joins where the related model contributes scoped columns ## Read Policy Callback `reading(function (Request $request, array $appliers) { ... })` runs after the request has been parsed and validated into appliers. It may return: - `Decision::accept()` or `true` - `Decision::deny($reason)` or `false` - `Decision::approve($reason)` If the callback returns anything else, the tool throws an `InvalidArgumentException`.