关于 mcp-queue-doctor
mcp-queue-doctor 是用于诊断 PostgreSQL 作业队列的 MCP 服务,支持 pg-boss 和 graphile-worker。它提供带证据的故障分析与恢复建议,并通过只读查询避免修改队列。
主要功能
- 01检测重试风暴与卡住的作业
- 02发现错过的计划任务和过期问题
- 03查看队列状态、作业失败与详情
- 04按错误类型分析故障
- 05根据后端能力限制诊断规则
- 06只读查询并给出恢复建议
适用场景
- 01排查作业连续失败的共同原因
- 02调查长期处于活动状态的作业
- 03确认计划任务是否按时触发
- 04在不更改生产数据的情况下评估恢复方式
MCP 相关
通过 stdio 向 MCP 客户端提供队列诊断工具,直接连接配置的 PostgreSQL 数据库。诊断查询以只读事务执行,恢复操作仅给出建议,不会执行。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
mcp-queue-doctor
一个用于诊断 Postgres 作业队列的 MCP 服务,支持 pg-boss 和 graphile-worker。重试风暴、工作器卡死、计划任务遗漏、过期时间超限:哪里出了问题、原因是什么,以及最安全的恢复方式。
❌ "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 [...]"
第二个答案才是产品。每条发现都附带得出结论的证据,因此你或 Agent 可以检查推理过程,而不是盲目信任结论。
启发式规则的来源
这些规则提取自一项晨间健康检查。自 2026 年 4 月起,它每天都会在生产环境中针对一个驱动约 30 个 cron 队列的 pg-boss 实例运行。每个阈值都经过真实误报或真实漏报故障的调整,下面的每条规则都注明了促使其产生的事件。规则的来源正是重点:这些并非为了 README 凭空编造的启发式规则。
安装
npm install -g mcp-queue-doctor
{
"mcpServers": {
"queue-doctor": {
"command": "mcp-queue-doctor",
"env": {
"QUEUE_DOCTOR_DATABASE_URL": "postgres://readonly:pw@localhost:5432/app"
}
}
}
}
然后询问:“我的作业队列有问题吗?”
想先看看它如何工作? examples/demo 会启动一个临时 Postgres,并在大约一分钟内模拟七种故障。它还会在同一个数据库中部署一个 graphile-worker 实例,让七条规则中的四条刻意保持静默——这是最直观的方式,可以看出声明能力究竟有什么用。
连接方式
该服务使用 stdio,因此每个 MCP 客户端都会将其作为子进程启动。唯一不同的是配置文件的位置,以及该进程能否连接到你的数据库。
Claude Desktop — macOS 上的配置文件为 ~/Library/Application Support/Claude/claude_desktop_config.json,Windows 上为 %APPDATA%\Claude\claude_desktop_config.json。使用上面的 mcpServers 块,然后重启应用。
Desktop 会从应用程序包而非登录 shell 启动子进程,因此 PATH 很精简,裸写 mcp-queue-doctor 或 npx 往往无法解析。请使用绝对路径——全局安装后运行 which mcp-queue-doctor 获取路径,或者提供 npx 的绝对路径,并将 ["-y", "mcp-queue-doctor"] 作为参数。
Claude Code — 一条命令,无需编辑文件:
claude mcp add queue-doctor -e QUEUE_DOCTOR_DATABASE_URL=postgres://... -- npx -y mcp-queue-doctor
添加 -s project,即可在仓库根目录写入纳入版本控制的 .mcp.json,而不是写入个人配置,这样在该仓库中工作的每个人都能使用这个工具。
云端 / 远程会话(Claude Code on the web 以及其他任何无头运行器)——只有纳入版本控制的 .mcp.json 才能生效,因为没有人可以响应批准提示。请引用连接字符串,而不是将其提交到仓库;Claude Code 会在 .mcp.json 中展开 ${VAR} 和 ${VAR:-default}:
{
"mcpServers": {
"queue-doctor": {
"command": "npx",
"args": ["-y", "mcp-queue-doctor"],
"env": { "QUEUE_DOCTOR_DATABASE_URL": "${QUEUE_DOCTOR_DATABASE_URL}" }
}
}
}
项目级服务仍需获得信任才能启动。在无头会话中,这意味着要在仓库的 .claude/settings.json 中设置 enableAllProjectMcpServers: true,因为交互式批准提示不会出现。
真正的限制在于可访问性,而非配置。服务运行在客户端所在的位置,并直接连接 Postgres——中间没有托管组件。因此,云端沙盒只能诊断沙盒内部的数据库:例如 demo 栈,或会话自行启动的开发栈。绑定到你自己主机 loopback 地址的生产队列,沙盒完全无法访问;为了让它可访问而将其暴露出来,也是不明智的做法。
要诊断生产环境,请使用已具备访问路径的机器上的客户端——例如通过 SSH 隧道连接的笔记本电脑:
ssh -N -L 5432:127.0.0.1:5432 prod-host
并将 QUEUE_DOCTOR_DATABASE_URL 指向 127.0.0.1:5432。隧道就是访问授权,终端保持打开多久,授权就持续多久;凭据始终不会离开你的机器。
工具
| 工具 | 解答内容 |
|---|---|
diagnose | 从这里开始。 运行整个规则目录,返回按优先级排序的发现结果及其证据和恢复步骤 |
queue_overview | 按状态统计各队列的数量、卡住的任务,以及每个队列的过期/保留/重试配置 |
failed_jobs | 指定时间范围内的失败任务及错误消息,以及按队列划分的错误频率明细 |
stuck_jobs | 超过阈值仍处于活动状态的任务,包含运行时长、过期时间和心跳陈旧情况 |
missed_schedules | 最近一次触发时间早于其 cron 表达式预期的队列 |
schedule_status | 所有已注册的计划,包含 cron 表达式、时区、上次触发时间和下次预期触发时间 |
job_detail | 单个任务的完整记录:状态、时间、重试次数、负载、输出 |
server_info | 连通性、检测到的架构、匹配的方言和精简后的功能 |
计划的预期触发时间是通过解析 pg-boss 自身的 schedule 表中每个 cron 表达式得出的,因此,常见情况下无需配置。这个健康检查的原始版本维护了一份手工编写的预期任务列表;没人记得添加的任务就会悄无声息地漏掉。
规则目录
| 规则 | 触发条件 | 相关事件 |
|---|---|---|
retry-storm | 大量失败密集发生,且主要由同一个错误导致 | 每日 API 配额超限,导致一夜之间有 875 个语料填充任务失败。数量看起来像是 875 个问题;实际情况却只有一个 |
expiry-overrun | 失败时长集中在队列的过期时限附近 | 上游限流减慢处理速度后,一次全语料扫描无法在 30 分钟的过期时限内完成。系统每晚都将其报告为任务失败;解决办法是设置内部运行时限 |
stuck-jobs | 任务长时间处于活动状态,或心跳已停止 | 工作进程被强制终止且未正常关闭时,记录会一直处于活动状态,直到维护流程回收它们 |
missed-schedule | 最近一次触发时间早于上一次预期触发时间 | 区分“从未触发过”(调度器从未启动)和“停止触发” |
duplicate-registration | 一个 cron 队列在同一轮触发时入队两次 | 某个埋点钩子在每个进程中调用了两次任务注册,因此数周以来每个 cron 任务都运行了两次 |
retention-window | 失败记录数与时间窗口内的计数不一致 | 即使问题已修复,健康检查邮件仍连续数天显示黄色,因为统计中包含了几天前失败的记录 |
dead-queue | 很早之前注册、未安排任务且不包含任何内容 | 已停止运行的生产者,或重构时被移除的注册 |
失败会被分类(rate_limit、transient_transport、auth、
not_found),因为类别会影响建议:针对大量 429 错误的正确应对方式,
与针对连接重置的正确应对方式几乎相反。
后端
| 后端 | 支持情况 | 验证版本 |
|---|---|---|
| pg-boss v11+ | 完全支持 | 11.1.2(架构 26)、12.27.0(架构 37) |
| pg-boss v10 | 已识别,但拒绝使用——见下文 | 10.4.2(架构 24) |
| pg-boss v9 及更早版本 | 已识别,但拒绝使用 | — |
| graphile-worker 0.17 | 部分支持,声明了功能 | 0.17.3 |
通过 QUEUE_DOCTOR_BACKEND=pgboss(默认)或 graphile 选择后端;架构默认值随所选后端变化。
能力,而非零值
不同后端不仅列名不同,建模工作流的方式也不同。 graphile-worker 会在作业成功后将其删除,没有单个作业的过期时间,也没有 工作器心跳,并且将 cron 表达式保存在文件中,而非数据库中。因此,无论付出 什么代价,都无法在其中回答“过去一天完成了多少个作业”。
因此,每个后端都会声明自己能回答哪些问题;依赖缺失数据的规则会保持静默, 而不是报告零值——零值看起来像是经过测量得出的结果。
| 规则 | pg-boss v11+ | graphile-worker |
|---|---|---|
retry-storm | ✅ | ✅ |
stuck-jobs | ✅(需要心跳) | ✅(仅按时长判断) |
expiry-overrun | ✅ | — 不支持过期时间 |
missed-schedule | ✅ | — cron 保存在文件中 |
duplicate-registration | ✅ | — 没有触发历史记录 |
retention-window | ✅ | — 不保留任何内容 |
dead-queue | ✅ | — 没有队列注册表 |
server_info 会报告能力集,并明确说明每项限制。
与 pg-boss 版本对应
pg-boss 的表并非稳定 API。不同版本中,它重命名了所有时间戳列(createdon → created_on),删除了整张表(archive 在 v11 中移除),将一个时长字段从 interval 改为整数(expire_in → expire_seconds),对作业表进行了分区,还添加了会实质影响诊断结果的列(heartbeat_on)。
将某种结构写死在工具中,会导致它在下次升级时出问题——运气不好时,甚至不会有任何提示。这正是提取本工具的健康检查曾经连续数周发出看似笃定、实则错误的“错过计划”警告的原因:pgboss.archive 消失后,实际出现的是 SQLSTATE 42P01。
因此,所有模式知识都以数据形式保存在一个文件中:src/pgboss/dialect.ts:
- 所有关系和列名都在方言中声明。 查询构建器会从中生成标识符,因此支持新的 pg-boss 布局只需修改该文件——其他地方的 SQL 都不会提及 pg-boss 表名。
- 方言根据观测到的结构匹配,而非根据版本号。 pg-boss 的版本与模式版本之间的映射并未作为契约发布,猜测映射会重新引入本设计要防范的问题。会读取并报告版本整数,用于提示“此版本比我们验证过的所有版本都新”,但它绝不会决定运行哪条 SQL。
- 通过检测功能识别可选列。 如果没有
heartbeat_on,卡住作业的检测会降级为按时长判断,并说明这一点,而不是直接失败。 - 遇到未知布局时会明确拒绝。 能明确识别 v10 之前的模式,并说明原因后予以拒绝,因为如果用现代查询诊断该模式,就会在不知不觉间漏掉所有已归档作业。错误的诊断比拒绝诊断更糟。
server_info 会报告匹配的方言、模式版本、该版本是否已通过真实 pg-boss 验证,以及任何受限的能力。
这并非理论上的顾虑——它已经帮助发现了一个真实的 bug。最初,方言将支持下限设为 v10,因为认为 v10 移除了 archive 表。启动 pg-boss 10.4.2 后发现,归档表仍然存在,而且过期字段仍是 expire_in interval,因此方言直接拒绝了 v10,完全无法匹配该版本。实际支持下限是 v11,而 v10 现在也有自己的方言:能够识别,但会明确拒绝,因为在 v10 中只读取作业表,会在不知不觉间漏掉所有已归档的作业。
CI 会确保这一点始终成立。集成测试套件会将 pg-boss 10、11 和 12 分别启动到独立的模式中,并断言观测到的模式版本出现在方言已验证列表中——这样,未来的 pg-boss 如果更改模式,测试就会明确失败,而不会运行未经验证的 SQL。
从设计上确保只读
每次查询都在带有 statement_timeout 和行数上限的 BEGIN READ ONLY 事务中运行,并且始终回滚。恢复操作仅作为建议提供,并附有确切命令——绝不会执行。困惑的 Agent 无法清空你的队列,因为数据库本身会拒绝写入。
这里有三项相互独立的保证,因为我们要防范的故障是写入他人的生产队列:
- 每个事务都执行
BEGIN READ ONLY - 在连接级别设置
default_transaction_read_only=on - 文档要求你使用权限最小化的角色进行连接——这是唯一一项不依赖此代码正确性的保证
超时通过 set_config(..., is_local => true) 作为参数绑定,而不是插入 SQL。模式名称是唯一无法作为绑定参数的标识符,因此会根据标识符语法进行验证并加引号。
日志关联(可选)
队列状态能说明任务发生了故障;应用程序日志通常能说明原因。将服务指向日志后端,诊断结果就会引用故障相关的日志行。
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
此功能特意设为可选,也特意设计成不会造成任何问题:日志后端失效绝不会让原本有效的诊断变成失败,而且“我们没有查看”与“我们查看了,但什么也没找到”可以明确区分——否则,缺少日志行就会被误读为不存在的证据。配置不完整会导致启动错误,而不是静默降级。
连接无法直接访问的数据库
生产队列往往最需要诊断,却也最难访问:Postgres 绑定到 loopback,没有端口转发,只有前面的应用程序对外开放。为了让诊断程序连接而将数据库开放给网络,是一种不划算的权衡——授权会永久生效,而且权限范围远超实际需要。
因此,此服务可以改为通过 HTTPS,对只读 SQL 端点执行 SQL:
QUEUE_DOCTOR_HTTP_SQL_URL=https://your-app.example/api/admin/sql
QUEUE_DOCTOR_HTTP_SQL_TOKEN=...
设置这些变量后就不需要连接字符串;两者都设置时会优先使用 HTTP 传输,因此环境中的 DATABASE_URL 不会在不知情的情况下变成连接目标。端点必须接受 {"query": "...", "params": [...]},并以 {"rows": [...], "truncated": bool} 作答。参考实现:showbook 的 /api/admin/sql。
安全保障转移到了远端,这是改进,而非妥协。端点会自行开启只读事务、强制实施自身的超时和行数上限,还可以进行速率限制、记录每条查询,并使用权限范围比应用程序自身更窄的角色——这些都不依赖此客户端正确运行。对你而言,变化在于:端点的 statement_timeout 和行数上限优先于 QUEUE_DOCTOR_STATEMENT_TIMEOUT_MS 和 QUEUE_DOCTOR_MAX_ROWS;端点截断的结果会标记为 truncated,而不是静默地返回较短结果;每次 diagnose 大约会产生十几次请求,具体取决于当前的速率限制。
绑定参数是必需的,而非可选项:如果客户端为了访问只读端点而不得不将字面值直接插入 SQL,那它就是在构造注入入口。
配置
| 变量 | 默认值 | 用途 |
|---|---|---|
QUEUE_DOCTOR_DATABASE_URL / DATABASE_URL | — | 必需,除非使用 HTTP 传输。Postgres 连接字符串 |
QUEUE_DOCTOR_HTTP_SQL_URL | — | 用于查询的只读 SQL 端点,替代直接连接 |
QUEUE_DOCTOR_HTTP_SQL_TOKEN | — | 该端点的 Bearer 令牌 |
QUEUE_DOCTOR_BACKEND | pgboss | pgboss 或 graphile |
QUEUE_DOCTOR_SCHEMA | 按后端而定 | 安装队列所用的 Schema |
QUEUE_DOCTOR_STATEMENT_TIMEOUT_MS | 5000 | 每次查询的超时时间(100–120000) |
QUEUE_DOCTOR_MAX_ROWS | 500 | 每次查询的行数上限(1–10000) |
QUEUE_DOCTOR_LOG_LEVEL | info | debug/info/warn/error/silent(stderr) |
QUEUE_DOCTOR_THRESHOLDS | — | 覆盖规则阈值的 JSON 对象(见下文) |
参见 .env.example。需要 Node.js ≥ 20.11。
调整规则
这些阈值是根据提取这些规则所用的队列进行调校的。它们是一个有理有据的起点,却不是放之四海而皆准的答案:对于一个每小时因不稳定的上游服务而合理地失败 50 次的队列来说,这并不构成重试风暴;如果每次都告诉你发生了重试风暴,最终你就会不再阅读报告。
可通过 JSON 对象覆盖任意阈值——只有指定的键会发生变化:
QUEUE_DOCTOR_THRESHOLDS='{"stormMinFailures":50,"idleQueueSeconds":2592000}'
| 键 | 默认值 | 用途 |
|---|---|---|
stormMinFailures | 20 | 一次突发被判定为风暴前的失败次数 |
stormDominantShare | 0.5 | 某个错误被判定为主导错误所需占比 |
stormPeakPerMinute | 5 | 将突发而非零星失败标记出来的每分钟失败次数 |
stormCriticalFailures | 100 | 超过此值时,风暴为严重级别,而非警告 |
expiryProximity | 0.95 | 看起来像被终止而非失败的到期比例 |
expiryMinJobs | 3 | 形成模式前到期的任务数 |
heartbeatMissedMultiplier | 3 | 工作进程被判定为失联前错过的心跳次数 |
missedScheduleCriticalSeconds | 86400 | 超过此延迟时,漏执行会被判定为严重 |
retentionMismatchMin | 5 | 标记保留策略问题前,额外的过期失败行数 |
duplicateTickMin | 2 | 怀疑重复注册前出现重复触发的周期数 |
idleQueueSeconds | 604800 | 空队列达到该时长后值得提示 |
correlatedLogSample | 5 | 作为证据附加到诊断结果中的日志行数 |
未知键会导致启动错误,而不是警告——这样可以避免拼写错误悄悄保留默认值。server_info 会报告生效的值以及你设置了哪些值,以便确认覆盖已生效。
发布到 MCP registry
server.json 是 registry 清单。它的版本和所指向的 npm 版本都会由 npm version 同步(参见 scripts/sync-version.mjs),并且测试会在二者不一致时失败——registry 条目指向 npm 上不存在的版本会让客户端遇到 404,这比条目过时更糟。
所有权通过已发布的 package.json 中的 mcpName 字段证明,因此必须先发布到 npm:
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
本项目不是什么
- 不是队列浏览器。 要翻阅任务,
psql更合适。 - 不是仪表板。 这是 Agent 基础设施;你的 MCP 客户端就是 UI。
- 不是 Redis 队列工具。 两种受支持的后端都原生使用 Postgres,这才使只读事务保证成为可能。BullMQ 和 Celery 则需要不同的安全方案。
- 不是写入工具。 它不会重试、取消或清除任何内容。
路线图
- 只读数据库层、Schema 探测、CI
- 只读工具集
- 诊断引擎
- 使用混乱工作进程的
docker compose up演示 - 在 CI 中针对真实的 pg-boss 10/11/12 进行集成测试
- 日志关联,让诊断结果能够引用应用程序日志
- 第二个适配器(graphile-worker)
- 通过显式标志启用可选写入工具(
retry_job、cancel_job) - 可配置的规则阈值
开发
npm install
npm run verify # lint + typecheck + test + build
单元测试套件通过脚本化的模拟客户端驱动数据库层,并通过重现各个问题成因的固定测试数据验证规则,因此运行 npm test 无需 Postgres、容器或网络。
集成测试套件会在运行中的 Postgres 上启动真实的 pg-boss(v10、v11、v12)和 graphile-worker:
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:[email protected]:55432/qd \
npm run test:integration
如果未设置该变量,测试套件会自动跳过,因此没有 Postgres 的贡献者也不会受阻。如需亲自运行,请使用 examples/demo。
许可证
MIT
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。