# MCP server for AI agents

> A read-only MCP server that lets an AI agent inspect queue health, find jobs and read metrics.

Source: https://boring-observability.dev/skyline/docs/mcp-server
Section: Reference — Skyline for Laravel documentation
Updated: 2026-09-17

---

Skyline ships a read-only [MCP](https://modelcontextprotocol.io) server, so an AI agent can answer "why is the `emails` queue backed up?" or "find the failed `ChargeCheckout` for checkout 123 and show me the exception" from the same data the dashboard reads — without you pasting screenshots into a chat window.

## Installing it

The server is built on [`laravel/mcp`](https://github.com/laravel/mcp), which Skyline *suggests* rather than requires: it needs PHP 8.2+ and Laravel 11.45+ or 12.41+, which is a narrower range than Skyline itself supports. Install it in your application and the server registers itself.

```bash
composer require laravel/mcp
```

Without that package nothing is registered, whatever `config/horizon.php` says.

## The tools

| Tool | What it returns |
| --- | --- |
| `queue-overview` | Whether Horizon is running, jobs per minute, recent and failed counts, and every queue's backlog, wait, processes, time to clear and paused state. |
| `list-supervisors` | Master supervisors and their pools, with processes per queue, pool options, paused queues and uptime. |
| `list-jobs` | Jobs in one status — `pending`, `reserved`, `delayed`, `completed`, `failed` or `silenced` — filtered by queue, [search phrase](https://boring-observability.dev/skyline/docs/dashboard-operations#search) or tag, with a cursor for paging. |
| `get-job` | One job in full: arguments, attempts and limits, timings, earlier attempt failures, the worker that ran it, retries and the exception. |
| `get-metrics` | Throughput, runtime and running totals per job class or queue, or one class's or queue's snapshot history. |
| `get-trends` | Backlog, failures and wait per queue over the trends window, plus worker restarts by reason when [insights](https://boring-observability.dev/skyline/docs/insights) are enabled. |
| `list-batches` / `get-batch` | Batches with their progress, searchable by name or id, and a batch's failed jobs. |

## Connecting a local agent

The local server runs over stdio, so it reaches no further than the machine the agent runs on. For Claude Code:

```bash
claude mcp add skyline -- php artisan mcp:start skyline
```

Other clients take the same command. `php artisan mcp:inspector skyline` opens the MCP Inspector, which is the quickest way to try the tools yourself before handing them to an agent.

## Serving it over HTTP

To let a remote agent connect, turn on the web server. It is off by default.

```ini
HORIZON_MCP_WEB_ENABLED=true
```

It is served at `/horizon/mcp` — or `{horizon.path}/mcp` if you moved the dashboard — on the dashboard's domain. Every request must pass the `viewHorizon` gate, exactly like the dashboard. Agents have no browser session, so list the middleware that authenticates their tokens:

```php
'mcp' => [
    'enabled' => env('HORIZON_MCP_ENABLED', true),

    'web' => [
        'enabled' => env('HORIZON_MCP_WEB_ENABLED', false),
        'path' => env('HORIZON_MCP_PATH'),     // defaults to "{horizon.path}/mcp"
        'domain' => env('HORIZON_MCP_DOMAIN'), // defaults to horizon.domain
        'middleware' => ['auth:sanctum'],      // or Passport's 'auth:api'
    ],

    'exception_length' => 4000,
],
```

The middleware runs ahead of the gate check, so the gate then sees the user the token belongs to — allow that user in `viewHorizon` as you would a person. Laravel's [MCP authentication docs](https://laravel.com/docs/mcp#authentication) cover setting up Sanctum or Passport for MCP clients. See also [Security & access](https://boring-observability.dev/skyline/docs/security-and-access).

`exception_length` caps how much of a stack trace a tool returns unless the agent asks for the whole thing.

## What it deliberately will not do

- **It is read-only.** No tool retries, stops, deletes or pauses anything. An agent can tell you the `emails` queue needs draining; it cannot drain it.
- **Payloads are never exposed.** A job's serialized command holds its real property values, so agents see only the arguments Skyline stores separately — with `job_arguments.hidden` keys masked as `[hidden]` — plus an allowlist of payload metadata (tries, timeout, backoff, tags).
- **Search and queue filters scan** the retention window, like the dashboard's search box. Tags are only indexed for failed jobs and monitored tags.

> **The gate lets everyone through locally**
>
> In the `local` environment the `viewHorizon` gate admits everyone, just as it does for the dashboard. Don't turn the web server on for a machine that is reachable from outside while `APP_ENV=local`.

Set `HORIZON_MCP_ENABLED=false` to register nothing at all, even with `laravel/mcp` installed.


## Common questions

### How do I connect an AI agent to Laravel Horizon?

Install laravel/mcp in your application and Skyline registers a read-only MCP server automatically. A local agent connects over stdio — for Claude Code, claude mcp add skyline -- php artisan mcp:start skyline. Other clients take the same command, and php artisan mcp:inspector skyline opens the MCP Inspector so you can try the tools yourself.

### Can an AI agent retry or delete jobs through the Skyline MCP server?

No. Every tool is read-only: nothing retries, stops, deletes, pauses or releases anything. An agent can tell you the emails queue needs draining and which class is failing, but the action stays with you in the dashboard or the API.

### Does the MCP server expose job payloads to the AI agent?

No. A job's serialized command holds its real property values, so it is never returned. Agents see the arguments Skyline stores separately, with job_arguments.hidden keys masked as [hidden], plus an allowlist of payload metadata — tries, timeout, backoff and tags.

### How is the MCP HTTP endpoint secured?

It is off by default. Turning it on with HORIZON_MCP_WEB_ENABLED=true serves it at {horizon.path}/mcp, and every request must pass the viewHorizon gate exactly like the dashboard. Agents have no browser session, so list token middleware such as auth:sanctum or Passport's auth:api under mcp.web.middleware; it runs ahead of the gate, which then sees the user the token belongs to. Note that in the local environment the gate admits everyone, so do not expose the web server from a machine running with APP_ENV=local.
