Skip to main content
While the #[Tool] attribute is recommended for most use cases, Tool Classes and Inline Tools offer additional flexibility for reusable or dynamically generated tools.

Tool Classes

Tool classes are standalone PHP classes that encapsulate tool logic. They’re ideal for:
  • Complex tools with extensive logic
  • Tools shared across multiple agents
  • Tools requiring their own dependencies or configuration

Creating a Tool Class

namespace App\AiTools;

use LarAgent\Tool;
use LarAgent\Core\Abstractions\DataModel;

class WeatherTool extends Tool
{
    protected string $name = 'get_weather';

    protected string $description = 'Get the current weather for a location';

    protected array $properties = [
        'location' => [
            'type' => 'string',
            'description' => 'The city and state, e.g. San Francisco, CA',
        ],
        'unit' => [
            'type' => 'string',
            'description' => 'Temperature unit',
            'enum' => ['celsius', 'fahrenheit'],
        ],
    ];

    protected array $required = ['location'];

    protected function handle(array|DataModel $input): mixed
    {
        $location = $input['location'];
        $unit = $input['unit'] ?? 'celsius';

        return WeatherService::get($location, $unit);
    }
}
The handle() method replaces execute() for class-based tools from v1.2. It provides automatic DataModel and Enum conversion. The execute() method still works for backward compatibility.

Registering Tool Classes

Add tool classes to the $tools property in your agent:
class WeatherAgent extends Agent
{
    protected $tools = [
        \App\Tools\WeatherTool::class,
        \App\Tools\LocationTool::class,
    ];
}
Or register them in the registerTools() method by instantiating:
public function registerTools()
{
    return [
        new \App\AiTools\WeatherTool(),
        new \App\AiTools\LocationTool(),
    ];
}
Or add them at runtime:
$agent->withTool(WeatherTool::class);
// or
$agent->withTool(new WeatherTool());

Tool Class Properties

PropertyTypeDescription
$namestringUnique identifier for the tool
$descriptionstringDescription shown to the LLM
$propertiesarrayParameter definitions with types and descriptions
$requiredarrayList of required parameter names
$dataModelClass?stringDataModel class to use as the tool’s input schema
$metaDataarrayOptional metadata (not sent to LLM)

Property Definitions

Properties are OpenAPI compatible schemas. Each property in $properties can have:
protected array $properties = [
    'param_name' => [
        'type' => 'string',           // string, number, integer, boolean, array, object
        'description' => 'Help text',
        'enum' => ['a', 'b', 'c'],    // Optional: restrict to specific values
    ],
];

Using DataModel as Input Schema

Define your entire tool schema using a DataModel class:
use LarAgent\Tool;
use LarAgent\Core\Abstractions\DataModel;

class TaskDataModel extends DataModel
{
    public string $title;
    public int $estimatedHours;
    public ?string $description = null;
}

class CreateTaskTool extends Tool
{
    protected string $name = 'create_task';
    protected string $description = 'Create a new task';
    
    // Automatically populates all properties from TaskDataModel
    protected ?string $dataModelClass = TaskDataModel::class;
    
    protected function handle(array|DataModel $input): mixed
    {
        // $input is already a TaskDataModel instance!
        $task = $input;
        return "Task '{$task->title}' created with {$task->estimatedHours} hours.";
    }
}

DataModel in Properties Array

Mix DataModel classes directly in your $properties array:
class PersonDataModel extends DataModel
{
    public string $name;
    public string $email;
}

class AddressDataModel extends DataModel
{
    public string $street;
    public string $city;
}

class ScheduleMeetingTool extends Tool
{
    protected string $name = 'schedule_meeting';
    protected string $description = 'Schedule a meeting';
    
    protected array $properties = [
        'title' => ['type' => 'string'],
        'attendee' => PersonDataModel::class,  // Auto-expanded!
        'location' => AddressDataModel::class, // Auto-expanded!
    ];
    
    protected array $required = ['title', 'attendee', 'location'];
    
    protected function handle(array|DataModel $input): mixed
    {
        // DataModel properties are automatically converted!
        $attendee = $input['attendee'];  // PersonDataModel instance
        $location = $input['location'];  // AddressDataModel instance
        
        return "Meeting '{$input['title']}' scheduled with {$attendee->name}";
    }
}

Inline Tools

Inline tools are created programmatically using the fluent Tool API. They’re ideal for:
  • Tools that depend on runtime context (user state, request data)
  • Dynamically generated tools based on configuration
  • Quick prototyping before extracting to a class

Creating Inline Tools

Use the registerTools() method in your agent:
use LarAgent\Tool;

class MyAgent extends Agent
{
    public function registerTools()
    {
        $user = auth()->user();

        return [
            Tool::create('get_user_location', "Get the current user's location")
                ->setCallback(fn() => $user->location->city),

            Tool::create('get_weather', 'Get weather for a location')
                ->addProperty('location', 'string', 'City name')
                ->addProperty('unit', 'string', 'Temperature unit', ['celsius', 'fahrenheit'])
                ->setRequired('location')
                ->setCallback(fn($location, $unit = 'celsius') => 
                    WeatherService::get($location, $unit)
                ),
        ];
    }
}

Fluent API Reference

Tool::create(string $name, string $description)
    ->addProperty(string $name, string $type, string $description, ?array $enum = null)
    ->addDataModelAsProperties(string $dataModelClass)
    ->setRequired(string|array $properties)
    ->setCallback(callable $callback);

Using DataModel with Inline Tools

Use addProperty() with a DataModel class as the type:
use LarAgent\Tool;

$tool = Tool::create('schedule_meeting', 'Schedule a meeting')
    ->addProperty('title', 'string', 'Meeting title')
    ->addProperty('attendee', PersonDataModel::class)  // DataModel as type!
    ->addProperty('location', AddressDataModel::class) // DataModel as type!
    ->setRequired(['title', 'attendee', 'location'])
    ->setCallback(function (string $title, PersonDataModel $attendee, AddressDataModel $location) {
        return "Meeting '{$title}' scheduled with {$attendee->name} at {$location->city}";
    });
Or use addDataModelAsProperties() to use an entire DataModel as your tool’s input:
use LarAgent\Tool;
use LarAgent\Core\Abstractions\DataModel;

class TaskDataModel extends DataModel
{
    public string $title;
    public int $estimatedHours;
    public ?string $description = null;
}

$tool = Tool::create('create_task', 'Create a task')
    ->addDataModelAsProperties(TaskDataModel::class)
    ->setCallback(function (TaskDataModel $task) {
        return "Task '{$task->title}' created with {$task->estimatedHours} hours.";
    });

Callback Options

The setCallback() method accepts any PHP callable, including:
// Closure
->setCallback(fn($param) => doSomething($param))

// Function name
->setCallback('myGlobalFunction')

// Class method (array syntax)
->setCallback([$this, 'methodName'])

Runtime Tool Addition

Add inline tools at runtime using withTool():
$tool = Tool::create('custom_tool', 'Do something custom')
    ->addProperty('input', 'string', 'The input value')
    ->setCallback(fn($input) => processInput($input));

$response = MyAgent::for('user-123')
    ->withTool($tool)
    ->respond('Use the custom tool');

Context-Aware Tools Example

public function registerTools()
{
    $user = auth()->user();
    $tools = [];

    // Always available
    $tools[] = Tool::create('get_time', 'Get current server time')
        ->setCallback(fn() => now()->toIso8601String());

    // User-specific tool
    $tools[] = Tool::create('get_my_orders', 'Get my recent orders')
        ->addProperty('limit', 'integer', 'Number of orders to return')
        ->setCallback(fn($limit = 5) => 
            $user->orders()->latest()->take($limit)->get()->toArray()
        );

    // Permission-based tool
    if ($user->can('view_reports')) {
        $tools[] = Tool::create('get_sales_report', 'Get sales report')
            ->addProperty('period', 'string', 'Time period', ['day', 'week', 'month'])
            ->setCallback(fn($period) => ReportService::sales($period));
    }

    return $tools;
}

Choosing the Right Approach

ApproachBest For
#[Tool] AttributeMost use cases β€” simple, type-safe, IDE support
Tool ClassesComplex, reusable tools with multiple dependencies
Inline ToolsDynamic, context-dependent, or quick prototypes
Start with the #[Tool] attribute. Use Inline Tools when the tool depends on runtime context.

Next Steps