Diagnoses pg-boss and graphile-worker job queues: retry storms, stuck workers, missed schedules.
Copy the AI prompt to install this server into Claude Code, Cursor, or another agent β or use 1-click editor setup below.
We haven't yet run this listing's install command through our automated sandbox check. This isn't a red flag β we're steadily working through the catalog.
π‘ Paste the JSON block into your client's configuration file under mcpServers, then restart the application.
An MCP server that diagnoses Postgres job queues β pg-boss and graphile-worker. Retry storms, stuck workers, missed schedules, expiry overruns: what is broken, why, and the safest way to recover.
The second answer is the product. Every finding carries the evidence it was drawn from, so you β or an agent β can check the reasoning instead of trusting it.
The rules are extracted from a morning health check that has run daily in production since April 2026 against a pg-boss instance driving ~30 cron queues. Every threshold was tuned by a real false positive or a real missed failure, and each rule below names the incident that motivated it. That provenance is the point: these are not heuristics invented for a README.
Then ask: "Is anything wrong with my job queue?"
Want to see it work first? examples/demo spins up a
throwaway Postgres and manufactures seven failures in about a minute. It also
plants a graphile-worker instance in the same database, where four of the seven
rules go deliberately silent β the clearest way to see what capability
declaration actually buys you.
The server speaks stdio, so every MCP client starts it as a subprocess. The only thing that varies is where the config lives β and whether that process can reach your database.
Claude Desktop β ~/Library/Application Support/Claude/claude_desktop_config.json
on macOS, %APPDATA%\Claude\claude_desktop_config.json on Windows. Use the
mcpServers block above, then restart the app.
Desktop launches its subprocesses from the app bundle, not a login shell, so
PATH is minimal and a bare mcp-queue-doctor or npx often fails to resolve.
Give it an absolute path β which mcp-queue-doctor after a global install, or
the absolute path to npx with ["-y", "mcp-queue-doctor"] as its args.
Claude Code β one command, no file editing:
Add -s project to write a checked-in .mcp.json at the repo root instead of
your personal config, so everyone working in that repo gets the tool.
Cloud / remote sessions (Claude Code on the web, and any other headless
runner) β a checked-in .mcp.json is the only mechanism that works, because
nobody is there to answer an approval prompt. Reference the connection string
rather than committing it; Claude Code expands ${VAR} and ${VAR:-default}
in .mcp.json:
Project-scoped servers still need to be trusted before they start. In a
headless session that means setting enableAllProjectMcpServers: true in the
repo's .claude/settings.json, since the interactive approval never arrives.
Reachability is the real constraint, not the config. The server runs wherever the client runs, and it connects to Postgres directly β there is no hosted component in between. A cloud sandbox can therefore only diagnose a database inside that sandbox: the demo stack, or a dev stack the session brought up itself. A production queue bound to loopback on your own host is not reachable from a sandbox at all, and exposing it to make it reachable is the wrong trade.
Diagnose production from a client on a machine that already has a route to it β your laptop, over an SSH tunnel:
and point QUEUE_DOCTOR_DATABASE_URL at 127.0.0.1:5432. The tunnel is the
access grant, it lasts exactly as long as the terminal stays open, and the
credentials never leave your machine.
| Tool | Answers |
|---|---|
diagnose | Start here. Runs the whole rule catalog, returns ranked findings with evidence and recovery steps |
queue_overview | Per-queue counts by state, stuck jobs, and each queue's expiry/retention/retry config |
failed_jobs | Failures in a window with error messages, plus a per-queue error-frequency breakdown |
stuck_jobs | Jobs active past a threshold, with age, expiry, and heartbeat staleness |
missed_schedules | Cron queues whose latest firing is older than their expression implies |
schedule_status | Every registered schedule with cron, timezone, last firing, and next expected |
job_detail | One job's full record: state, timings, retries, payload, output |
server_info | Connectivity, detected schema, matched dialect, and reduced capabilities |
Schedule expectations are derived from pg-boss's own schedule table by parsing
each cron expression, so the common case needs no configuration. The health
check this was extracted from carried a hand-maintained list of expected jobs
that silently stopped covering whatever nobody remembered to add.
| Rule | Fires when | Motivating incident |
|---|---|---|
retry-storm | Many failures, densely packed, dominated by one error | A daily API quota tipped over and 875 corpus-fill jobs failed in one night. The count suggested 875 problems; the shape showed one |
expiry-overrun | Failure durations cluster at the queue's expiry | A full-corpus sweep couldn't finish inside a 30-minute expiry once upstream throttling slowed it. It reported as a job failure nightly; the fix was an internal wall-clock budget |
stuck-jobs | Jobs active far too long, or heartbeats stopped | A worker killed without graceful shutdown leaves rows active until maintenance reclaims them |
missed-schedule | Latest firing predates the last expected tick | Distinguishes "never fired" (scheduler never booted) from "stopped firing" |
duplicate-registration | A cron queue enqueued twice for one tick | An instrumentation hook invoked job registration twice per process, so every cron ran double for weeks |
retention-window | Failed-row count disagrees with the windowed count | A health email stayed yellow for days after the bug was fixed, counting rows that failed days earlier |
dead-queue | Registered long ago, unscheduled, holds nothing | A producer that stopped, or a registration dropped in a refactor |
Failures are classified (rate_limit, transient_transport, auth,
not_found) because the class changes the advice: the right response to a storm
of 429s is close to the opposite of the right response to connection resets.
| Backend | Support | Verified against |
|---|---|---|
| pg-boss v11+ | Full | 11.1.2 (schema 26), 12.27.0 (schema 37) |
| pg-boss v10 | Recognised, refused β see below | 10.4.2 (schema 24) |
| pg-boss v9 and earlier | Recognised, refused | β |
| graphile-worker 0.17 | Partial, capability-declared | 0.17.3 |
Select with QUEUE_DOCTOR_BACKEND=pgboss (default) or graphile; the schema
default follows the backend.
Backends don't just name columns differently β they model work differently. graphile-worker deletes a job when it succeeds, has no per-job expiry, no worker heartbeats, and keeps cron expressions in a file rather than the database. So "how many completed in the last day" has no answer there at any price.
Every backend therefore declares what it can answer, and rules that depend on missing data stay silent rather than reporting a zero β a zero reads like a measurement.
| Rule | pg-boss v11+ | graphile-worker |
|---|---|---|
retry-storm | β | β |
stuck-jobs | β (with heartbeats) | β (age only) |
expiry-overrun | β | β no expiry exists |
missed-schedule | β | β cron lives in a file |
duplicate-registration | β | β no firing history |
retention-window | β | β nothing is retained |
dead-queue | β | β no queue registry |
server_info reports the capability set and spells out each limitation.
pg-boss's tables are not a stable API. Across versions it has renamed every
timestamp column (createdon β created_on), dropped a whole table (archive,
removed in v11), changed a duration from an interval to an integer (expire_in
β expire_seconds), partitioned the job table, and added columns
(heartbeat_on) that materially change what can be diagnosed.
A tool that hard-codes one shape breaks on the next upgrade β silently, if it is
unlucky. That is exactly how the health check this is extracted from spent weeks
emitting a confident, wrong "missed schedules" warning that was really SQLSTATE
42P01 after pgboss.archive disappeared.
So schema knowledge lives in one file, src/pgboss/dialect.ts, as data:
No reviews yet β be the first to share how this listing worked for you.
Showcase your server listing on GitHub or your project documentation. Embed this dynamic SVG badge to highlight official listing status and live engagement.
[](https://allmcps.com/mcp/mcp-queue-doctor)<a href="https://allmcps.com/mcp/mcp-queue-doctor"><img src="https://allmcps.com/api/badge/mcp-queue-doctor?style=directory" alt="MCP Queue Doctor on AllMCPs" /></a>