Appearance
Custom Tools
Extend Iris's capabilities by creating custom tools.
Creating a Tool
Tools live in app/Tools/ and extend Prism's Tool class.
Basic Structure
php
<?php
declare(strict_types=1);
namespace App\Tools;
use Prism\Prism\Tool;
class MyCustomTool extends Tool
{
public function __construct()
{
$this
->as('my_custom_tool')
->for('Description of what this tool does and when to use it')
->withStringParameter('input', 'Description of the input')
->using($this);
}
public function __invoke(string $input): string
{
return "Processed: {$input}";
}
}With Dependencies
php
public function __construct(
protected SomeService $service,
protected User $user, // Automatically injected
) {
$this
->as('my_custom_tool')
->for('Does something useful')
->withStringParameter('input', 'The input to process')
->using($this);
}
public function __invoke(string $input): string
{
$result = $this->service->process($input, $this->user);
return "Result: {$result}";
}Parameter Types
php
->withStringParameter('name', 'A text value')
->withNumberParameter('count', 'A numeric value')
->withBooleanParameter('enabled', 'A true/false value')
->withArrayParameter('items', 'A list of values')
->withEnumParameter('status', 'Status option', ['pending', 'active', 'done'])Mark parameters as optional:
php
->withStringParameter('optional_param', 'Can be omitted', required: false)Returning Results
Simple String
php
return "Successfully processed: {$input}";With Artifacts
For images or files:
php
use Prism\Prism\ValueObjects\Artifact;
use Prism\Prism\ValueObjects\ToolOutput;
return new ToolOutput(
result: json_encode(['status' => 'success']),
artifacts: [new Artifact(
data: base64_encode($imageData),
mimeType: 'image/png',
metadata: ['description' => $description],
)],
);Registering Tools
Add your tool to app/Services/ToolRegistry.php:
php
public function __construct(
// ... existing tools
protected MyCustomTool $myCustomTool,
) {}
public function getTools(): array
{
return [
// ... existing tools
$this->myCustomTool,
];
}Laravel's container automatically injects dependencies. The User binding is configured in AppServiceProvider.
Best Practices
Clear Descriptions
php
// Good - helps LLM understand when to use
->for('Store important information about the user for later recall')
// Bad - vague
->for('Saves data')Informative Results
php
// Good
return "Created task 'Buy groceries' with high priority, due tomorrow.";
// Bad
return "Done";Error Handling
php
try {
$event = $this->calendar->find($eventId);
$this->calendar->delete($event);
return "Deleted event: {$event->title}";
} catch (NotFoundException $e) {
return "Could not find an event with ID '{$eventId}'.";
} catch (Throwable $e) {
Log::error('Event deletion failed', ['error' => $e->getMessage()]);
return "An error occurred. Please try again.";
}Testing Tools
php
it('returns weather information', function () {
Http::fake([
'api.weatherapi.com/*' => Http::response([
'location' => ['name' => 'London'],
'current' => ['temp_c' => 15, 'condition' => ['text' => 'Cloudy']],
]),
]);
$tool = new WeatherTool();
$result = $tool('London');
expect($result)->toContain('London')->toContain('15°C');
});