Skip to main content
Glama
ethanasm

mcp-queue-doctor

by ethanasm

mcp-queue-doctor

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.

❌ "3 jobs in enrichment/corpus-fill are in state 'failed'."

✅ "enrichment/corpus-fill failed 140 times over 3m, peaking at 50 failures in a
   single minute. 91% share one error, which looks like an upstream rate limit.
   This is one fault reproduced many times, not many separate faults — so the fix
   belongs at the source, and retrying the jobs individually will reproduce it.

   Recovery, safest first:
     1. Stop enqueuing to this queue — every new job feeds the same failure.
     2. Confirm when the upstream quota resets; treat that as the time to resume.
     3. Add a cooldown gate after N consecutive 429s.
     ⚠ Do NOT bulk-retry yet — the upstream is still limited.

   Evidence: 140 failures, 91% 'HTTP 429 Too Many Requests (daily quota
   exceeded)', peak 50/min, busiest minutes [...], 12 other errors [...]"

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.

Where the heuristics come from

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.

Related MCP server: MCP PostgreSQL Operations

Install

npm install -g mcp-queue-doctor
{
  "mcpServers": {
    "queue-doctor": {
      "command": "mcp-queue-doctor",
      "env": {
        "QUEUE_DOCTOR_DATABASE_URL": "postgres://readonly:pw@localhost:5432/app"
      }
    }
  }
}

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.

Connecting it

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:

claude mcp add queue-doctor -e QUEUE_DOCTOR_DATABASE_URL=postgres://... -- npx -y mcp-queue-doctor

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:

{
  "mcpServers": {
    "queue-doctor": {
      "command": "npx",
      "args": ["-y", "mcp-queue-doctor"],
      "env": { "QUEUE_DOCTOR_DATABASE_URL": "${QUEUE_DOCTOR_DATABASE_URL}" }
    }
  }
}

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:

ssh -N -L 5432:127.0.0.1:5432 prod-host

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.

Tools

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.

The rule catalog

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.

Backends

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.

Capabilities, not zeros

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.

Versioned against pg-boss

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:

  • Every relation and column name is declared in a dialect. Query builders emit identifiers from it, so supporting a new pg-boss layout is an edit to that file — no SQL elsewhere mentions a pg-boss table by name.

  • Dialects are matched on observed shape, not on a version number. pg-boss's release→schema-version mapping is not published as a contract, and a guessed mapping would reintroduce the very failure this guards against. The version integer is read, reported, and used to say "this is newer than anything we have verified" — but it never decides which SQL runs.

  • Optional columns are feature-detected. No heartbeat_on? Stuck-job detection degrades to age-based and says so, instead of failing.

  • Unknown layouts are refused, by name. A pre-v10 schema is recognised specifically and rejected with the reason, because diagnosing it against modern queries would silently miss every archived job. A wrong diagnosis is worse than a refusal.

server_info reports the matched dialect, the schema version, whether that version has been verified against real pg-boss, and any reduced capabilities.

This is not a theoretical concern — it has already caught a real bug. The dialect originally claimed a v10 floor, on the belief that v10 removed the archive table. Booting pg-boss 10.4.2 showed the archive table still present and expiry still an expire_in interval, so the dialect was rejecting v10 outright and matching nothing at all for it. The real floor is v11, and v10 now has its own dialect: recognised, and refused by name, because reading the job table alone on v10 silently misses everything already archived.

CI keeps this honest. The integration suite boots pg-boss 10, 11 and 12 into separate schemas and asserts that the observed schema version appears in the dialect's verified list — so a future pg-boss that changes the schema fails loudly rather than running unverified SQL.

Read-only, by construction

Every query runs inside a BEGIN READ ONLY transaction with a statement_timeout and a row cap, and is always rolled back. Recovery actions are recommended, with exact commands — never executed. A confused agent cannot purge your queue, because the database itself refuses the write.

Three independent guarantees, because the failure being guarded against is writing to someone's production queue:

  1. BEGIN READ ONLY on every transaction

  2. default_transaction_read_only=on at connection level

  3. The docs tell you to connect as a least-privilege role — the only guarantee that does not depend on this code being correct

Timeouts bind as parameters via set_config(..., is_local => true) rather than being interpolated into SQL. The schema name — the one identifier that cannot be a bind parameter — is validated against an identifier grammar and quoted.

Log correlation (optional)

Queue state says that a job failed; application logs usually say why. Point the server at a log backend and findings quote the lines behind a failure.

QUEUE_DOCTOR_AXIOM_TOKEN=xapt-...      # read-capable PAT
QUEUE_DOCTOR_AXIOM_DATASET=app-prod
QUEUE_DOCTOR_AXIOM_ORG_ID=your-org
QUEUE_DOCTOR_AXIOM_QUEUE_FIELD=job     # field carrying the queue name

Deliberately optional, and deliberately unable to break anything: a dead log backend never turns a working diagnosis into a failed one, and "we did not look" stays distinguishable from "we looked and found nothing" — otherwise an absent log line reads as evidence of absence. Half-configured settings are a startup error rather than a silent downgrade.

Reaching a database you cannot connect to

Production queues are often the ones you most want diagnosed and least able to reach: Postgres bound to loopback, no port forwarding, only the application in front of it exposed. Opening the database to the network so a diagnostic can connect is a poor trade — the grant is permanent and far wider than the need.

So the server can run its SQL over HTTPS against a read-only SQL endpoint instead:

QUEUE_DOCTOR_HTTP_SQL_URL=https://your-app.example/api/admin/sql
QUEUE_DOCTOR_HTTP_SQL_TOKEN=...

Set these and no connection string is needed; set both and the HTTP transport wins, so an ambient DATABASE_URL cannot quietly become the target. The endpoint must accept {"query": "...", "params": [...]} and answer with {"rows": [...], "truncated": bool}. Reference implementation: showbook's /api/admin/sql.

The safety properties move to the far end, which is an improvement rather than a compromise. The endpoint opens its own read-only transaction, enforces its own timeout and row cap, can rate-limit, can log every query, and can be backed by a role with narrower grants than the application's own — none of which depend on this client being correct. What changes for you: the endpoint's statement_timeout and row cap win over QUEUE_DOCTOR_STATEMENT_TIMEOUT_MS and QUEUE_DOCTOR_MAX_ROWS, a truncating endpoint is reported as truncated rather than silently short, and one diagnose costs roughly a dozen requests against whatever rate limit is in force.

Bind parameters are required, not optional: a client forced to inline its own literals to reach a read-only endpoint would be building an injection sink to get there.

Configuration

Variable

Default

Purpose

QUEUE_DOCTOR_DATABASE_URL / DATABASE_URL

—

Required, unless the HTTP transport is used. Postgres connection string

QUEUE_DOCTOR_HTTP_SQL_URL

—

Read-only SQL endpoint to query through instead of connecting

QUEUE_DOCTOR_HTTP_SQL_TOKEN

—

Bearer token for that endpoint

QUEUE_DOCTOR_BACKEND

pgboss

pgboss or graphile

QUEUE_DOCTOR_SCHEMA

per backend

Schema the queue was installed into

QUEUE_DOCTOR_STATEMENT_TIMEOUT_MS

5000

Per-query timeout (100–120000)

QUEUE_DOCTOR_MAX_ROWS

500

Row cap per query (1–10000)

QUEUE_DOCTOR_LOG_LEVEL

info

debug/info/warn/error/silent (stderr)

QUEUE_DOCTOR_THRESHOLDS

—

JSON object overriding rule thresholds (see below)

See .env.example. Requires Node.js ≥ 20.11.

Tuning the rules

The thresholds are tuned to the queue these rules were extracted from. That is a defensible starting point and a poor universal answer: a queue that legitimately fails fifty times an hour against a flaky upstream does not have a retry storm, and being told it does every time teaches you to stop reading.

Override any of them with a JSON object — only the keys you set change:

QUEUE_DOCTOR_THRESHOLDS='{"stormMinFailures":50,"idleQueueSeconds":2592000}'

Key

Default

Governs

stormMinFailures

20

Failures before a burst counts as a storm

stormDominantShare

0.5

Share one error must hold to be called dominant

stormPeakPerMinute

5

Failures in a minute that mark a burst, not a trickle

stormCriticalFailures

100

Above this a storm is critical, not a warning

expiryProximity

0.95

Fraction of expiry that looks killed rather than failed

expiryMinJobs

3

Jobs at expiry before it is a pattern

heartbeatMissedMultiplier

3

Missed heartbeats before a worker counts as gone

missedScheduleCriticalSeconds

86400

Lateness beyond which a miss is critical

retentionMismatchMin

5

Extra stale failed rows before flagging retention

duplicateTickMin

2

Ticks with duplicate firings before suspecting double registration

idleQueueSeconds

604800

Age at which an empty queue is worth mentioning

correlatedLogSample

5

Log lines attached to a finding as evidence

An unknown key is a startup error, not a warning — a typo that silently leaves the default in place is the failure this prevents. server_info reports the effective values and which ones you set, so you can confirm an override took.

Publishing to the MCP registry

server.json is the registry manifest. Its version and the npm version it points at are both synced by npm version (see scripts/sync-version.mjs), and a test fails if they drift — a registry entry naming a version that is not on npm sends clients to a 404, which is worse than a stale entry.

Ownership is proved by the mcpName field in the published package.json, so npm must be published first:

npm version patch          # syncs src/version.ts and server.json
npm publish                # the registry reads mcpName off this
mcp-publisher login github # device auth as the io.github.<user> namespace owner
mcp-publisher publish

What this is not

  • Not a queue browser. To page through jobs, psql is better.

  • Not a dashboard. This is agent infrastructure; your MCP client is the UI.

  • Not a Redis queue tool. Both supported backends are Postgres-native, which is what makes the read-only transaction guarantee possible at all. BullMQ and Celery would need a different safety story.

  • Not a writer. It will not retry, cancel, or purge anything.

Roadmap

  • Read-only database layer, schema probe, CI

  • The read-only tool surface

  • The diagnosis engine

  • A docker compose up demo with a chaos worker

  • Integration tests against real pg-boss 10/11/12 in CI

  • Log correlation, so findings can cite application logs

  • A second adapter (graphile-worker)

  • Opt-in write tools (retry_job, cancel_job) behind an explicit flag

  • Configurable rule thresholds

Development

npm install
npm run verify     # lint + typecheck + test + build

The unit suite drives the database layer through a scripted fake client and the rules through fixtures reconstructing each motivating incident, so npm test runs with no Postgres, no containers, and no network.

The integration suite boots real pg-boss (v10, v11, v12) and real graphile-worker against a live Postgres:

docker run -d -p 55432:5432 -e POSTGRES_USER=qd -e POSTGRES_PASSWORD=qd \
  -e POSTGRES_DB=qd postgres:16-alpine
QUEUE_DOCTOR_TEST_DATABASE_URL=postgres://qd:qd@127.0.0.1:55432/qd \
  npm run test:integration

It skips itself when that variable is unset, so a contributor without Postgres is never blocked. For a hands-on run, use examples/demo.

License

MIT

Available Tools

8 tools
diagnoseDiagnoseA
Read-onlyIdempotent

Run the whole diagnosis catalog and return ranked findings — retry storms, expiry overruns, stuck or abandoned workers, missed schedules, duplicate cron registration, retention-window confusion, and idle queues. Each finding carries the evidence it was drawn from and recovery steps ordered safest-first. This is the tool to reach for when the question is "what is wrong?".

ParametersJSON Schema
NameRequiredDescriptionDefault
includeInfoNoInclude informational findings that prevent misreadings but are not breakage.
windowHoursNo
graceMinutesNo
stuckAfterMinutesNo

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already provide readOnlyHint=true, idempotentHint=true, and destructiveHint=false, so the safety profile is covered. The description adds valuable behavioral detail: it returns ranked findings, each carrying the evidence it was drawn from, and recovery steps ordered safest-first. This goes beyond annotations and helps the agent understand the output structure and ordering rationale. No contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is exactly two sentences. The first sentence front-loads the action and scope, then lists the finding categories; the second clarifies the output structure and gives a usage cue. There is zero wasted text — every phrase contributes to purpose, output, or usage.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with four optional parameters and no output schema, the description explains the high-level behavior (what it does, what it returns) and the general trigger. However, it omits any parameter semantics and does not describe the exact structure of the returned findings beyond 'evidence' and 'recovery steps'. Given that annotations cover safety, this is a moderate gap — the agent knows when and why to call it, but not precisely how parameters alter the result.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is only 25% (only includeInfo has a description). The tool description does not mention any of the four parameters (includeInfo, windowHours, graceMinutes, stuckAfterMinutes) at all. Since coverage is low, the description should compensate by explaining parameter roles, but it does not. This leaves the agent to infer meaning from parameter names alone.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with 'Run the whole diagnosis catalog and return ranked findings' — a specific verb and resource — and enumerates concrete finding categories (retry storms, expiry overruns, stuck/abandoned workers, etc.). This distinguishes it from sibling tools like failed_jobs or stuck_jobs, which target single issues, and ends with an explicit purpose cue: 'the tool to reach for when the question is what is wrong?'. The purpose is unambiguous and well differentiated.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives a clear usage condition: 'This is the tool to reach for when the question is what is wrong?' — that tells the agent when to choose it. However, it does not explicitly name alternatives or state when NOT to use it (e.g., for a single specific symptom use the corresponding sibling). That is a clear context but no exclusions, so it stops short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

failed_jobsFailed jobsA
Read-onlyIdempotent

Jobs that failed within the window, newest first, with the error message pulled from each job's output. Bounded on when the job finished — not on the row merely being in the failed state — so results reflect what broke recently rather than everything still inside the queue's retention period.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
queueNoRestrict to a single queue.
windowHoursNo
groupByErrorNoAlso return a per-queue breakdown of error frequency and timing.

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover read-only, idempotent, and non-destructive behavior. The description adds useful behavioral context: results are bounded by job finish time, and error messages are extracted from job output. This goes beyond what annotations provide and helps the agent understand the exact semantics of the result set.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single well-structured sentence with no filler. The main purpose is front-loaded, followed by the key nuance about finish-time bounding. Every clause adds value and the contrast with retention period is efficient.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's moderate complexity and no output schema, the description covers the essential behavior: what is returned, how it's ordered, and the bounding logic. It mentions error messages and, via schema, the groupByError breakdown. Minor gaps exist (e.g., exact pagination or output format), but annotations and the description together give an agent enough to call the tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema describes 'queue' and 'groupByError', but not 'limit' or 'windowHours'. The description mentions the window and finish-time bounding, which indirectly clarifies that windowHours defines the time window, and 'newest first' implies ordering. While it doesn't fully explain parameter ranges or defaults, it provides enough context to compensate for the 50% schema coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool returns failed jobs within a time window, newest first, with error messages. It explicitly distinguishes itself from listing all failed jobs in retention by bounding on finish time, which sets it apart from siblings like stuck_jobs or queue_overview without needing to open their schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explains that results reflect recent failures rather than everything in retention, giving implicit guidance on when to use this tool. It stops short of naming specific alternatives or explicit 'when not to use' criteria, but the contrast with retention period is clear enough for an agent to make the right selection.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

job_detailJob detailA
Read-onlyIdempotent

One job's full record: state, timestamps, retry counters, configured expiry and dead-letter queue, computed duration, and its payload and output. Use this to read the actual error behind a failure surfaced by another tool.

ParametersJSON Schema
NameRequiredDescriptionDefault
jobIdYesThe job id, as reported by failed_jobs or stuck_jobs.

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false, so the safety profile is covered. The description adds behavioral detail by listing the specific data returned, including the computed duration and the ability to read the actual error, which goes beyond the annotations without contradicting them.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two sentences, front-loads the resource and its contents, and immediately states the use case. Every word contributes value; there is no redundant fluff or repetition of schema fields already described.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description carries the burden of explaining return values, and it lists the key fields (state, timestamps, retry counters, expiry/DLQ, duration, payload, output). It also covers the primary use case (reading errors). It does not mention potential error responses or edge cases, but for a simple read-only lookup this is sufficient.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The single parameter jobId is fully documented in the schema with a clear description ('as reported by failed_jobs or stuck_jobs'). Schema coverage is 100%, so the description does not need to add more. It adds no extra semantic detail about the parameter, so a baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('read') and a precise resource ('One job's full record') and enumerates the exact fields returned (state, timestamps, retry counters, expiry, DLQ, duration, payload, output). It clearly distinguishes from siblings like failed_jobs or stuck_jobs by framing this as the detail-retrieval tool for a specific job.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly says 'Use this to read the actual error behind a failure surfaced by another tool,' which tells the agent exactly when to invoke it. It implies alternatives (other tools surface failures) but does not name them or provide explicit when-not-to-use conditions, though the intent is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

missed_schedulesMissed schedulesA
Read-onlyIdempotent

Only the cron schedules whose most recent firing is older than their expression implies. Schedules registered more recently than their last expected tick are excluded, since they cannot have missed it.

ParametersJSON Schema
NameRequiredDescriptionDefault
graceMinutesNo

TDQS

A3.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description adds the specific selection logic — 'most recent firing is older than their expression implies' and the exclusion of schedules registered more recently than their last expected tick. This goes beyond the readOnlyHint, destructiveHint, and idempotentHint annotations, giving the agent a precise behavioral model without contradicting the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences with zero filler. The main scope is front-loaded ('Only the cron schedules...'), and the second sentence adds a necessary clarification about exclusions. Every word earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with only one optional parameter, omitting any explanation of graceMinutes is a critical gap. The description also does not mention the returned shape or pagination, though the lack of an output schema lowers that bar. The parameter gap alone makes the description incomplete for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The sole parameter, graceMinutes, is completely undocumented in both the schema (0% coverage) and the description. An agent cannot determine what graceMinutes affects, how it changes results, or whether it is a tolerance window, a cooldown, or something else. The description provides no meaning for any parameter.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: it lists cron schedules whose most recent firing is older than their expression implies. This is clearly distinct from sibling tools like failed_jobs, stuck_jobs, and schedule_status, making the purpose unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage (when you need missed schedules) and explains the exclusion of recently registered schedules, but it does not explicitly contrast this tool with alternatives or state when not to use it. No sibling tools are mentioned, so an agent must infer the appropriate context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

queue_overviewQueue overviewA
Read-onlyIdempotent

Per-queue health snapshot: job counts by state, jobs stuck active, completions and failures in the window, and each queue's configured expiry, retention and retry limit. Failed counts are reported both within the window and all-time, because the two disagreeing is itself diagnostic.

ParametersJSON Schema
NameRequiredDescriptionDefault
windowHoursNoHow far back to count completions and failures.
includeInternalNoInclude pg-boss's own internal queues.
stuckAfterMinutesNoActive duration past which a job counts as stuck.

TDQS

A3.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly, idempotent, and non-destructive, so the safety profile is covered. The description adds valuable behavioral nuance: it explains that failed counts are reported both within-window and all-time, and that a discrepancy between them is itself diagnostic. This goes beyond annotations and helps the agent interpret results.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two dense sentences with no waste. The core purpose is front-loaded, and the second sentence adds a useful diagnostic insight without bloating the description. Every word earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only snapshot tool with no output schema, the description lists the key return elements (job counts by state, stuck active, completions, failures, and configuration). It also explains the rationale for dual failure counts. The missing piece is an explicit statement of output structure (e.g., 'returns a list of queues'), but it is strongly implied by the content.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%—each parameter already has a clear description (windowHours, includeInternal, stuckAfterMinutes). The tool description does not add any meaning or context about these parameters beyond the schema, so it stays at the baseline of 3.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb+resource: 'Per-queue health snapshot' with specific metrics (job counts, stuck jobs, completions, failures, configuration). It distinguishes from siblings by scope and content, but does not explicitly name any sibling or contrast itself, so it doesn't fully earn a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance on when to use this tool versus alternatives like diagnose or failed_jobs. The description implies a health overview but never states conditions like 'use for a quick queue-wide health check' or 'not for deep dives into individual job failures.' The agent must infer usage context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

schedule_statusSchedule statusA
Read-onlyIdempotent

Every cron schedule pg-boss has registered, with its expression, timezone, last observed firing, and the previous and next firings the expression implies. Expectations are derived from the schedule table itself, so no configuration is needed.

ParametersJSON Schema
NameRequiredDescriptionDefault
graceMinutesNoSlack allowed before a schedule counts as missed.

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description adds useful behavioral context beyond the readOnly and idempotent annotations: it explains that expectations are computed directly from the schedule table and no configuration is needed, implying deterministic output independent of external setup. It doesn't discuss edge cases like empty schedules, but the added derivation detail earns credit.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two sentences with the primary scope front-loaded in the first sentence and a brief, non-redundant explanatory note in the second. Every clause adds substance, and there is no filler or repetition of the tool name.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple read-only list tool with one optional parameter and no output schema, the description adequately covers the return fields (expression, timezone, last observed firing, previous/next firings) and the data source. It could mention whether schedules with no recorded firing are included, but nothing essential for calling the tool is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema fully documents graceMinutes with a default, min/max bounds, and a description, so the schema carries the parameter burden. However, the description provides no additional meaning about how graceMinutes affects the returned schedules, so it neither helps nor hurts beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description explicitly states the tool's function: it provides every cron schedule pg-boss has registered, along with expression, timezone, last observed firing, and the previous/next firings implied. This clearly differentiates it from sibling tools like failed_jobs, stuck_jobs, and missed_schedules, which address different concerns.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description offers no explicit guidance on when to use this tool versus alternatives such as missed_schedules or queue_overview. The note that expectations are derived from the schedule table and require no configuration is behavioral, not a usage heuristic, so an agent gets no help selecting this over siblings.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

server_infoServer infoA
Read-onlyIdempotent

Report queue-doctor's configuration, database connectivity, and the detected pg-boss schema — including which dialect matched, whether that schema version has been verified, and any reduced capabilities. Call this first whenever another tool returns an unexpected or empty result.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, and destructiveHint, covering the safety profile. The description adds valuable context about what the report contains — dialect match, schema verification, reduced capabilities — which helps the agent interpret results without contradicting the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two sentences with no wasted words. The first sentence front-loads the report contents, and the second provides the triggering condition. Every clause earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter diagnostic tool with no output schema, the description provides sufficient context: what it reports and when to call it. It does not detail the return structure, but the listed components and the explicit usage cue make it complete enough for an agent to invoke correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

This tool has 0 parameters, and schema coverage is 100% by default. Per the rubric, 0 params earns a baseline of 4; the description does not need to add parameter semantics and correctly stays silent on that front.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('Report') and a specific resource ('queue-doctor's configuration, database connectivity, and the detected pg-boss schema'), enumerating the exact scope of the report. This clearly distinguishes it from the sibling diagnostic tools and goes well beyond the title.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives an explicit when-to-use instruction: 'Call this first whenever another tool returns an unexpected or empty result.' This is clear context, but it does not mention alternatives or exclusions, so it stops short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

stuck_jobsStuck jobsB
Read-onlyIdempotent

Jobs that have been active longer than a threshold, with age, configured expiry, and — where the schema supports heartbeats — how long since the worker last checked in. A stale heartbeat distinguishes an abandoned job from a merely slow one.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
olderThanMinutesNo

TDQS

B3.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover read-only, idempotent, non-destructive behavior, so the description does not need to repeat those. It adds valuable context by defining what 'stuck' means and explaining that a stale heartbeat distinguishes abandoned from slow jobs — this helps the agent interpret output correctly. It does not disclose output format or pagination, but for a read-only tool with these annotations this is adequate.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact—two sentences—and the key concept is front-loaded. The second sentence about heartbeat adds interpretive value rather than fluff. It could be slightly tighter, but every clause contributes meaning.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple read-only list tool with no output schema and two parameters, the description covers the core concept well but omits the limit parameter semantics and does not explicitly state that it returns a list of jobs. The heartbeat distinction is a nice addition, but the missing parameter explanation leaves an agent guessing about one of the two inputs.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate for both parameters. It implicitly maps olderThanMinutes to 'active longer than a threshold', but does not explain the limit parameter's purpose (number of results). No defaults, ranges, or constraints are mentioned. The description largely focuses on output fields rather than parameter behavior, leaving one parameter entirely undocumented.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly identifies the resource ('Jobs that have been active longer than a threshold') and the included fields (age, configured expiry, heartbeat age). It does not use an explicit verb like 'list' or 'get', but the intent is unambiguous and the heartbeat distinction differentiates it from related concepts like failed jobs. It lacks direct sibling differentiation, but the semantics of 'stuck' are specific enough.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no explicit guidance on when to use this tool versus alternatives like failed_jobs or queue_overview. The description implies use for investigating long-running jobs, but does not state prerequisites, exclusions, or when another tool would be more appropriate. The heartbeat note hints at interpretation, not usage.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 8 tool updatesv0.2.2
    • First observeddiagnose
    • First observedfailed_jobs
    • First observedjob_detail
    • First observedmissed_schedules
    • First observedqueue_overview
    • First observedschedule_status
    • First observedserver_info
    • First observedstuck_jobs

TDQS

A4/5.0

Scored across 8 tools

Disambiguation5/5

Each tool targets a clearly distinct concern: aggregate diagnosis, server/schema info, queue-level health, failed jobs, stuck jobs, schedule listings, missed schedules, and individual job records. The only near-overlap (schedule_status vs missed_schedules) is resolved by one being the full set and the other a filtered diagnostic view.

Naming Consistency4/5

Tool names are uniformly lowercase snake_case and mostly follow a descriptive noun-phrase pattern like failed_jobs, queue_overview, and schedule_status. The single verb-only name diagnose breaks the pattern slightly, but it is still consistent with the diagnostic domain and not confusing.

Tool Count5/5

Eight tools is a well-scoped size for a queue diagnosis server. Each tool covers a distinct diagnostic need without redundancy or filler, and the count is squarely in the comfortable mid-range.

Completeness5/5

The tool surface covers the full diagnostic workflow: server health, per-queue health, failed/stuck jobs, schedule monitoring, missed schedule detection, and deep-dive job details. There are no obvious dead ends; the diagnose tool ties the narrower tools together, and job_detail lets an agent follow up on any surfaced error.

Maintenance

ActivitySlowing
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    F
    maintenance
    Facilitates management and optimization of PostgreSQL databases, offering analysis, setup guidance, and debugging, while ensuring secure and efficient database operations.
    3
    4 npm
    23
    AGPL 3.0
  • A
    license
    A
    quality
    B
    maintenance
    Enables comprehensive PostgreSQL database monitoring, analysis, and management through natural language queries. Provides performance insights, bloat analysis, vacuum monitoring, and intelligent maintenance recommendations across PostgreSQL versions 12-17.
    34
    161
    MIT
  • A
    license
    A
    quality
    C
    maintenance
    Diagnoses and manages PostgreSQL logical replication including slot WAL retention, walsender lag, stuck subscriptions, and provides gated remediation tools for safe AI-driven operations.
    13
    GPL 3.0
  • A
    license
    A
    quality
    B
    maintenance
    Read-only PostgreSQL performance auditing via MCP. Collects metrics from PostgreSQL system views, applies deterministic checks for index, query, vacuum, configuration, and connection issues, and returns structured findings for MCP clients.
    5
    MIT