Skip to content

Sponsor

Custom Tools ​

Create a class that extends Tackle\Tools\AbstractTool. In Tackle 1.56.8 and later, register it with the tackle.tools container tag. You can also extend DefaultCodingAgent and rebind its contract as shown below.

Step 1 — Generate the tool (or write it manually) ​

bash
php artisan tackle:tool ReadDatabase

Step 2 — Implement the tool ​

php
// app/Ai/Tools/ReadDatabase.php
namespace App\Ai\Tools;

use Illuminate\Contracts\JsonSchema\JsonSchema;
use Illuminate\Support\Facades\DB;
use Laravel\Ai\Tools\Request;
use Tackle\Tools\AbstractTool;

class ReadDatabase extends AbstractTool
{
    public function description(): string
    {
        return 'Run a read-only SQL query and return results as JSON. SELECT only.';
    }

    public function schema(JsonSchema $schema): array
    {
        return [
            'query' => $schema->string()
                ->description('The SELECT query to run.')
                ->required(),
        ];
    }

    public function handle(Request $request): string
    {
        $sql = $request->string('query', '');

        if (! str_starts_with(strtolower(ltrim($sql)), 'select')) {
            return 'Only SELECT queries are allowed.';
        }

        return json_encode(DB::select($sql), JSON_PRETTY_PRINT);
    }
}

Step 3 — Extend the agent (or generate it) ​

bash
php artisan tackle:agent MyCodingAgent

Step 4 — Wire in your tool ​

php
// app/Ai/MyCodingAgent.php
namespace App\Ai;

use App\Ai\Tools\ReadDatabase;
use Tackle\Agents\DefaultCodingAgent;

class MyCodingAgent extends DefaultCodingAgent
{
    public function __construct(
        private ReadDatabase $readDatabase,
        ...$args,
    ) {
        parent::__construct(...$args);
    }

    public function tools(): iterable
    {
        return [...parent::tools(), $this->readDatabase];
    }
}

Step 5 — Rebind in your service provider ​

php
// app/Providers/AppServiceProvider.php
use App\Ai\MyCodingAgent;
use Tackle\Contracts\CodingAgent;

public function register(): void
{
    $this->app->bind(CodingAgent::class, MyCodingAgent::class);
}

The Laravel container resolves all constructor dependencies automatically, so your tool class can type-hint anything it needs (DB connections, services, etc.).

Tool contract ​

Every tool receives a Laravel\Ai\Tools\Request object in handle(). It behaves like a read-only request bag:

php
$request->string('key', 'default');   // string value
$request->boolean('key', false);      // boolean value
$request->integer('key', 0);          // integer value
$request->get('key', 'default');      // raw value
$request->all();                      // all arguments as array

When a tool should refuse an action, return a string explaining why rather than throwing an exception. The agent reads the refusal message and reroutes itself accordingly.

Generators and stubs ​

Tackle ships generator commands so you don't have to look up method signatures:

bash
# Scaffold a tool at app/Ai/Tools/MyTool.php
php artisan tackle:tool MyTool

# Scaffold an agent that extends DefaultCodingAgent (most common)
php artisan tackle:agent MyAgent

# Scaffold a bare CodingAgent implementation
php artisan tackle:agent MyAgent --full

To customise the generated stubs, publish them first:

bash
php artisan vendor:publish --tag="tackle-stubs"

This copies the stubs to stubs/tackle/ in your project root. Both commands check for published stubs before falling back to the package defaults.

Register tools from a package ​

Since Tackle 1.56.8, a package or application service provider can register tools without replacing the default coding agent:

php
public function register(): void
{
    $this->app->tag([
        \App\Ai\Tools\ReadDatabase::class,
    ], 'tackle.tools');
}

The default coding agent resolves tagged tools through Laravel's container, applies the tackle.tools configuration allowlist, and wraps them with its normal shield and tool-event handling. The container tag and configuration key have the same name: the tag contributes tools; the configuration key restricts the available tool names.

Custom agents that replace tools() must include package tools explicitly or call parent::tools(). Lean agents retain their narrower tool list. MCP tools are separately configured in tackle.mcp.tools.

Tackle Grokbot uses this extension point to register its discovery, sending, and delivery-history tools.

Released under the MIT License.