huzaifaarain / laravel-pulse-mcp
Securely expose Laravel Pulse production monitoring data to AI agents through an OAuth-protected MCP server.
Requires
- php: ^8.3
- illuminate/support: ^12.41.1||^13.0
- laravel/mcp: ^1.0
- laravel/pulse: ^1.4
Requires (Dev)
- brianium/paratest: ^7.0||^8.0
- larastan/larastan: ^2.9||^3.0
- laravel/passport: ^13.0
- laravel/pint: ^1.29
- laravel/sanctum: ^4.0
- nunomaduro/collision: ^8.1
- orchestra/testbench: ^10.6||^11.0
- phpstan/extension-installer: ^1.4
- phpunit/phpunit: ^11.5.3
- rector/rector: ^1.2||^2.2
Suggests
- laravel/passport: Required (^13.0) for OAuth authentication with dynamic client registration.
- laravel/sanctum: Alternative authentication using personal access tokens.
Provides
None
Conflicts
None
Replaces
None
README
Let AI agents like Claude Code, Codex, Cursor and VS Code read your production Laravel Pulse data through a secure, read-only MCP server. You no longer need to copy slow queries, exceptions or queue stats out of the dashboard by hand.
You: What's slow in production today?
Agent: pulse_health → pulse_overview → pulse_slow_requests → pulse_slow_queries
GET /orders averages 3.1s. OrderController.php:31 runs an unindexed
orders lookup by customer_id and status (2.4s, 2 occurrences)...
The package runs inside your application. Agents connect to it over HTTPS with OAuth (Laravel Passport, including dynamic client registration and a browser consent screen) or with Sanctum personal access tokens. Only users you authorize can read the data.
Contents
- Requirements
- Installation
- Authentication
- Connecting an agent
- Tools and prompts
- Laravel Boost skill
- Configuration
- Security model
- Limitations
Requirements
- PHP 8.3+
- Laravel 12.41.1+ or 13 (the minimum comes from
laravel/mcp) laravel/pulse1.4+ with database storagelaravel/passport13+ (OAuth) orlaravel/sanctum4+ (tokens)
Installation
composer require huzaifaarain/laravel-pulse-mcp
The endpoint stays off until you enable it, typically only in production:
PULSE_MCP_ENABLED=true
It is then served at POST /mcp/pulse. If you cache routes, run php artisan route:cache again after enabling it.
Authentication
Choose a driver with PULSE_MCP_AUTH. Either way, a user must pass the viewPulseMcp gate (see Authorizing users).
OAuth with Passport (default)
This is the best experience: the agent opens a browser, you sign in to your app, and you approve access. No tokens need to be copied.
-
Install and configure Passport 13:
composer require laravel/passport php artisan passport:install
-
Add
Laravel\Passport\HasApiTokensto yourUsermodel, implementLaravel\Passport\Contracts\OAuthenticatable, and add anapiguard with thepassportdriver inconfig/auth.php. -
The package registers everything else:
- OAuth discovery at
/.well-known/oauth-protected-resourceand/.well-known/oauth-authorization-server - dynamic client registration at
POST /oauth/register - the
mcp:usescope
- OAuth discovery at
If your application has no Passport authorization view, the package provides a self-contained consent screen. To customise it, publish it with php artisan vendor:publish --tag=pulse-mcp-views, or set your own with Passport::authorizationView(). Signing in uses your application's normal login.
Sanctum tokens
PULSE_MCP_AUTH=sanctum
Add Laravel\Sanctum\HasApiTokens to your User model, then issue a token with the mcp:use ability:
php artisan pulse-mcp:token ops@example.com # by email or id php artisan pulse-mcp:token 1 --expires=30 --name=laptop php artisan pulse-mcp:token 1 --model="App\Models\Admin" # defaults to the default guard's user model
The command prints the token and a ready-to-run claude mcp add command, and it warns if the user fails the viewPulseMcp gate. In code, the equivalent is $user->createToken('pulse-mcp', ['mcp:use']).
Authorizing users
Access defaults to Pulse's own viewPulse gate, so dashboard viewers can connect. Define viewPulseMcp to narrow it:
use Illuminate\Support\Facades\Gate; Gate::define('viewPulseMcp', fn (User $user) => $user->is_admin);
Connecting an agent
The package prints the exact client configuration for you:
php artisan pulse-mcp:client claude --url=https://your-app.com/mcp/pulse php artisan pulse-mcp:client codex php artisan pulse-mcp:client cursor php artisan pulse-mcp:client vscode
Claude Code (OAuth):
claude mcp add --transport http pulse https://your-app.com/mcp/pulse
Codex (OAuth):
codex mcp add pulse --url https://your-app.com/mcp/pulse codex mcp login pulse
Sanctum: send the token as a header, for example:
claude mcp add --transport http pulse https://your-app.com/mcp/pulse --header "Authorization: Bearer <token>"
Tools and prompts
All tools are read-only and idempotent. period is one of 1h, 6h, 24h or 7d, which are the windows Pulse aggregates. The default is 24h.
| Tool | Purpose |
|---|---|
pulse_health |
Freshness check: recording status, ingest driver, latest entry, reporting servers, and warnings about stopped pulse:work / pulse:check |
pulse_overview |
Totals plus the top 5 rows of every card. Start here |
pulse_exceptions |
Exceptions by class and location, with count and latest occurrence |
pulse_slow_queries |
Slow SQL with code location, count, slowest duration and threshold |
pulse_slow_requests |
Slow routes with controller action |
pulse_slow_jobs |
Slow queued jobs |
pulse_slow_outgoing_requests |
Slow third-party HTTP calls |
pulse_queues |
Queued, processing, processed, released and failed jobs per queue, with an optional series |
pulse_cache |
Hit/miss totals, hit rate and the busiest keys |
pulse_servers |
CPU, memory and disk per server, with period averages and peaks |
pulse_usage |
Top users by requests, slow requests or jobs |
pulse_types |
Every recorded type, including custom and third-party cards |
pulse_aggregate |
Generic reader for any type, in aggregate, graph or values mode |
The list tools accept limit, sort and a case-insensitive search filter, and report truncated when more rows exist.
Prompts: diagnose_performance (period, focus) and triage_errors (period) give agents a guided investigation.
Laravel Boost skill
The package ships a Laravel Boost skill, pulse-production-monitoring, plus a short guideline. Together they teach agents in your codebase when to reach for the Pulse tools, how to investigate, and how to trace each finding back to your code. Install it with:
php artisan boost:install
Select huzaifaarain/laravel-pulse-mcp when asked for third-party packages. The package must be a direct dependency in your composer.json.
Configuration
Publish the config file with:
php artisan vendor:publish --tag=pulse-mcp-config
| Key | Env | Default | Description |
|---|---|---|---|
enabled |
PULSE_MCP_ENABLED |
false |
Registers the endpoint |
path |
PULSE_MCP_PATH |
mcp/pulse |
Endpoint path |
auth.driver |
PULSE_MCP_AUTH |
passport |
passport or sanctum |
auth.guard |
PULSE_MCP_GUARD |
api / sanctum |
Auth guard for the route |
oauth.routes |
PULSE_MCP_OAUTH_ROUTES |
true |
OAuth discovery and client registration routes |
oauth.authorization_view |
pulse-mcp::authorize |
Consent view used when the app has none | |
gate |
viewPulseMcp |
Gate every user must pass | |
middleware |
[] |
Extra route middleware | |
throttle |
PULSE_MCP_THROTTLE |
60,1 |
Rate limit, max,minutes; empty disables it |
ignore_own_requests |
true |
Keeps agent calls out of Pulse's request and usage cards | |
limits.default / limits.max |
20 / 100 |
Rows per tool call | |
limits.search_scan |
1000 |
Rows scanned before a search filter |
|
cache_ttl |
PULSE_MCP_CACHE_TTL |
10 |
Seconds to cache Pulse queries; 0 disables it |
redact.mask_sql_literals |
true |
Masks string and numeric literals in SQL | |
redact.max_sql_length |
2000 |
Truncates long SQL | |
redact.strip_url_query_values |
true |
Masks outgoing URL query values, keeping the names | |
redact.include_user_email |
false |
Includes emails in pulse_usage |
|
redact.cache_key_patterns |
[] |
Regexes masked in cache keys |
Security model
- Off by default. Nothing is exposed until
PULSE_MCP_ENABLED=true. - Authenticated and authorized. Every request needs a valid token that carries the
mcp:usescope or ability, and the user must passviewPulseMcp. - Read-only. No tool writes, purges or changes Pulse or application data.
- Redacted by default. SQL literals, URL query values and user emails are masked, and you can add patterns for cache keys. Pulse never stores SQL bindings in the first place.
- Rate limited and cached. A throttle and a short query cache protect the database from agent loops.
- Invisible to Pulse. Pulse does not record its own reads, and agent requests are excluded from its request cards.
Limitations
- Pulse keeps at most 7 days of data and only aggregates the 1h, 6h, 24h and 7d windows.
- Pulse records slow entries only above each recorder's threshold, and it may sample, so counts are indicative.
- Exceptions include class, location and counts, but no messages or stack traces, because Pulse does not store them.
- With Redis ingest, data reaches the database only while
pulse:workruns, and server metrics needpulse:check.pulse_healthwarns about both.
Development
composer test # PHPUnit composer analyse # Larastan composer style:fix # Pint composer rector # Rector
Links
Credits
License
Laravel Pulse MCP is open-source software licensed under the MIT license.