mcpField
M
mcp-queue-doctor

mcp-queue-doctor

本地服务
github.com/ethanasm开发工具012 周前更新

关于 mcp-queue-doctor

mcp-queue-doctor 是用于诊断 PostgreSQL 作业队列的 MCP 服务,支持 pg-boss 和 graphile-worker。它提供带证据的故障分析与恢复建议,并通过只读查询避免修改队列。

主要功能

  1. 01检测重试风暴与卡住的作业
  2. 02发现错过的计划任务和过期问题
  3. 03查看队列状态、作业失败与详情
  4. 04按错误类型分析故障
  5. 05根据后端能力限制诊断规则
  6. 06只读查询并给出恢复建议

适用场景

  1. 01排查作业连续失败的共同原因
  2. 02调查长期处于活动状态的作业
  3. 03确认计划任务是否按时触发
  4. 04在不更改生产数据的情况下评估恢复方式

MCP 相关

通过 stdio 向 MCP 客户端提供队列诊断工具,直接连接配置的 PostgreSQL 数据库。诊断查询以只读事务执行,恢复操作仅给出建议,不会执行。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"ethanasm-mcp-queue-doctor": {
"command": "npx",
"args": [
"-y",
"mcp-queue-doctor"
]
}
}
}
启动命令npx -y mcp-queue-doctor

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npmmcp-queue-doctor0.2.2

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

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 无法清空你的队列,因为数据库本身会拒绝写入。

这里有三项相互独立的保证,因为我们要防范的故障是写入他人的生产队列:

  1. 每个事务都执行 BEGIN READ ONLY
  2. 在连接级别设置 default_transaction_read_only=on
  3. 文档要求你使用权限最小化的角色进行连接——这是唯一一项不依赖此代码正确性的保证

超时通过 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_BACKENDpgbosspgboss 或 graphile
QUEUE_DOCTOR_SCHEMA按后端而定安装队列所用的 Schema
QUEUE_DOCTOR_STATEMENT_TIMEOUT_MS5000每次查询的超时时间(100–120000)
QUEUE_DOCTOR_MAX_ROWS500每次查询的行数上限(1–10000)
QUEUE_DOCTOR_LOG_LEVELinfodebug/info/warn/error/silent(stderr)
QUEUE_DOCTOR_THRESHOLDS—覆盖规则阈值的 JSON 对象(见下文)

参见 .env.example。需要 Node.js ≥ 20.11。

调整规则

这些阈值是根据提取这些规则所用的队列进行调校的。它们是一个有理有据的起点,却不是放之四海而皆准的答案:对于一个每小时因不稳定的上游服务而合理地失败 50 次的队列来说,这并不构成重试风暴;如果每次都告诉你发生了重试风暴,最终你就会不再阅读报告。

可通过 JSON 对象覆盖任意阈值——只有指定的键会发生变化:

QUEUE_DOCTOR_THRESHOLDS='{"stormMinFailures":50,"idleQueueSeconds":2592000}'
键默认值用途
stormMinFailures20一次突发被判定为风暴前的失败次数
stormDominantShare0.5某个错误被判定为主导错误所需占比
stormPeakPerMinute5将突发而非零星失败标记出来的每分钟失败次数
stormCriticalFailures100超过此值时,风暴为严重级别,而非警告
expiryProximity0.95看起来像被终止而非失败的到期比例
expiryMinJobs3形成模式前到期的任务数
heartbeatMissedMultiplier3工作进程被判定为失联前错过的心跳次数
missedScheduleCriticalSeconds86400超过此延迟时,漏执行会被判定为严重
retentionMismatchMin5标记保留策略问题前,额外的过期失败行数
duplicateTickMin2怀疑重复注册前出现重复触发的周期数
idleQueueSeconds604800空队列达到该时长后值得提示
correlatedLogSample5作为证据附加到诊断结果中的日志行数

未知键会导致启动错误,而不是警告——这样可以避免拼写错误悄悄保留默认值。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

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。