Skip to main content
Glama

DryRun PostgreSQL MCP

一个不需要连接生产环境的 PostgreSQL MCP 服务器。

dryrun 让 AI agent、IDE 和 CI 拥有完整的 schema 感知能力。它基于离线快照,而不是实时数据库连接。你可以对 schema 进行 lint,验证查询,检查迁移安全性,并探索外键关系图。全程无需任何凭据离开 DBA 的机器。

dryrunboringSQL 套件的一部分,与 RegreSQLFixturize 并列。

问题

LLM/AI 编程助手非常擅长编写代码和 SQL 查询,但它们是盲目的。它们不知道你的 schema、索引或约束。它们可能会生成一条迁移,在你最繁忙的表上持有一个 ACCESS EXCLUSIVE 锁,然后拖垮你的应用。

有些 PostgreSQL MCP 服务器会要求你提供数据库连接。而要执行日常的管理操作,你甚至可能需要 SUPERUSER 权限。但这就等于在找麻烦。

我们已经看到这种做法的后果:AI agent 清空生产数据库,以及本应只读的 MCP 服务器中却被发现 SQL 注入

模型不需要查询你的数据库。它需要理解你的 schema:结构、约束、统计信息以及版本特有的行为。这类知识是结构性的,它只在部署迁移时发生变化,不会在两次查询之间变化。

Related MCP server: pg-lens-mcp

DryRun 功能

dryrun 包含两个部分:一个 CLI 工具 和一个 MCP 服务器。CLI 负责提取并分析你的 schema,MCP 服务器负责把这些分析结果暴露给 AI 助手。两部分刻意分开。

CLI – 提取与分析

CLI 会连接到你的 PostgreSQL 数据库,检查完整 catalog(表、视图、索引、约束、分区、函数、枚举、in RLS 策略、触发器、扩展、GUC),并把一份快照写入 .dryrun/history.db。这份快照是其他一切功能的唯一事实来源。

拿到快照后,CLI 就可以离线工作:

  • Lint – 20+ 条规范规则(命名、类型、主键、时间戳、分区)和 13 条可结构化审计规则(重复索引、外键覆盖、循环外键、vacuum 调优)

  • 迁移安全 – 锁类型分析、耗时估算、表重写检测,并为每条 DDL 语句提供安全的替代方案

  • 查询验证 – 通过 libpg_query 解析 SQL、针对实际 schema 校验列引用、反模式检测

  • 快照 diff – 比较快照之间的 schema、planner 统计或活动;对照实时数据库检测偏差

  • Query stats – 按节点捕获 pg_stat_statements,把 ORM 查询变体归纳为 shape,diff 两次捕获,找出新增或变慢的查询

  • Multi-node stats – 每副本统计、seq_scan 热点、路由不均衡

MCP 服务器 – 给 AI 助手一个 schema 大脑

MCP 服务器读取同一份快照,通过 stdio 或 SSE 暴露了 14 个工具,包括 schema 探索、查询验证、plan 分析、迁移检查、linting、vacuum 健康检查以及已捕获的 pg_stat_statements 高频查询。另外还有三个工具(explain_querycheck_driftcolumnar_report)在你连接实时数据库后加入。然后你的 AI 助手在写 SQL 时就已经了解你的数据库。

不需要数据库。助手永远看不到凭据。

为什么要离线

Schema 上下文属于文件,而不是实时连接。 列类型、行数估算、索引定义、外键关系和 PostgreSQL 版本都可以一次性导出并提交到仓库。一个有数据库访问权限的人转储 schema,其他人——无论是人类还是 AI agent——都能在全无凭据的情况下获得完整的 schema 智能。

凭据不应该离开 DBA 的机器。 如果一个 MCP 服务器需要 DATABASE_URL 才能做任何有用的事,那么每个使用它的开发者都需要生产环境凭据。这是一个跟 AI 毫无关系的安全问题。

服务器应该做分析,而不是透传。 返回原始 \d+ 输出,跟你自己把这个输出粘贴进对话中没什么区别。真正的价值在于解读这些数据:检查某条迁移在你的 PostgreSQL 版本下是否安全、标记缺失外键索引、对照实际 schema 校验列引用。

安装

Homebrew:

Homebrew 6.0 要求第三方 tap 必须先被信任,它们的 formula 才能加载:

brew trust --tap boringsql/boringsql
brew install boringsql/boringsql/dryrun

对于 Homebrew 5.x 及更早版本,可以跳过 brew trust 这一步。如果不想信任整个 tap,可以只信任单个 formula:brew trust --formula boringsql/boringsql/dryrun

npm / npx:

如果你已经有 Node.js,那完全不需要额外安装就能运行 dryrun

npx @boringsql/dryrun --version

这条命令会为你当前的平台(darwin-arm64、linux-x64、linux-arm64)获取预编译二进制,缓存到本地,并打印版本号。如果要让 dryrun 永久进入 PATH:

npm install -g @boringsql/dryrun
dryrun --version

这个 npm 包包装的其实就是同一个 Go 二进制,所有 CLI 命令行为完全一致。类似 lint 这样的命令需要先准备好 schema 快照——见 Quickstart。预编译二进制覆盖 macOS(Apple Silicon + Intel)、Linux(x64 + arm64)以及 Windows x64。其他平台(Alpine/musl、Windows arm64)请使用 Homebrew 或从源码构建。

从源码构建:

需要 Go 1.26+。如果没有,请通过 go.dev/dl 安装。

git clone https://github.com/boringsql/dryrun.git
cd dryrun
go build -o bin/dryrun ./cmd/dryrun

生成的二进制位于 bin/dryrun

30 秒演示

安装好 dryrun 后,在一个干净或已 clone 的仓库中,直接基于现成的 schema 快照执行 lint,完全不需要数据库、不需要初始化:

git clone https://github.com/boringsql/dryrun.git
cd dryrun/examples/demo
dryrun lint

(如果你通过 npm 或 Homebrew 安装,但没有 clone 这个仓库?你就没有 examples/demo——请跳到 Quickstart,让 dryrun 指向你自己的 schema。下面示例输出就是 lint 产生的。)

[ERROR] public.audit_log: table has no primary key
       fix: add a primary key (bigint GENERATED ALWAYS AS IDENTITY recommended)
[WARN ] public.audit_log: gap in range partitions: ends at '2024-07-01' but next starts at '2024-10-01'
       fix: inserts into the gap will fail unless a DEFAULT partition exists
[ERROR] public.task_comments: table has no primary key
       fix: add a primary key (bigint GENERATED ALWAYS AS IDENTITY recommended)
[WARN ] public.projects.created_at: timestamp column uses timestamp without time zone instead of timestamptz
       fix: use timestamptz for timestamp columns
[ERROR] public.tasks.project_id: FK 'tasks_project_id_fkey' on column(s) (project_id) has no covering index
       fix: add an index on FK columns to avoid sequential scans on DELETE/UPDATE
[WARN ] public.users.email: column 'email' uses character varying(255), prefer text
       fix: VARCHAR(n) adds a hidden CHECK constraint with no performance benefit
[WARN ] public.user_notifications: table is missing 'created_at' column
       fix: add: created_at timestamptz NOT NULL DEFAULT now()

26 finding(s): 6 error, 20 warning, 0 info (13 tables checked)

无需数据库,这完全都是从离线 snapshot 出来的。

这个 demo 同样可以通过 MCP 来完成。移动 examples/demo 目录,然后给助手注册服务器:

claude mcp add dryrun -- npx -y @boringsql/dryrun mcp-serve

然后,在这个目录下问:“我有哪些表?它们有什么问题?”

MCP 服务器

一条命令就能把服务器接入到你的 AI Agent。setup 会检测 Claude Code、Cursor、Codex 和 Zed,写入对应 Agent 的 MCP 配置,并且添加一条 AGENTS.md/CLAUDE.md 指令,让 agent 在写 SQL 之前先检查 schema:

dryrun setup

如果你想自己指定 Agent,或者是在非交互 shell 中操作,请传入 --agents

dryrun setup --agents=claude,cursor   # or: all

如果只想手动注册服务器:

# for claude code
claude mcp add dryrun -- dryrun mcp-serve

# for codex
codex mcp add dryrun -- dryrun mcp-serve

如果你是通过源码构建的,请使用二进制的完整路径:

claude mcp add dryrun -- /path/to/dryrun mcp-serve

或者,完全不需要安装,直接把客户端指向 npx:

claude mcp add dryrun -- npx -y @boringsql/dryrun mcp-serve

这个 raw 客户端配置为:

{
  "mcpServers": {
    "dryrun": {
      "command": "npx",
      "args": ["-y", "@boringsql/dryrun", "mcp-serve"]
    }
  }
}

服务器会读取当前项目 .dryrun/history.db 中最新的一份快照。完全不需要数据库凭据,AI 助手直接获得来自离线快照的完整 schema 智能。

即使没有快照,服务器也能正常启动,它的工具会返回“没有加载 schema”。用 dryrun init --db "$DATABASE_URL" 创建一份,或者直接拉取团队推送的快照(dryrun snapshot pull --from-path ./snapshots,见 快速开始),然后从 assistant 调用 reload_schema 工具即可。新 schema 会在不重启服务器的情况下自动生效。

对于有多个数据库的项目,针对每个数据库运行一个 dryrun mcp-serve,并在客户端配置中为每个服务器添加一条记录。一个 MCP 进程内原生支持多数据库的问题已经记录在 #7

关于实时数据库配置、SSE transport 以及 Claude Desktop configuration,请查阅 Tutorial

快速开始

有两种启动方式,选择适合你的一种即可。

选项 A:你有数据库访问权限

如果你能连接到某个 PostgreSQL 实例(本地、开发、生产),一条命令就能完成所有事情:

dryrun init --db "$DATABASE_URL"

这会创建 dryrun.toml(包含 [project] id 和默认 profile)、.dryrun/ 数据目录,并把数据库的数据信息捕获到 .dryrun/history.db。快照以 (project_id, database_id) 作为 key;当一个 project 有多个数据库(例如 authbilling)时,你可以按 profile 分别设置 database_id。完整的配置说明请参考 docs/dryrun-toml.md

方式 B:你没有直接访问权限

拥有凭据的人先捕获一次快照,然后推送到一个共享目录——无论是放在仓库里,还是双方都能访问的位置:

dryrun init --db "$DATABASE_URL"
dryrun snapshot push --to-path ./snapshots

他们需要提交两份文件dryrun.toml./snapshots。其他人直接拉取:

dryrun snapshot pull --from-path ./snapshots

snapshot pull 会把快照导入到本地的 .dryrun/history.db。整个过程中,这些人的机器上不需要有任何数据库。

提交 dryrun.toml 不是可选项:快照的 key 是 (project_id, database_id),而 dryrun init 会把实时数据库中的数据库名用作 database_id。如果你的同事在运行 dryrun init 时没有传 --db,他会得到一个不同的 database_id,然后 pull 会报 0 copied,因为它找的 key 源端根本没有。

推送出的快照会包含 planner 和 activity stats,离线工具需要容量和 vacuum 数据时也一样能用——这是纯 JSON 导出做不到的事。如果需要使用的是 registry 而不是文件目录,见 dryrun remote addsnapshot push --remote 两个命令。如果是给别人或给 Agent 读的可读 JSON,dryrun dump-schema 仍然能输出 JSON(它只是导出,不是输入)。

然后,直接使用

dryrun lint

所有命令都只读取 .dryrun/history.db,即可在离线模式下工作。每个 project 都有自己的 dryrun.toml.dryrun/,没有全局状态。记得把 .dryrun/ 加到你的 .gitignore

所有快照都存在 .dryrun/history.db 中,以 (project_id, database_id) 作为 key。这是唯一的 schema source:MCP 服务器、lintdrift 都从其中读取。如果你从旧版本遗留下来一个 .dryrun/schema.json,它会直接忽略,不算数。

多节点:捕获每个 replica 的 activity

snapshot take 是基于主库执行的,负责收集 schema 和 planner stats。但像 idx_scann_dead_tup、上次 vacuum 时间这类 activity 都保存在每个 replica 自身,所以需要逐个捕获副本:

dryrun --profile primary  snapshot take
dryrun --profile replica1 snapshot activity --from "$REPLICA1_URL" --label replica1
dryrun --profile replica2 snapshot activity --from "$REPLICA2_URL" --label replica2

接下来,MCP 的 describe_table(按节点拆分)和 detect kind=anomalies 工具就会展示每个节点的 idx_scan,让你轻松发现雷群不均衡等问题。材质,请参考 docs/multi-node-stats.md

一个 project 对应多个数据库

dryrun snapshot take 使用 (project_id, database_id) 作为快照。默认配置可以直接工作,project_id 是当前目录名,database_id 是从 current_database() 解析出来的真实数据库名。

dryrun init --db "$AUTH_DB"            # captures auth
dryrun snapshot take --db "$BILLING_DB" # captures billing into its own stream
dryrun snapshot list --db "$AUTH_DB"    # only auth snapshots

如果你需要使用稳定的引用(并且让 listdiff 不用反复输入 URL),就在 dryrun.toml 中定义 profile:

[project]
id = "myapp"

[profiles.auth]
db_url = "${AUTH_DATABASE_URL}"
database_id = "auth"

[profiles.billing]
db_url = "${BILLING_DATABASE_URL}"
database_id = "billing"

然后:

dryrun --profile billing snapshot list
dryrun --profile billing snapshot diff --latest

所有 profile 选项都可以在 docs/dryrun-toml.md 查到。

所有与数据库相关的命令(initprobedump-schemadriftstats apply 等等,以及多个 snapshot 系列的子命令)都支持 --profile 参数;当没有显式传入 --db 时,会回退到对应 profile 里解析出的 db_urllint 本身是离线运行的:它只读取 .dryrun/history.db,只有当你显式传入 --db 时才与实时数据库交互。

注意: 目前的 MCP 服务器只支持单数据库。你可以使用默认 profile。或者也可以:每个数据库分别运行一个 dryrun mcp-serve 进程。在一个 MCP 进程内提供原生多数据库的支持已记录在 #7

在整个团队内共享快照

DryRun 的价值在团队环境中会得到更大发挥。多个开发者都可以从任意 POSIX compatible 目录导入快照。

要向发布自己的快照,你需要

cd project_name

# capture from the live DB (use cwd name for project name)
dryrun init --db "$DATABASE_URL"
dryrun snapshot take --db "$DATABASE_URL"
dryrun snapshot push --to-path ./snapshots --all

然后其他开发者就可以将快照导入到其本地 history:

dryrun snapshot pull --from-path ./shared/snapshots --all

快照是内容寻址的({project}/{database}/{ts}-{hash}.json.zst),并且是幂等的——同样的快照推送两次,结果不会变化。

最简单的部署是专门建一个 Git repo 来存放快照。创建一个快照仓库,并把 *.json.zst binary 加入 ``.gitattributes`,这样 git 就不会再去尝试 diff 这些 bundle 文件了。

离线工具(lintcheck_migrationdrift)在 pull 完成之后就能立刻使用。

没有服务器、没有凭据。和之前说的是同一件事。

Push snapshots to an OCI registry

Any OCI registry can hold snapshots: GitHub Container Registry, Google Artifact Registry, Amazon ECR, Docker Hub, Harbor, or a self-hosted one. The registry handles authentication, retention, and access control, so there is no server to run.

Authenticate the same way you would for docker push, register the remote, and push:

docker login ghcr.io
dryrun remote add ghcr --ref ghcr.io/myorg/dryrun --default
dryrun snapshot take --push

snapshot take --push captures and publishes in one step. Consumers pull:

dryrun snapshot pull --remote ghcr

pull fetches only the latest take by default, so cold pulls (fresh CI, empty history.db) stay cheap regardless of how much history the registry holds. Use --full to backfill the entire history, or --federation --since 7d (also 2w, 24h, or a UTC date like 2026-01-01) for a window. Importing push always sends your full local history; since our local history is incremental by content hash, an owner that pushes on a cadence only uploads the new observations each run.

--ref is the registry base. Each database gets a separate repository under it, <ref>/<project_id>/<database_id>, so myapp's auth database lands at ghcr.io/myorg/dryy/myapp/auth. Snapshots map over OCI artifacts addressed by content hash, so pushing the same one twice changes nothing and shared blobs deduplicate on the registry. For Google Artifact Registry, run gcloud auth-foreign docker us-docker.pkg.dev instead of docker login; the rest is identical.

Authentication. By default dryrun reuses your Docker credentials (~/.docker/config.json and credentials helpers), so any registry you can docker login to works without extra configuration. Two overrides on remote add cover the rest:

  • --token-env VAR reads from the environment variable a static bearer token, for registries that issue short-lived tokens (e.g. --token-env GAR_TOKEN fed by gcloud auth print-access-token).

  • --auth gcp uses Google Application Default Credentials directly, so Google Artifact Registry / Container Registry work after gcloud auth login (or a service-account key via GOOGLE_APPLICATION_CREDENTIALS) without configure-docker. The ADC token is refreshed automatically.

See docs/dryrun-toml.md for per-profile remotes and sharing one stream across projects.

More

License

BSD 2-Clause License


I see my earlier draft had many mistakes, including placeholder, "GLOBAL" which is wrong. In the final answer, I need to avoid this. I will now compose the final carefully.

Also note the source uses "The Nextline" for "history". There is no placeholder, but "GXP24" and "GXP25" are specifically placeholder tokens. We must keep them.

I need to keep "GXP24" and "GXP25". In my previous drafts I wrote "GLOBAL" by accident. The correct are GXP24 and GXP25.

Also note: "the rest is identical" - we can translate "rest" as "其余部分".

I need to ensure that in the final, I don't have "tonight" etc.

Let me just write the final clean translation now, manually, and ensure no mistakes.

Final:

### 将快照推送到 OCI 注册表

任何 OCI 注册表都可以容纳快照:GitHub Container Registry、Google Artifact Registry、Amazon ECR、Docker Hub、Harbor 或自托管的注册表。注册表负责身份验证、保留策略和访问控制,因此无需运行任何服务器。

按照 `docker push` 的认证方式,注册远程仓库,然后推送:

GXP24

`snapshot take --push` 在一步中完成捕获和发布。使用者执行拉取:

GXP25

默认情况下,`pull` 只获取最新快照,因此无论注册表保存了多少历史记录,冷拉取(全新 CI 环境、空 `history.db`)都能保持低成本。使用 `--full` 回填整个历史,或使用 `--since 7d`(也可用 `2w`、`24h` 或以 `2026-01-01` UTC 日期)来指定时间窗口。`push` 始终发送你的完整本地历史;由于它是按内容哈希增量,所以按节奏推送的推送每次只会运行出新观测数据。

`--ref` 是注册表基础路径。每个数据库在其下都有自己的仓库,即 `<ref>/<project_id>/<database_id>`,因此 `myapp` 的 `auth` 数据库会在 `ghcr.io/myorg/dryrun/myapp/auth`。快照映射到按内容哈希的 OCI 制品,因此重复推送同一快照不会变化,共享 blob 也会在注册表上去重。对于 Google Artifact Registry,可执行 `gcloud auth config-docker us-docker.pkg.dev` 代替 `docker login`,其余操作完全一样。

**身份验证。** 默认情况下,dryrun 会复用你的 Docker 凭据(`~/.docker/config.json` 和凭据辅助),所以任何你能 `docker login` 的注册表都无需额外配置。`remote add` 还有两个覆盖选项用于其他情况:

*   `--token-env VAR` 将注册表从环境变量读取静态的 bearer token,适用于签发短期令牌的注册表(例如由 `gcloud auth print-access-token` 提供的 `--token-env GAR_TOKEN`)。
*   `--auth gcp` 直接使用 Google Application Default Credentials,因此在 Google Artifact Registry / Container Registry 中,在 `gcloud auth login`(或通过 `GOOGLE_APPLICATION_CREDENTIALS` 服务账号密钥)后即可使用,而无需 `configure-docker`。ADC 令牌会自动更新。

进一步了解 [`docs/dryrun-toml.md`](docs/dryrun-toml.md) 中的 profiles 和 remote 配置来自项目共享一个数据流。

## 更多

* **[教程](TUTORIAL.md)**:离线工作流与多节点全覆盖工具参考
* **[多节点统计](docs/multi-node-stats.md)**:集群统计收集、聚合规则和不带负载检测
* **[查询统计](docs/query-stats.md)**:`pg_stat_statements` 捕获、形状分组和差异对比
* **[配置参考](docs/dryrun-toml.md)**:构成模范的 `dryrun.toml` `--config` 规则` 远程等所在
* **[CLI 稳定性](docs/cli-stability.md)**:你的哪些命令是 stable 还是 experimental
* **[安全概要](SECURITY.md)**:CLI/MCP 分割和数据脱敏
* **[boringSQL](https://boringsql.com)**,博客和项目主页
* **[dryrun 项目页](https://boringsql.com/products/dryrun/)**,概览与文档
* **[环境保护](https://boringsql.com/posts/dont-let-ai-to-prod/)**, 为什么要避免AI接触生产库感?

No, this is getting wrong. I need to stop and give a clean, correct translation. I will now produce the final answer carefully using my translation grammar. Let's do it step by step, but not spend more tokens. I'll produce a final answer in one go.

Final answer (corrected):
A
license - permissive license
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
9dResponse time
6dRelease cycle
18Releases (12mo)
Commit activity
Issues opened vs closed

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    Not graded
    quality
    F
    maintenance
    Provides AI-IDEs with real-time access to PostgreSQL and Supabase database schemas through the Model Context Protocol, enabling smarter code generation in tools like Cursor, Windsurf, and VS Code + Cline.
    9
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Securely connect AI assistants to PostgreSQL databases with read-only access, schema discovery, querying, and performance analysis tools.
    9
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    A read-only MCP server for PostgreSQL schemas and their git history, enabling AI agents to inspect schema details, migrations, ERDs, missing indexes, circular foreign keys, and churn without write access.
    8
    AGPL 3.0

View all related MCP servers

Related MCP Connectors

  • Query PostgreSQL databases in plain English — LLM-generated, safety-validated SQL.

  • Generate realistic, FK-consistent synthetic test data for your databases from your AI assistant.

  • The grounded data layer for any LLM: governed SQL, metrics, lineage and catalog over your data.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/boringSQL/dryrun'

If you have feedback or need assistance with the MCP directory API, please join our Discord server