stubbedev / laravel-dev-mcp
MCP server for local Laravel development (Go, distributed as a prebuilt binary)
Package info
github.com/stubbedev/laravel-dev-mcp
Language:Go
Type:composer-plugin
pkg:composer/stubbedev/laravel-dev-mcp
Requires
- php: >=8.1
- composer-plugin-api: ^2.0
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
A fast, low-footprint MCP server for local Laravel development, written in Go. It consolidates and replaces laravel/boost and laravel-telescope-mcp into one binary — no PHP package to install in your app.
- stdio and HTTP transports.
- MCP roots (incl. the
X-Mcp-RootHTTP header) so a single instance can serve many repos/worktrees. - Go-native where possible: reads
.env,composer.lock,config/*.php(parsed via a PHP AST — no PHP execution), log files, and talks to the database directly (mysql / mariadb / pgsql / sqlite). Falls back tophp/php artisanonly where the live app is unavoidable:routes,artisan,tinker, named-routeabsolute_url, andconfig/db_connectionswhen a config uses array spreads or other dynamic code.phpmust be onPATH(or setLARAVEL_MCP_PHP) for those.doctoradditionally shells tocomposer auditwhencomposeris present (skips cleanly otherwise). Everything else — includingtelescope_prune(a directDELETE) — is pure Go. - Works without Telescope — Telescope-backed tools degrade with a clear message; everything else keeps working.
Install
npm (auto-updating — @latest always fetches the newest release binary):
npx -y @stubbedev/laravel-dev-mcp@latest
Or pin it in an MCP client config:
{
"mcpServers": {
"laravel-dev": {
"command": "npx",
"args": ["-y", "@stubbedev/laravel-dev-mcp@latest"]
}
}
}
The npm package is a thin launcher that downloads the prebuilt Go binary for
your platform (linux x64/arm64, macOS arm64) on install — or on first run if the
install-time download is skipped. Set LARAVEL_MCP_SKIP_DOWNLOAD=1 to skip it.
Composer (no Node at runtime — handy when the host already has PHP):
composer require --dev stubbedev/laravel-dev-mcp
This package is a Composer plugin. On install/update it downloads the prebuilt
Go binary for your platform (linux x64/arm64, macOS arm64) and registers it in the
project's .mcp.json:
{
"mcpServers": {
"laravel-dev": {
"command": "vendor/stubbedev/laravel-dev-mcp/bin/laravel-dev-mcp"
}
}
}
The MCP client then execs the native binary directly — PHP is only the installer, never in the request path. Composer prompts once to trust the plugin (or pre-approve it):
composer config allow-plugins.stubbedev/laravel-dev-mcp true
The download matches the installed version, so pinning a tag pins the binary;
the relative path keeps the committed .mcp.json working for every teammate who
runs composer install. Set LARAVEL_MCP_SKIP_DOWNLOAD=1 to skip the fetch and
the .mcp.json write.
npm and Composer download each release's binary once per user, verify it against
the release's checksums.txt, and keep it in
$XDG_CACHE_HOME/laravel-dev-mcp/<version>/ (by default ~/.cache on Linux,
~/Library/Caches on macOS), and hardlink it into every install, or copy it
where the install is on another filesystem. Further projects and git worktrees
on the same version install without downloading anything. Delete old version
directories to reclaim their space; installs keep their own link.
Homebrew:
brew tap stubbedev/tap brew install stubbedev/tap/laravel-dev-mcp
Nix (binary cache at nix.stubbe.dev):
nix run github:stubbedev/laravel-dev-mcp
Go:
go install github.com/stubbedev/laravel-dev-mcp@latest
Usage
stdio (run from the Laravel app directory, or set the MCP root):
laravel-dev-mcp
HTTP (multi-repo; pass the project per request via header):
laravel-dev-mcp --http # 127.0.0.1:8765/mcp # then call /mcp with header: X-Mcp-Root: /path/to/laravel-app
Example MCP client config (stdio):
{
"mcpServers": {
"laravel-dev": {
"command": "laravel-dev-mcp"
}
}
}
Configuration
| Env var | Default | Purpose |
|---|---|---|
LARAVEL_MCP_PHP |
php |
PHP binary used for artisan shell-outs. |
LARAVEL_MCP_HTTP_ADDR |
— | Enable HTTP on this address (or use --http). |
LARAVEL_MCP_HTTP |
— | Truthy (1/true) enables HTTP on the default 127.0.0.1:8765. |
LARAVEL_MCP_HTTP_PATH |
/mcp |
HTTP endpoint path (--http-path). |
LARAVEL_MCP_TOKEN |
— | If set, HTTP clients must send it as Authorization: Bearer <token> (or X-Mcp-Token). Ignored for stdio. |
LARAVEL_MCP_DOCS_URL |
https://boost.laravel.com |
Docs search backend. |
Tools
All tools are exposed up front. Works on any Laravel app:
app_info— PHP/Laravel versions, env, installed composer packages.doctor— health check: missing.env/APP_KEY, stale config/route/event caches (which silently ignore source &.envedits), DB connectivity, vulnerable composer packages (composer audit). Setaudit=falseto skip the network call.profile— per-request profiling: total time, query breakdown, slowest queries, and N+1 detection (statements that ran 2+ times after normalizing bound values). Reads from Telescope (telescope_entries, grouped by request — no extra package needed), or the Clockwork (storage/clockwork) / Debugbar (storage/debugbar) storage files when present. Auto-detects the source; no app boot.state— live backend state.kind=cachereads a cache entry (value + TTL) or, without akey, store stats/sample keys;kind=queuereports pending + recent failed jobs with decoded payloads. Backends: database, redis (built-in client — no dependency), file. Honorsenv=testing.db_connections— every configured connection (fromconfig/database.php, passwords masked).db_schema— list tables, or describe one table's columns/indexes/FKs (honors table prefix).db_query— read-only SQL (SELECT/SHOW/EXPLAIN/DESCRIBE/WITH/PRAGMA only).- The three
db_*tools acceptenv=testing(or any env name) to target the test database: it overlays.env.testingand, fortesting,phpunit.xml<env>entries over.env— matching how Laravel resolves config under tests.
- The three
models— discover Eloquent models acrossapp/,app/Modules/,Modules/,src/(AST parse, no PHP run): table, fillable, casts, relationships.logs— application + frontend logs.source=app(default) tails the newestlaravel*.log(resolved viaconfig/logging.php, honoring a customLOG_CHANNEL/path and daily rotation), filterable bylevel;source=errorreturns the last error-level entry;source=browsertailsstorage/logs/browser.log.routes—php artisan route:list, filterable.config— read anyconfig/*.phpvalue by dotted key (native AST parse, env-resolved, secrets redacted); falls back to PHP when a config uses array spreads / dynamic code so nothing is omitted; omit the key to list config files.absolute_url— build a URL fromAPP_URL(path or named route).artisan— run allowlisted read-only artisan commands (about,db:show,migrate:status,queue:failed,schedule:list,event:list, …).docs_search— search version-matched Laravel-ecosystem docs.tinker— execute arbitrary PHP in the app context (available whenlaravel/tinkeris installed).
Requires Laravel Telescope (degrades gracefully when absent):
telescope— query telemetry by type (requests,queries,exceptions,jobs,mail,cache, …), with filtersslow,since_hours,tag,request_id, andid(single entry).telescope_prune— delete old Telescope entries (hours, default 24).
Development
just check # every gate in order: vet, lint, test, build, sync nix/package.nix vendorHash just vet # go vet ./... just lint # golangci-lint run (config in .golangci.yml) just test # go test ./... just build # compile-check every package just fmt # apply the gofumpt, gci and golines formatters
Linting runs every golangci-lint linter (default: all; only deprecated ones
are off, in favour of their replacements). An intentional exception is a
//nolint:<linter> // <reason> on the exact line, which nolintlint checks.
Every dev tool (Go pinned to the version go.mod declares, gopls,
golangci-lint, delve, just, git, gh) comes from the flake: nix develop, or
direnv allow (the tracked .envrc runs use flake). The shell sets GOTOOLCHAIN=local
and CGO_ENABLED=0. The package itself lives in nix/package.nix; after a
dependency change, just sync-flake refreshes its vendorHash.
CI runs build/test/lint and the nix check (ci.yml), plus CodeQL, Grype,
govulncheck and dependency review (security.yml). Dependabot keeps Go
modules and pinned actions current.
PHP syntax support
config/*.php and model files are parsed with a PHP 8.1 grammar (the newest
php-parser implements). Files using
later syntax, such as DNF types, typed class constants, property hooks,
asymmetric visibility or the 8.5 pipe operator, still work: a config that
can't be read statically is resolved through PHP instead, and an affected model
is listed with partial: true.
Releases are cut with just release-patch|minor|major (tags vX.Y.Z), which
trigger multi-arch binary builds, the nix cache push, the GitHub release, and
the Homebrew tap bump.
License
MIT