nativephp/superstack
NativePHP Superstack bundles NativePHP with Laravel, Livewire, and Tailwind into a ready-to-use starter kit for building native desktop apps with familiar PHP tooling, sensible defaults, and preconfigured build tooling.
Treat this as a project template, not a dependency. You do not composer require nativephp/superstack into an existing app; you scaffold from it and then own the code.
# Prereqs first — the repo's AGENTS.md is explicit about this
php -v && composer -V # install via https://php.new if missing
git clone https://github.com/nativephp/superstack my-app
cd my-app
composer install
cp .env.example .env && php artisan key:generate
php artisan migrate --seed
npm install && npm run build
Then pick your surface:
php artisan serve # web: Filament panel at /admin
php artisan native:serve # desktop window running the same app
The starter ships a Laravel Boost bootstrap (agents/AGENTS.md). Run it before writing code, because it rewrites the instructions the AI tooling reads:
composer require laravel/boost --dev
php artisan boost:install
Where to look first, in this order:
composer.json — pins the nativephp/*, filament/*, and MCP package versions. This tells you which upstream docs actually apply.app/Providers/Filament/*PanelProvider.php — panel path, auth guard, discovered resources.config/nativephp.php — window/menu/build config, plus the NATIVEPHP_* env vars.routes/ai.php (or routes/mcp.php) — MCP server registration; app/Mcp/ for the tool classes.database/seeders/DatabaseSeeder.php — the seeded Filament login.First use case to prove the stack end-to-end: generate one Filament resource over a real model, then expose a read-only slice of the same model as an MCP tool and call it from Claude Desktop or the MCP Inspector. If that loop works, you understand all three layers.
php artisan make:filament-resource Invoice --generate
php artisan make:mcp-tool LookupInvoices # check the repo's actual generator name
The whole point of the "superstack" is that Filament, the native shell, and the MCP server are all presentation layers. Put business logic in app/Actions or app/Services and call it from everywhere — controllers, Filament actions, native menu callbacks, MCP tools. If you write queries in a Filament table and again in an MCP tool, the two will drift within a month.
final class CreateInvoice
{
public function handle(array $data): Invoice
{
return DB::transaction(fn () => Invoice::create($data + ['user_id' => auth()->id()]));
}
}
Standard day-to-day moves: php artisan make:filament-resource X --generate, then tighten. Group resources with ->navigationGroup(), gate them with policies (Filament auto-respects them), and add dashboard widgets for anything the native window should also show.
public static function table(Table $table): Table
{
return $table
->columns([TextColumn::make('number')->searchable(), ...])
->filters([TrashedFilter::make()])
->defaultSort('created_at', 'desc');
}
Never let a Native\Laravel\Facades\* call sit in a code path that also runs in the browser or in tests. Wrap it:
if (NativeApp::isRunning()) {
Window::open()->route('dashboard')->width(1280)->height(800);
}
Put menus, tray icons, and global shortcuts in a dedicated service provider so they're discoverable, and register native→PHP event listeners there too rather than scattering them.
An MCP tool is an API whose client is an LLM. The description and JSON schema are prompt engineering, not documentation — vague schemas produce confidently wrong tool calls.
public function schema(JsonSchema $schema): array
{
return [
'status' => $schema->string()->enum(['draft', 'sent', 'paid'])->required(),
'limit' => $schema->integer()->default(20),
];
}
public function handle(Request $request): Response
{
Gate::authorize('viewAny', Invoice::class); // parity with Filament
$invoices = Invoice::query()->where('status', $request->string('status'))
->limit(min($request->integer('limit', 20), 50))
->get(['number', 'total', 'status']);
return Response::json(['invoices' => $invoices]);
}
Test MCP tools the way you'd test an external API: php artisan mcp:serve locally and drive it with npx @modelcontextprotocol/inspector. Do not test them through the chat client — too many variables.
The same codebase ships twice: as a web app (Filament over HTTP) and as a packaged native binary (php artisan native:build). Keep anything that must be server-side — third-party webhooks, heavy queues — out of the native app's critical path, and make the native app degrade gracefully when offline (local SQLite, queued sync) if you're going that far.
git remote add upstream + cherry-pick or git diff. This is the single biggest cost of adopting it — budget for it.auth()->user() is not what you expect. Resolve the user explicitly (token, loginUsingId, or hard-scope tools to read-only public data), and always Gate::authorize(...) rather than assuming the tool is "internal."->limit() with a hard ceiling and return only the columns needed.native:serve downloads the runtime and is slow. Expect a multi-minute cold start and a firewall prompt. If it hangs, re-run php artisan native:install — half-downloaded binaries are the usual culprit.APP_URL and Vite's dev-server origin don't line up the way they do in a browser. Prefer relative paths and rebuild with npm run build before native:build, not after.sync driver for native-critical jobs, or run a worker as a child process explicitly. Silent no-op dispatches are a classic "works in dev, dead in the installer" bug..env values (or a different SQLite path) depending on how the binary is launched. Log config('database.connections.sqlite.database') early in the boot to confirm.storage/logs/laravel.log. Native: the same log plus the Electron devtools console, opened from the dev menu. MCP: the Inspector's notification pane. Know which one you're looking at before you start commenting out code.AGENTS.md/CLAUDE.md. Re-run php artisan boost:install after significant dependency changes, and commit the generated files — otherwise an AI-assisted session will confidently follow stale guidelines.composer.json, and lean on the upstream NativePHP, Filament, and Laravel MCP docs rather than searching for the superstack combination.How can I help you explore Laravel packages today?