Laravel Daily's

Give Your Laravel Agents Real Power: A Practical Guide to Tool Calling with the AI SDK

Bright illustration of a Laravel AI agent connected to database, embeddings, web search, file storage, and MCP tools

An AI agent becomes useful when it can do more than generate text.

It needs access to your application’s data, search systems, files, and external services. In the Laravel AI SDK, these capabilities are exposed through tools.

A tool is a PHP class. The model decides when to call it. Laravel executes the class and sends the result back to the model. Your application keeps control of the logic, permissions, and data boundaries.

This makes tool calling a natural fit for Laravel, the productive PHP web framework used to build modern applications.

Illustration of a Laravel AI agent selecting database and embedding search tools

How Laravel AI SDK tools work

A tool has three responsibilities:

  • description() tells the model when the tool is useful.
  • schema() defines the arguments the model may provide.
  • handle() performs the operation and returns the result.

Start by installing the SDK:

composer require laravel/ai

php artisan vendor:publish --provider="Laravel\Ai\AiServiceProvider"

php artisan migrate

Create a tool with Artisan:

php artisan make:tool LookupTickets

Laravel places the generated class in app/Ai/Tools. A simple tool looks like this:

<?php

namespace App\Ai\Tools;

use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Ai\Contracts\Tool;
use Laravel\Ai\Tools\Request;
use Stringable;

class LookupTickets implements Tool
{
    public function __construct(
        protected int $userId,
    ) {}

    public function description(): Stringable|string
    {
        return 'Find recent support tickets belonging to the current user.';
    }

    public function schema(JsonSchema $schema): array
    {
        return [
            'status' => $schema->string()
                ->enum(['open', 'closed', 'pending'])
                ->description('Optional ticket status filter.'),
        ];
    }

    public function handle(Request $request): Stringable|string
    {
        $status = $request->string('status')->toString();

        return Ticket::query()
            ->where('user_id', $this->userId)
            ->when($status, fn ($query) => $query->where('status', $status))
            ->latest()
            ->limit(10)
            ->get(['id', 'subject', 'status', 'created_at'])
            ->toJson();
    }
}

The constructor is important. The user ID comes from your application code, not from the prompt. That prevents the model from changing the account whose data it can access.

Keep tool responses small. Select only the columns the agent needs, limit the number of records, and avoid returning sensitive fields.

Register tools with an agent

Create an agent with:

php artisan make:agent SupportAgent

Then implement HasTools and return the available tools from tools():

<?php

namespace App\Ai\Agents;

use App\Ai\Tools\LookupTickets;
use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Contracts\HasTools;
use Laravel\Ai\Promptable;

class SupportAgent implements Agent, HasTools
{
    use Promptable;

    public function __construct(
        protected int $userId,
    ) {}

    public function instructions(): string
    {
        return <<<'INSTRUCTIONS'
            You are a support assistant.
            Use tools to inspect the user's tickets and knowledge base.
            Do not invent ticket details.
            If the available data does not answer the question, say so.
        INSTRUCTIONS;
    }

    public function tools(): iterable
    {
        return [
            new LookupTickets($this->userId),
        ];
    }
}

Prompt the agent from a controller or route:

$response = (new SupportAgent(auth()->id()))
    ->prompt('What happened with my open support tickets?');

return (string) $response;

The SDK manages the tool loop. The model receives the tool description and schema, chooses a tool when appropriate, and receives the tool result before producing its final response.

This is one reason the SDK belongs among modern PHP developer tools. The integration uses familiar classes, dependency injection, Eloquent, validation, and Laravel conventions.

Validate tool arguments at runtime

JSON Schema helps the model form valid arguments. It is not a replacement for server-side validation.

Use the tool request’s validate() method before running application logic:

public function handle(Request $request): Stringable|string
{
    $validated = $request->validate([
        'status' => ['nullable', 'string', 'in:open,closed,pending'],
    ]);

    return Ticket::query()
        ->where('user_id', $this->userId)
        ->when(
            $validated['status'] ?? null,
            fn ($query, $status) => $query->where('status', $status)
        )
        ->latest()
        ->limit(10)
        ->get(['id', 'subject', 'status', 'created_at'])
        ->toJson();
}

Validation errors are returned to the model as part of the tool invocation. Make messages specific when the model needs to correct its arguments.

For database tools, also consider:

  • A read-only database user.
  • Explicit table and column allowlists.
  • Fixed result limits.
  • Authorization checks outside the model’s control.
  • Logging InvokingTool, ToolInvoked, and ToolFailed events.

Tools that mutate data should use the human approval flow. Read operations are easier to reason about and safer to expose first.

Add semantic search with embeddings

Support questions do not always contain exact database values.

A customer may ask, “Why can’t I see the reports I paid for?” That question may not match a ticket subject or status. Semantic search can retrieve relevant knowledge-base documents by meaning.

First, add a vector column:

Schema::ensureVectorExtensionExists();

Schema::create('knowledge_articles', function (Blueprint $table) {
    $table->id();
    $table->string('title');
    $table->text('content');
    $table->vector('embedding', dimensions: 1536)->index();
    $table->timestamps();
});

Generate embeddings when articles are created:

use Illuminate\Support\Str;

$article->embedding = Str::of($article->content)->toEmbeddings();
$article->save();

Your database must support vector queries. Laravel documents native vector support for PostgreSQL with pgvector and MariaDB 11.7 or later.

The AI SDK includes a SimilaritySearch tool. Register it alongside your database lookup tool:

use App\Models\KnowledgeArticle;
use Laravel\Ai\Tools\SimilaritySearch;

public function tools(): iterable
{
    return [
        new LookupTickets($this->userId),

        SimilaritySearch::usingModel(
            model: KnowledgeArticle::class,
            column: 'embedding',
            minSimilarity: 0.7,
            limit: 5,
        )->withDescription(
            'Search the support knowledge base for articles relevant to the user’s question.'
        ),
    ];
}

For tenant-specific content, scope the search query:

SimilaritySearch::usingModel(
    model: KnowledgeArticle::class,
    column: 'embedding',
    minSimilarity: 0.7,
    limit: 5,
    query: fn ($query) => $query->where('team_id', $this->teamId),
)

The resulting support agent can now choose between two search strategies:

  1. A structured lookup for tickets and account records.
  2. A semantic lookup for documentation and troubleshooting guidance.

That combination is a practical foundation for retrieval-augmented generation in Laravel.

Bright illustration of a PHP tool class with description, JSON schema, and handle execution

Recover from incorrect tool calls

Models sometimes call a tool with a slightly incorrect name. The RepairToolCalls attribute lets an agent recover from unknown local tool calls.

use Laravel\Ai\Attributes\RepairToolCalls;
use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Contracts\HasTools;
use Laravel\Ai\Promptable;

#[RepairToolCalls]
class SupportAgent implements Agent, HasTools
{
    use Promptable;

    // ...
}

Laravel sends the failed call back to the model with the available local tool names. The model can then correct the call and try again.

If Laravel calculates the maximum number of steps automatically, the repair step is added for you. Explicit MaxSteps limits remain unchanged.

Reduce token usage with deferred tools

Every registered tool contributes a description and schema to the request. A large catalog can consume context and make tool selection less accurate.

For OpenAI and Anthropic, use ToolSearch to defer less frequently used tools:

use App\Ai\Tools\LookupInvoices;
use App\Ai\Tools\RefundOrder;
use Laravel\Ai\Providers\Tools\ToolSearch;

public function tools(): iterable
{
    return [
        new LookupTickets($this->userId),

        new ToolSearch(tools: [
            new LookupInvoices($this->userId),
            new RefundOrder($this->userId),
        ]),
    ];
}

The provider searches the deferred tools and loads detailed definitions only when they are relevant.

Anthropic also supports a search strategy:

new ToolSearch(
    tools: [new LookupInvoices($this->userId)],
    strategy: 'bm25',
)

Keep at least one tool outside the wrapper when using Anthropic. Providers that do not support tool search will reject the configuration rather than silently ignoring it.

Use provider tools when the provider already has the capability

Some tools run inside the AI provider instead of your Laravel application.

Web search and fetching

Use WebSearch for current information:

use Laravel\Ai\Providers\Tools\WebSearch;

public function tools(): iterable
{
    return [
        (new WebSearch)
            ->max(5)
            ->allow(['laravel.com', 'php.net']),
    ];
}

Use WebFetch when the agent needs to read known URLs:

use Laravel\Ai\Providers\Tools\WebFetch;

public function tools(): iterable
{
    return [
        (new WebFetch)
            ->max(3)
            ->allow(['laravel.com']),
    ];
}

Provider support varies. Check the provider tools documentation before selecting a model.

File search

For provider-managed vector stores, use FileSearch:

use Laravel\Ai\Providers\Tools\FileSearch;

public function tools(): iterable
{
    return [
        new FileSearch(stores: ['support-knowledge-base']),
    ];
}

Use metadata filters when your files contain fields such as department, author, product, or publication status.

Illustration of a Laravel AI agent using web search, cloud files, and MCP server connections

Give agents controlled filesystem access

The FileStorage factory exposes Laravel filesystem operations as tools.

For read-only access:

use Laravel\Ai\Tools\FileStorage;

public function tools(): iterable
{
    return FileStorage::readOnly('local');
}

For the full set of operations:

public function tools(): iterable
{
    return FileStorage::all('s3');
}

The full collection can list, read, inspect, copy, write, generate URLs, and delete files. Prefer readOnly() unless the agent genuinely needs to modify storage.

You can also remove individual capabilities:

use Laravel\Ai\Tools\FileStorage;
use Laravel\Ai\Tools\Filesystem\DeleteFile;

public function tools(): iterable
{
    return FileStorage::all('s3')
        ->reject(fn ($tool) => $tool instanceof DeleteFile);
}

For destructive file operations, combine this with human approval.

Connect MCP tools through Laravel MCP

The AI SDK can consume tools exposed by a Model Context Protocol server.

Install Laravel MCP:

composer require laravel/mcp

Then attach tools from a remote MCP server:

use Laravel\Mcp\Client;

public function tools(): iterable
{
    return [
        ...Client::web('https://mcp.example.com')
            ->withToken($this->token)
            ->tools(),
    ];
}

You can also use a named client:

use Laravel\Mcp\Facades\Mcp;

public function tools(): iterable
{
    return [
        ...Mcp::client('github')->tools(),
    ];
}

Or connect to a local server:

use Laravel\Mcp\Client;

public function tools(): iterable
{
    return [
        ...Client::local('php', ['artisan', 'mcp:start'])->tools(),
    ];
}

The distinction is straightforward:

  • AI SDK tools let your Laravel agent call functionality.
  • Laravel MCP tools let external AI clients call your application.

Read the Laravel MCP documentation for server registration, authentication, authorization, and testing.

Build a focused tool surface

Tool calling works best when each agent has a clear purpose.

A support agent may need ticket lookup, semantic knowledge search, and read-only files. It probably does not need deployment controls, billing mutations, or access to every internal table.

Start with one focused tool. Validate its arguments. Scope it to the authenticated user or tenant. Return a small result. Then add semantic search or provider tools when the workflow requires them.

That approach gives your Laravel application practical AI capabilities without introducing a second service or abandoning the conventions that make PHP development productive. Whether you need to build a REST API with PHP or add an AI assistant to an existing product, tools give your agents a controlled way to work with real application data.

The model supplies reasoning. Your Laravel tools supply the power and the boundaries.

Previous
Route-Based Modals in Inertia 3.x: Full Pages, Reused as Overlays in Laravel + Vue
Next
Layout Props in Inertia 3.x: Dynamic Headers, Sidebars, and Toasts in Your Laravel + Vue App