Porthole for Laravel

See the full picture, right away.

An authenticated MCP server that lets your coding agent read Horizon, Pulse, and Telescope — without you tabbing out of the editor to piece the ops story together.

What it is

Ops telemetry, spoken as tools.

Laravel Porthole is an authenticated Model Context Protocol server for apps that already run Horizon, Pulse, and/or Telescope. It sits on laravel/mcp and exposes those sources as MCP tools your agent can call.

  • Read tools register by default. Horizon status and jobs, Pulse health and slow paths, Telescope entries — available when the matching package is installed.
  • Write tools stay off until you opt in. Pausing Horizon, retrying failed jobs, and pruning Telescope are gated behind explicit config flags and a separate mutateHorizon gate.
  • Two transports. Local stdio via php artisan mcp:start porthole (local env only), or HTTP at POST /mcp/porthole with bearer auth or Porthole’s own OAuth issuer.

Why teams install it

Keep the investigation where the code is.

You already trust Horizon, Pulse, and Telescope. Porthole does not replace them — it lets an agent query the same operational picture while it is already in your repo.

  • Faster incident loops. Ask what failed, what is slow, and what the queue looks like without leaving the agent session.
  • Same gates you already enforce. Callers need an authenticated user who passes viewPorthole plus the source gate (viewHorizon, viewPulse, or viewTelescope). An undefined gate denies the call.
  • Freeware, open package. Install from Packagist, configure gates, connect your MCP client. No product tour required.
  • Built for people who already live in the stack. Solo Laravel builders and platform/infra folks who keep Telescope/Horizon/Pulse warm — not a dashboard pitch for executives.

Use cases

Where Porthole earns its keep.

Practical moments when an agent with ops context is more useful than another browser tab.

Local debugging with stdio

Point Cursor, Claude Code, or VS Code at php artisan mcp:start porthole in a local app. The agent can pull Horizon/Pulse/Telescope context while you fix the bug.

Shared HTTP access for the team

Expose /mcp/porthole behind Sanctum or API auth so agents in staging can read the same sources your staff already can — with throttle and gates intact.

Careful write affordances

When you intentionally enable writes, an agent can pause Horizon, resume it, or retry allowlisted failed jobs — still behind mutateHorizon, never as a silent default.

Requirements

What you need on the host app.

Summarized from the package README — treat that document as source of truth.

  • PHP 8.2+
  • Laravel 11.45, 12.41, or 13
  • laravel/mcp 1.x
  • At least one of laravel/horizon, laravel/pulse, or laravel/telescope

Security

Security is the product, not a footnote.

Porthole ships closed by default. You open only the surfaces you mean to open.

No permissive defaults

Define gates before the package boots. There is no built-in “allow everyone” path. Missing viewPorthole or a source gate denies the call. Institution-admin style roles are not assumed — these are staff ops gates.

HTTP auth is explicit

Point auth.middleware at the guard your MCP clients already use (auth:api, auth:sanctum, etc.). With the Porthole OAuth issuer off, an empty middleware list means the HTTP route is not registered — including when auth.http_required is false.

OAuth is opt-in and scoped

Porthole’s issuer at /porthole/oauth stays off until oauth.enabled is true (and local, unless allow_non_local). Tokens live in Porthole’s tables with an mcp:use scope — they are not your application’s Passport/Sanctum API tokens.

Stdio trust is local-only

Stdio registers only when APP_ENV=local and stdio.enabled is true. Setting auth.stdio_required to false trusts the local process for tool calls; it does not open HTTP.

Payloads are reduced before they leave

Horizon job payloads shrink to display names. Telescope strips query bindings, cookies, authorization headers, and trace arguments. Sensitive keys (password, token, secret, and related) become [redacted]. SQL literals and absolute URL query strings are masked. This is redaction for agent consumption — not a tenant isolation boundary.

Writes are absent until enabled

writes_enabled and destructive_enabled keep mutate/prune tools out of tools/list until you flip them. Retries still need a display-name allowlist; prune respects retention floors outside local.

Full authorization, OAuth, and redaction detail lives in the package README.

Getting started

Install, gate, connect.

Basic path below. Client-specific MCP config blocks (Cursor, VS Code, Claude Code, and more) are documented in the GitHub README.

1. Require the package

composer require chkltlabs/laravel-porthole

Then publish config if needed:

php artisan vendor:publish --tag=porthole-config

2. Define gates before boot

use App\Models\User;
use Illuminate\Support\Facades\Gate;

Gate::define('viewPorthole', function (User $user): bool {
    return $user->hasRole('staff');
});

Gate::define('mutateHorizon', function (User $user): bool {
    return $user->hasRole('staff');
});

Horizon, Pulse, and Telescope still apply their own gates. A caller needs both viewPorthole and the source gate. Adjust the role check to match your app — hasRole('staff') is an example, not a package dependency.

3. Choose a transport

Local stdio (laptop / same machine, APP_ENV=local):

{
  "command": "php",
  "args": ["artisan", "mcp:start", "porthole"],
  "cwd": "/absolute/path/to/your-app"
}

On stdio there is normally no logged-in user. For local trust, set auth.stdio_required to false (HTTP stays authenticated), or use HTTP with a token instead.

HTTP with a fixed bearer token:

{
  "url": "https://your-app.test/mcp/porthole",
  "headers": {
    "Authorization": "Bearer YOUR_ACCESS_TOKEN"
  }
}

Configure auth.middleware (for example auth:sanctum or auth:api) in config/porthole.php. For Porthole OAuth, omit the bearer header and follow the README’s OAuth section — do not put the issuer URL in the client’s url field.

4. Confirm the server

php artisan list mcp

Restart / reload MCP in your client so it picks up the server, then list tools. Read tools appear for each installed source.

Endpoint: POST /mcp/porthole. Deeper setup, troubleshooting, and the full tool catalog: github.com/chkltlabs/laravel-porthole.