Packstub.

Installation

Requirements

PHP 8.4 or newer
Laravel 13.x
Filament 5.x
laravel/ai ^0.11
laravel/mcp ^0.9
laravel/sanctum ^4 (tokens for MCP clients)

The package requires laravel/ai, laravel/mcp and laravel/sanctum, so Composer installs them for you.

Install

composer require packstub/filament-agents
php artisan packstub-agents:install
php artisan filament:assets

The install command publishes config/packstub-agents.php, offers to run the migrations and scaffolds app/Ai/Agents/Assistant.php. The migrations create the agent_limits table and the chat tables (agent_conversations, agent_conversation_messages, agent_message_feedback). They run from the package by default; a database-per-tenant app publishes and splits them, see Tenancy.

Sanctum

MCP clients authenticate with Sanctum personal access tokens, so your user model needs the HasApiTokens trait and the personal_access_tokens table:

use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens;
}
php artisan vendor:publish --tag=sanctum-migrations
php artisan migrate

Skip this when you only want the in-panel chat and set AGENT_MCP_ENABLED=false.

The theme

Filament v5 compiles plugin views into your panel's custom theme, so add the package views to it:

@import '../../../../vendor/filament/filament/resources/css/theme.css';

@source '../../../../vendor/packstub/filament-agents/resources/views';

Then rebuild the theme (npm run build or bun run build). The package's own stylesheet (agents.css) is registered as a Filament asset and published by filament:assets.

Register the plugin

use App\Ai\Agents\Assistant;
use App\Mcp\Servers\AcmeServer;
use Packstub\Agents\AgentsPlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        //
        ->plugin(
            AgentsPlugin::make()
                ->name('Ask Acme')
                ->agent(Assistant::class)
                ->server(AcmeServer::class)
                ->authorizeUsing(fn (string $ability) => auth()->user()->can($ability))
                ->roleLabelUsing(fn () => auth()->user()->role?->getLabel()),
        );
}
  • name() is how the assistant introduces itself and what the topbar button says.
  • agent() is your Agent subclass, see The assistant.
  • server() is the MCP server class that holds the tool list, see Tools. Without a server class, pass ->tools([...]) for a chat-only setup.
  • authorizeUsing() tells the package how to check an ability for the current person. Without it, an ability goes through Laravel's Gate when a gate of that name exists and is otherwise allowed, so an app without abilities works out of the box.
  • roleLabelUsing() gives the prompt and the refusal messages a role name ("Your role (Viewer) is not allowed to do this").

The provider key

Provider credentials live in laravel/ai's config/ai.php, so the usual environment variables work:

AGENT_PROVIDER=anthropic
ANTHROPIC_API_KEY=sk-ant-…

or

AGENT_PROVIDER=openai
OPENAI_API_KEY=sk-…

Without a key the chat hides itself (the pages, the topbar button and the sidebar) and the MCP endpoint keeps answering, since it does not need a model. AGENT_ENABLED=false hides the chat regardless.

Write a first tool

php artisan packstub-agents:tool SearchOrders --ability=orders.view

Add it to the server's $tools and open the panel. The next page, Tools, explains what goes into a tool.