guava/filament-mcp
guava/filament-mcp brings the Model Context Protocol to Filament, exposing your admin panel's resources and tools to AI assistants and MCP clients so they can query and act on your Laravel app through a standardized, permission-aware interface.
guavacz/filament-mcp (1.0.0, first tagged release) bridges a Filament panel and the Model Context Protocol, so an AI client (Claude Desktop, Cursor, an agent) can call into your admin panel's resources and actions as MCP tools.
composer require guavacz/filament-mcp
php artisan vendor:publish --tag="filament-mcp-config"
Where to look first, in this order:
config/filament-mcp.php — server name, transport, route/prefix, auth guard, and which panel(s) are exposed.src/ — the service provider (how servers/tools are registered), then Tools/ and any Servers/ directory. That folder is the public API surface at 1.0.0.First use case: expose exactly one read-only tool — an orders.list style tool backed by a Filament resource's getEloquentQuery() — point Claude Desktop or the MCP Inspector at it, and confirm you get paginated JSON back before wiring up anything that writes.
Keep the local loop honest: run php artisan serve, use the HTTP transport against http://localhost:8000, and re-run the inspector after every config change. 1.0.0 is a single initial commit, so the README and src/ are the only source of truth — there is no migration history to learn from.
One server per panel, not one server for the app. If you run AdminPanel and OpsPanel, register separate MCP servers so tool namespaces, guards, and tenancy don't leak across them. This also lets you hand a narrower tool set to a less-trusted client.
Tools as thin wrappers over Filament, not reimplementations. Resolve the resource and reuse its query builder and form schema:
public function handle(array $arguments): array
{
$resource = OrderResource::class;
$query = $resource::getEloquentQuery()->select(['id', 'number', 'status', 'total']);
return $query->paginate($arguments['per_page'] ?? 15)->toArray();
}
Deriving from getEloquentQuery() means global scopes, tenancy, and soft-delete behaviour stay consistent with what admins see in the UI. When you add a write tool, run it through the resource's form schema so validation rules live in one place.
Read-first rollout, then writes behind confirmation. Ship list/view/search tools first; agents are most useful and least dangerous there. For mutating tools, require an explicit confirm: true argument and wrap the handler in DB::transaction(). Return the created/updated record's ID so the client can chain follow-up calls.
Authorize inside the tool, every time. An MCP request arrives outside the browser session, so Filament's panel middleware and canAccessPanel() never ran. Call the policy explicitly and fail closed:
Gate::forUser($user)->authorize('viewAny', Order::class);
Map token abilities to tool names. Issue Sanctum tokens whose abilities mirror your tool identifiers (orders.list, orders.update) so revoking a capability is a one-line change rather than a code deploy. Resolve the acting user from the token in a middleware that wraps the server.
Tenant-scoped tools take an explicit tenant argument. With Filament tenancy enabled, do Filament::setTenant($tenant) inside the handler rather than trusting a default — otherwise a client silently operates on whichever tenant happens to be resolved.
Test the tool, not the protocol. Call the tool class directly from Pest with an authenticated user and assert on the returned array. Reserve end-to-end MCP client tests for one smoke test per server.
^1.0, read the diff of every release, and expect tool names and argument schemas to be renamed as the package matures. Those strings are embedded in prompts and client configs — a rename is a silent breakage for your users, so add a regression test asserting the exact tool names you expose.select() explicit columns, never return a full Eloquent model with relations. Cap per_page server-side rather than trusting the argument.config:clear locally after edits, and remember that route registration and tool discovery are typically driven from that cached config — a tool that "disappeared" after deploy is usually a stale config cache.LIKE '%term%' will take your admin panel down with it.app/Mcp/Tools so composer update never touches them.How can I help you explore Laravel packages today?