mcpField
P
Postgres DBA MCP

Postgres DBA MCP

本地服务
github.com/a-lafon数据库01 天前更新

关于 Postgres DBA MCP

Postgres DBA MCP 是供 AI 客户端探索单个 PostgreSQL 数据库的只读 MCP 服务。它以受限的 CSV 结果和 SQL 检查机制,支持数据库检查并避免无界输出或写入操作。

主要功能

  1. 01九项 DBA 工具及锁分析
  2. 02只读 SQL 执行
  3. 03紧凑且有上限的 CSV 结果
  4. 04查询计划与假设索引
  5. 05健康检查与慢查询发现

适用场景

  1. 01检查数据库架构和查询结果
  2. 02审查查询计划与索引选项
  3. 03检查锁、健康状态和慢查询

MCP 相关

它向 MCP 客户端提供有界的 PostgreSQL DBA 工具,包括 SQL 执行、查询计划检查、索引测试和健康检查。默认启用受限模式。

配置

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

mcp.json · 通用配置
{
"mcpServers": {
"a-lafon-postgres-dba": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-e",
"DATABASE_URI",
"ghcr.io/a-lafon/postgres-dba-mcp:0.3.3"
],
"env": {
"DATABASE_URI": "<DATABASE_URI>"
}
}
}
}
启动命令docker run -i --rm -e DATABASE_URI ghcr.io/a-lafon/postgres-dba-mcp:0.3.3
需要配置的环境变量
DATABASE_URI密钥PostgreSQL connection URI of the database to serve

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

安装包
ocighcr.io/a-lafon/postgres-dba-mcp:0.3.3

工具

还没有获取到工具列表

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

详细介绍

postgres-dba-mcp

为 AI 助手提供有界、只读的 PostgreSQL 访问能力。

Claude Code 询问 Pagila 电影数据库哪些演员参演的电影最多,接着询问 Gina Degeneres 的哪些电影被租得最多:每次 execute_sql 调用都会返回紧凑的 CSV,并在答案前附上行数统计

让 Claude Code、Codex 或任何 MCP 客户端探索 PostgreSQL 数据库、运行 SQL、读取查询计划、测试假设索引、找出最慢的查询并检查数据库健康状况,同时避免模型上下文被大量内容淹没,也不会脱离只读模式。

这是一个 Model Context Protocol 服务,为 AI 客户端提供对单个 PostgreSQL 数据库的紧凑、有界访问。它是 crystaldba/postgres-mcp 0.3.0 的即插即用替代方案,包含名称和参数均相同的原有九个工具,以及自带的 analyze_locks,并封装在一个静态二进制文件中。只需修改一行客户端配置即可切换;模型接收的内容则会有所变化(紧凑 CSV、有界结果、每次调用执行一条语句),详见 MIGRATION.md。

为什么

crystaldba/postgres-mcp 为助手提供了合适的 DBA 工具:架构、SQL、带假设索引的计划、热门查询、健康检查和索引建议。但有三点妨碍了日常使用:

  • 结果无界且冗长。 行以 Python repr 格式返回,且没有大小限制。对大表执行普通的 SELECT 会返回数十万个 token,甚至直接失败。
  • 受限模式可被绕过。 它不会检查 FROM 中的函数名,因此“只读”调用仍可运行 pg_read_file 或 pg_advisory_lock 等函数(CVE-2026-85620)。
  • 体量庞大。 Python 容器约为 300 MB,内存占用约为 250 MB。

模型接收的内容会有所变化:

  • 返回紧凑 CSV,并附带明确的结尾说明;每个结果都设有行数、字节数和单元格长度上限,并会提示每次截断;
  • 默认启用受限模式:在只读事务中,使用 PostgreSQL 自带的解析器(libpg_query)解析每条语句,并检查其中任意位置的每次函数调用;
  • 使用一个静态 Go 二进制文件,可在毫秒内启动。

测量结果

在同一个 PostgreSQL 17 数据库和同一个只读角色下,与 crystaldba/postgres-mcp 0.3.0 对比;测量基于 959bb72,日期为 2026-11-01,Agent 任务除外:

  • 每个 execute_sql 响应的令牌数减少 61 %:取两个服务均能回答的 4 个场景(S01、S04、S05、S06)的中位数。每个结果都限制为 32 kB;而 crystaldba 最多返回 268k 个令牌,或失败;
  • 受限模式下,131 条攻击语句中拒绝了 131 条;crystaldba 则会执行 10 条;
  • 内存占用为 9 MB,而非 246 MB,两者均在 Linux 容器中测量;直接运行二进制文件时冷启动需 13 ms,而 crystaldba 包含 docker run 需 2.3 s;
  • 使用 Claude Code 完成了 27 项 Agent 任务中的 27 项,而非 27 项中的 25 项——这是这里唯一有日期的测量:2026-10-22,基于 0.1.0,当时还没有波动性门控,也没有 analyze_locks。

另一方面:工具定义大 39 %(10 个工具共 2732 个令牌,crystaldba 的 9 个工具共 1962 个),而整个 Agent 任务的令牌数相近。从 --tools 中移除 locks,即可获得与 0.3.0 完全相同的工具集。

方法、所有数据(包括 postgres-dba-mcp 表现较差的部分)以及图表:见 BENCHMARK.md。生成这些结果的测试工具位于 bench,所用输入包括:bench/data 中的数据集、testdata/safesql 中的攻击和合法语料,以及 BENCHMARK.md 中列出的场景和任务。运行 cd bench && go run ./cmd/bench all,无需模型账号即可重新测量安全性、内存、冷启动和场景;精确的令牌计数需要使用 --tokens=claude 或设置 ANTHROPIC_API_KEY;Agent 任务及其评判器需要 claude CLI。前置条件以及每个级别所需内容:见 bench/README.md。

模型收到的内容

通过两个服务执行相同的 SELECT * FROM types。crystaldba 0.3.0:

[{'id': 1, 'txt': 'hello', 'empty': '', 'num': Decimal('532.10'), 'arr': [1, 2], 'iv': datetime.timedelta(days=1, seconds=7200), 'ts': datetime.datetime(2026, 3, 2, 10, 14, tzinfo=zoneinfo.ZoneInfo(key='Etc/UTC')), 'bin': b'\xde\xad\xbe\xef'}, {'id': 2, 'txt': None, 'empty': None, 'num': None, 'arr': None, 'iv': None, 'ts': None, 'bin': None}]

postgres-dba-mcp:

id,txt,empty,num,arr,iv,ts,bin
1,hello,"",532.10,"{1,2}",1 day 02:00:00,2026-03-02 10:14:00+00,\xdeadbeef
2,\N,\N,\N,\N,\N,\N,\N
-- 2 rows, 2 ms

值保留 PostgreSQL 文本格式,NULL(\N)与空字符串保持区分;页脚会说明返回了多少行,以及是否有内容被截断。

安装

使用 Homebrew(macOS 和 Linux):

brew install a-lafon/tap/postgres-dba-mcp

使用安装脚本(macOS 和 Linux,amd64 和 arm64):脚本会下载发行版归档文件,根据 checksums.txt 校验,并将二进制文件放入 ~/.local/bin(使用 POSTGRES_DBA_MCP_INSTALL_DIR 更改目录;使用 POSTGRES_DBA_MCP_VERSION=v0.1.0 指定发行版本):

curl -fsSL https://raw.githubusercontent.com/a-lafon/postgres-dba-mcp/main/install.sh | sh

使用 Docker(linux/amd64 和 linux/arm64,以非 root 用户运行,无 shell):

docker run -i --rm -e DATABASE_URI ghcr.io/a-lafon/postgres-dba-mcp:latest

此时 URI 中的 localhost 指容器自身:在 Docker Desktop 上使用 host.docker.internal,在 Linux 上使用 --network=host(见下文)。

也可从发行版归档文件安装,包含 Windows:从 releases page 下载适用于你系统的归档文件(amd64 支持 darwin、linux 和 windows;arm64 也支持 darwin 和 linux),根据 checksums.txt(SHA-256)进行校验,并将 postgres-dba-mcp 二进制文件放到 PATH 中。

使用 Go 工具链(Go 1.26 或更高版本):go install github.com/a-lafon/postgres-dba-mcp/cmd/postgres-dba-mcp@latest。

Linux 二进制文件和镜像使用原生 PostgreSQL 解析器解析 SQL(启动时约占 20 MB 内存)。macOS 和 Windows 二进制文件以及普通的 go install 使用其 WASM 版本,启动时内存峰值接近 300 MB,之后会释放内存。使用 C 编译器时,运行 CGO_ENABLED=1 go install -tags pgquery_cgo,netgo,osusergo github.com/a-lafon/postgres-dba-mcp/cmd/postgres-dba-mcp@latest 可构建原生版本。

运行 postgres-dba-mcp --version 检查安装。如果 shell 找不到该命令,说明 ~/.local/bin 不在你的 PATH 中:将其添加进去,或使用完整路径注册该服务。

使用 Claude Code 快速开始

在环境中使用数据库 URI 注册服务:

claude mcp add pg --env DATABASE_URI=postgres://pgdba_ro:secret@localhost:5432/pgdba -- postgres-dba-mcp

或者将其放入 .mcp.json:

{
  "mcpServers": {
    "pg": {
      "command": "postgres-dba-mcp",
      "env": { "DATABASE_URI": "${DATABASE_URI}" }
    }
  }
}

URI 也可以作为第一个参数(postgres-dba-mcp postgres://...)。如果两者都已提供,则以 DATABASE_URI 为准。服务启动时不会连接数据库:首次调用工具时才会建立连接。

URI 中的连接选项(sslmode、sslrootcert、connect_timeout、……)会生效,search_path 也一样。由于事务池化器会拒绝在连接时设置 search_path,因此它会应用于每个事务,而不是在连接时发送。出于同样的原因,像 timezone 这样的服务端设置会被报告为已忽略。拼写错误的 TLS 选项则会在启动时被拒绝:如果不处理,它会被当作服务端设置读取,连接也会退回到未经验证的证书。

通过 Docker 运行时,容器内的 localhost 指向容器本身,镜像不会对其进行改写。在 Docker Desktop 上,请在 URI 中使用 host.docker.internal;在 Linux 上,请使用 --network=host 或桥接网络中主机的地址。

{
  "mcpServers": {
    "pg": {
      "command": "docker",
      "args": ["run", "-i", "--rm", "-e", "DATABASE_URI", "ghcr.io/a-lafon/postgres-dba-mcp:latest"],
      "env": { "DATABASE_URI": "postgres://pgdba_ro:[email protected]:5432/pgdba" }
    }
  }
}

使用 Codex 快速开始

在环境中使用数据库 URI 注册服务:

codex mcp add pg --env DATABASE_URI=postgres://pgdba_ro:secret@localhost:5432/pgdba -- postgres-dba-mcp

或者将其放入 ~/.codex/config.toml:

[mcp_servers.pg]
command = "postgres-dba-mcp"
env = { DATABASE_URI = "postgres://pgdba_ro:secret@localhost:5432/pgdba" }

Codex 等待工具的时间超过 tool_timeout_sec 后就会停止等待(默认值为 60 s)。如果将 --statement-timeout 调高到超过该值,也要在同一条配置中调高 tool_timeout_sec。

多个数据库

一个进程只服务一个数据库。要使用多个数据库,请为每个数据库添加一条服务配置。每条配置都有各自的工具,因此你可以按服务设置权限规则。所有地方都使用只读角色,并且仅在需要写入的地方使用 --access-mode=unrestricted。

{
  "mcpServers": {
    "pg-prod": {
      "command": "postgres-dba-mcp",
      "env": { "DATABASE_URI": "${PROD_DATABASE_URI}" }
    },
    "pg-staging": {
      "command": "postgres-dba-mcp",
      "args": ["--access-mode=unrestricted"],
      "env": { "DATABASE_URI": "${STAGING_DATABASE_URI}" }
    }
  }
}

在 Claude Code 中,权限规则以服务名称为对象,因此上面的两条配置可以区别对待。例如,在 .claude/settings.json 中,允许使用 pg-prod 的所有工具,并在每次对 pg-staging 执行 execute_sql 前请求确认:

{
  "permissions": {
    "allow": ["mcp__pg-prod"],
    "ask": ["mcp__pg-staging__execute_sql"]
  }
}

URI 之外的凭据

你不必将密码放在 URI 或 .mcp.json 中。驱动程序(pgx)会自行读取标准 libpq 来源;此服务不会为这些来源添加选项。

URI 中不带密码,密码从 ~/.pgpass(或 PGPASSFILE 指定的文件)或 PGPASSWORD 中读取:

DATABASE_URI=postgres://[email protected]:5432/pgdba

~/.pgpass 中的一行格式为 host:port:database:user:password,且该文件不能被其他人读取(chmod 600)。PGPASSWORD=... postgres-dba-mcp 的用法相同。

使用来自 pg_service.conf 的命名服务(或 PGSERVICEFILE 指定的文件):

DATABASE_URI="service=prod"
[prod]
host=db.example.com
port=5432
dbname=pgdba
user=pgdba_ro
sslmode=verify-full

密码绝不会出现在服务日志中:启动日志只会显示 user@host:port/db。

只读角色

在 restricted 模式下,服务会拒绝写入操作,但真正的屏障是 Postgres 角色。请为服务分配一个只能读取数据的角色:

CREATE ROLE pgdba_ro LOGIN PASSWORD 'change-me';
GRANT pg_read_all_stats TO pgdba_ro;                      -- pg_stat_statements text and other sessions' statistics
GRANT USAGE ON SCHEMA public TO pgdba_ro;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO pgdba_ro;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO pgdba_ro;   -- tables created later

如果没有 pg_read_all_stats,analyze_locks 仍会报告 pid、锁和链,但其他角色会话的等待、状态、事务时长和查询文本会显示为 \N,并会在 -- session details hidden: grant pg_read_all_stats to <role> … 行中说明这一点。从 PostgreSQL 15 开始,属于 pg_read_all_stats 但设置为 NOINHERIT 的成员也无法读取任何信息,并会以同样的方式报告;PostgreSQL 14 则只要是该角色的成员就会授予此权限。无论哪种情况,只有报告确实隐藏了信息时才会出现该行。

对于 Agent 应能查看的每个其他模式,请重复执行 USAGE、SELECT 和 ALTER DEFAULT PRIVILEGES 行。首次连接后,如果角色是超级用户、绕过行级安全性、拥有表,或属于权限强大的内置角色(pg_read_server_files、pg_write_server_files、pg_execute_server_program、pg_signal_backend、rds_superuser、azure_pg_admin),服务会在 stderr 上发出警告。如果 PostgreSQL 版本高于 18,服务也会发出警告;18 是检查过受限模式拒绝名单所针对的内置函数的最新版本。

敏感列

受限模式会阻止写入,但不会阻止读取:角色可以读取的任何列都可能进入模型的上下文。如果某些列必须禁止访问,角色就是屏障。请创建专用角色,并且只授予它可以查看的内容,而不是使用 SELECT ON ALL TABLES。

使用列级授权时(此时 Postgres 会拒绝 SELECT *,并返回 SQLSTATE 42501,Agent 必须明确指定可以读取的列):

CREATE ROLE pgdba_agent LOGIN PASSWORD 'change-me';
GRANT pg_read_all_stats TO pgdba_agent;
GRANT USAGE ON SCHEMA public TO pgdba_agent;
GRANT SELECT (id, name, created_at) ON public.accounts TO pgdba_agent;   -- not the email column

也可以通过不包含敏感列的视图实现:

CREATE VIEW public.accounts_safe AS SELECT id, name, created_at FROM public.accounts;
GRANT SELECT ON public.accounts_safe TO pgdba_agent;

请注意,pg_read_all_stats 会显示 pg_stat_statements 中其他用户执行的语句文本,其中可能包含字面值。

标志

标志可以放在数据库 URI 前面或后面,格式为 --name=value 或 --name value。

标志默认值含义
--access-moderestrictedrestricted(只读,并进行 SQL 检查)或 unrestricted(execute_sql 可以修改数据库)
--transportstdiostdio、streamable-http 或 sse(已弃用)
--toolsall工具组的逗号分隔列表:schema、query、explain、stats、health、index、locks 或 all
--max-rows100结果的默认行数上限(至少为 1,且不超过 --max-rows-limit)
--max-rows-limit1000客户端可请求的最高 max_rows 值
--max-bytes32768单个工具结果的字节数上限(至少为 1024)
--max-cell200单元格缩短前允许的默认最长字符数(至少为 16,且不超过 --max-cell-limit)
--max-cell-limit10000客户端可请求的最高 max_cell 值
--statement-timeout30s每条语句的服务端超时时间(Go 时长,必须大于 0)
--allow-function无受限模式下允许的函数名称或模式;可重复指定,支持 schema.name 和 * 通配符
--deny-function无受限模式下拒绝的函数名称或模式;可重复指定,语法相同
--host127.0.0.1HTTP 传输:要绑定的地址
--port8000HTTP 传输:要监听的端口
--allow-remotefalseHTTP 传输:允许绑定非环回地址
--allowed-hosts无HTTP 传输:额外接受的 Host 值(host 或 host:port,逗号分隔列表)
--allowed-origins无HTTP 传输:额外接受的 Origin 值(scheme://host[:port],逗号分隔列表)
--sse-host无--host 的别名(兼容 crystaldba)
--sse-port无--port 的别名(兼容 crystaldba)
--version打印 postgres-dba-mcp <version> 并退出

环境变量:DATABASE_URI(连接字符串),以及驱动读取的 libpq 变量(PGPASSWORD、PGPASSFILE、PGSERVICEFILE、PGSSLMODE 等)。

工具

工具属于一个分组,可通过 --tools 关闭。

  • list_schemas(schema):列出架构及其所有者、类型和表数量。首行 -- 2 schemas not readable by <role>: a, b 表示该角色没有对所列架构的 USAGE 权限。
  • list_objects(schema):列出架构中的表、视图、序列或扩展。表行数是规划器的估算值,并以 ~ 开头。首行 -- 1 table not readable by <role>: t 表示该角色无法对所列的表或视图执行 SELECT(最多显示 5 个名称,之后为 …)。
  • get_object_details(schema):以紧凑形式显示表、视图、序列或扩展的详情(列、键、索引、约束、触发器)。
  • execute_sql(query):执行一条 SQL 语句;在受限模式下为只读,并限制结果范围。
  • explain_query(explain):概述查询计划,可选使用 analyze(仅限 SELECT)、verbose 文本或假设索引。
  • get_top_queries(stats):根据资源用量、总耗时或平均耗时,对当前数据库 pg_stat_statements 中的语句进行排序。如果没有 pg_read_all_stats 权限,PostgreSQL 会屏蔽其他角色的查询文本,输出也会说明这一点。
  • analyze_db_health(health):检查索引、连接、清理和死行、序列、复制、缓存命中率及约束,并且仅报告问题。
  • analyze_query_indexes(index):针对最多 10 条给定查询推荐索引。
  • analyze_workload_indexes(index):针对 pg_stat_statements 中频繁出现的慢语句推荐索引。
  • analyze_locks(locks):报告当前数据库正在发生的锁等待;每个被阻塞的会话及其直接阻塞者占一行,并包含每条阻塞链的根节点、锁及持续时间。waiting 是被阻塞语句的运行时长,也是等待时长的上限,因为 PostgreSQL 不会记录锁等待的开始时间。该工具只读取信息,绝不会取消或终止会话。如果当前没有等待中的锁,则返回 -- no lock wait in progress。即使没有 pg_read_all_stats 权限,该工具仍会报告 PID 和锁,但其他角色会话的持续时间、状态和查询文本会显示为 \N。

假设索引和索引工具需要 hypopg 扩展;get_top_queries 和 analyze_workload_indexes 需要 pg_stat_statements(也需列在 shared_preload_libraries 中)。

输出格式

工具返回纯文本,不会返回结构化 JSON。

  • 表格采用带表头行的 CSV 格式。字段包含逗号或双引号、有前导或尾随空格,或为空时,会加引号。
  • NULL 写作 \N。值中的换行符、回车符和制表符分别写作 \n、\r 和 \t,因此每条记录都只有一行。
  • 单元格超过 --max-cell 个字符时会被截断,并以 …[+<n> chars] 结尾;bytea 值(十六进制文本)也适用。execute_sql 接受 max_cell(默认值为 --max-cell,上限为 --max-cell-limit);单元格缩短时,会添加页脚行,例如 -- 1 cell shortened (txt); pass max_cell (up to 10000) to read more。
  • tsvector 值会概述为 [tsvector: <n> lexemes](只有一个词元时为 [tsvector: 1 lexeme])。选择 col::text 可读取其内容。
  • 每张表格都以 -- 开头的页脚结束(list_schemas 和 list_objects 也可能在表头前出现一行以 -- 开头的注释):-- 12 rows, 38 ms、-- 100 rows, truncated (more than 100), 38 ms 或 -- 57 rows, truncated (32768-byte limit), 38 ms。结果被截断时,会添加一行建议优化查询。没有返回行的语句以其命令标签结束(-- UPDATE 3, 3 ms)。
  • explain_query 以页脚 -- plan, <duration> 结束,表示整个调用的实际耗时。
  • 结果不会超过 --max-bytes,行数也不会超过 max_rows(默认值为 --max-rows)。
  • 错误会作为工具错误返回(isError: true),其文本以 ERROR: 开头;Postgres 提供时,还会附上 (SQLSTATE <code>)、DETAIL:、HINT: 和 POSITION: 行。对于超时语句,Postgres 不会报告限制值,此时会提供 HINT:,指出 --statement-timeout 并说明如何限制查询范围。

示例:

id,name,created_at
1,account-1,2026-03-02 10:14:00+00
2,account-2,2026-03-02 10:14:00+00
-- 2 rows, 4 ms

安全模型

从外到内的各层:

  1. 数据库角色。 它决定可以读取哪些内容。请使用上面的只读角色。
  2. 只读事务。 除非在不受限模式下调用 execute_sql,否则每个工具都在 BEGIN TRANSACTION READ ONLY 中运行,且事务始终会回滚。Postgres 本身会拒绝写入操作。
  3. 语句检查(受限模式)。 execute_sql 只接受一条 SELECT 类型的语句(包括 VALUES、TABLE 和 WITH ... SELECT)、对上述语句执行的 EXPLAIN,或 SHOW。它会拒绝 SELECT ... INTO、行锁定(FOR UPDATE)、包含数据修改的 WITH、事务控制、SET、COPY 及其他所有内容,并说明拒绝原因。语句通过扩展查询协议发送给 Postgres;该协议每条消息只承载一条语句,因此,检查放行的调用中无法附带第二条语句。拒绝信息中如果提到通过 --access-mode=unrestricted 可以运行该语句(下方拒绝列表中的函数除外),则表示正是该模式会拒绝它。
  4. 函数拒绝列表,检查整个语句。 语句中命名的每个函数,无论出现在何处,都会被检查:无论是在 FROM 中、子查询中、CTE 中,还是参数中。如果语句没有命名某个函数,而是通过运算符、类型转换或视图调用它,则不会检查该函数(参见下方的限制)。内置列表涵盖大型对象相关函数(lo_*)、set_config、咨询锁、pg_notify、后端取消和终止、服务器文件读取(pg_read_file、pg_ls_*)、复制槽和 WAL 控制、统计信息重置、索引维护(brin_summarize_*、gin_clean_pending_list)、规划器统计信息写入(pg_restore_*_stats、pg_clear_*_stats)、pg_upgrade 和 initdb 辅助函数、dblink*、query_to_xml*、ts_rewrite、tablefunc 和 xml2 中处理 SQL 字符串的函数(crosstab*、connectby、xpath_table),以及风险扩展中的文件或 HTTP 函数。PostgreSQL 14 到 18 中 pg_catalog 的每个易变函数要么列在此列表中,要么已审核并确认为安全;任何新增函数都会导致测试失败。
  5. 对其他所有函数进行易变性检查(受限模式)。 只读事务会拒绝 SQL 写入,但无法阻止扩展在 C 代码、其他会话或数据库外部执行操作。因此,若上述规则都无法确定某个调用是否安全,系统会在即将运行该语句的事务中查询 pg_proc,并在发现该调用涉及 PostgreSQL 未自带且未经审核放行的 VOLATILE 函数时拒绝执行。IMMUTABLE 和 STABLE 函数会通过检查,因此 PostGIS、pgvector 和你自己的只读函数仍可使用。未显式声明易变性的函数(CREATE FUNCTION 的默认值为 VOLATILE)会被拒绝,并显示其名称:如果函数只读取数据,请将其声明为 STABLE;也可以传入 --allow-function。解析会遵循搜索路径;由于目录查询无法获知服务器根据参数类型会选择哪个重载,因此只要搜索路径可达的同名函数中有一个是易变且未经审核的,该名称就会被拒绝:在搜索路径中的某个模式里重新定义内置函数名后,对该名称的调用会被拒绝,而不会获准。只有会求值其函数的语句才会执行此项查询。
  6. 服务器端限制。 每个事务都会设置 statement_timeout(--statement-timeout)、lock_timeout(10 s)和 idle_in_transaction_session_timeout(60 s)。连接返回连接池时会进行清理(DISCARD ALL、hypopg_reset());连接池最多包含 5 个连接,因此会话状态不会从一次调用泄漏到下一次调用。
  7. 有界输出。 行数、字节数和单元格长度均有限制(参见“输出格式”)。发送给 execute_sql 的 SELECT 会通过服务器端游标读取,每次最多提取 max_rows + 1 行,因此 Postgres 不会计算更多行,而且语句绝不会被重写为附带 LIMIT。其他工具会限制各自的查询。 --allow-function 和 --deny-function 会同时调整拒绝列表和门控规则:用户拒绝模式优先于用户允许模式,用户允许模式优先于内置列表,内置列表优先于门控规则。允许模式重新启用内置拒绝项时,启动时会在 stderr 上提示。

受限模式无法防护的情况:

  • 读取。 角色可以读取的任何表或列都可被读取。请使用该角色(参见「敏感列」)。
  • 你担保的函数。 门控规则会拒绝它不认识的 VOLATILE 函数,但你声明为 STABLE 或通过 --allow-function 允许的函数,都会被视为可信,包括 SECURITY DEFINER 函数。只有会读取数据的函数才标记为 STABLE。
  • 未被直接命名的函数。 视图的主体、运算符和类型转换都会运行语句中未直接命名的函数,因此不会被任何检查发现;同样,调用易变函数的 STABLE 函数也有这个问题,因为 PostgreSQL 会拒绝其中的直接写入,却不会拒绝间接副作用。每种情况都需要有人先在数据库中创建相应对象,因此防护措施是使用受限角色。
  • 开销过大的查询。 耗时但合法的 SELECT 会一直运行,直到触发 --statement-timeout。
  • 不受限模式。 --access-mode=unrestricted 允许 execute_sql 使用该角色的权限运行任意单条语句,并提交更改。截断限制的是读取内容,而不是实际执行内容:语句中直接命名的函数会运行到结束,因此数据库会执行全部内容,而不只是你看到的行。函数调用隐藏在视图、运算符或类型转换后的语句,则会在获取到的行处停止。语句在事务中运行,因此拒绝在事务块中运行的命令(VACUUM、CREATE INDEX CONCURRENTLY、REINDEX CONCURRENTLY、ALTER SYSTEM、CREATE DATABASE)会报错 "cannot run inside a transaction block",与 0.3.0 中的行为一致。启动时会在 stderr 上发出警告。
  • HTTP 传输没有身份验证(见下文)。

HTTP 传输

--transport=streamable-http 会在 http://<host>:<port>/mcp 提供 MCP 服务。--transport=sse 会在 /sse 提供已弃用的 SSE 端点,供 crystaldba 客户端使用。默认情况下,服务会绑定到 127.0.0.1:8000。

postgres-dba-mcp --transport=streamable-http --port=8000 postgres://pgdba_ro@localhost:5432/pgdba

没有身份验证。因此,服务会:

  • 仅绑定到回环地址。除非指定 --allow-remote,否则拒绝非回环地址的 --host;指定后会记录警告;
  • 拒绝 Host 标头不是 localhost、127.0.0.1、[::1](带监听端口)、绑定主机或 --allowed-hosts 条目的请求(403);
  • 拒绝 Origin 标头不是 http://localhost:<port>、http://127.0.0.1:<port>、http://[::1]:<port> 或 --allowed-origins 条目的请求(403)。没有 Origin 的请求(原生客户端)可通过;
  • 将请求正文限制为 1 MiB。

在 Docker 中,服务必须监听容器的所有网络接口,因此需要指定 --host=0.0.0.0 --allow-remote。只在主机的回环地址上发布端口,因为任何能访问该端口的人都能使用此角色访问数据库:

docker run --rm -p 127.0.0.1:8000:8000 \
  -e DATABASE_URI=postgres://pgdba_ro:[email protected]:5432/pgdba \
  ghcr.io/a-lafon/postgres-dba-mcp:latest \
  --transport=streamable-http --host=0.0.0.0 --allow-remote --port=8000

在 Linux 上,将 host.docker.internal 替换为主机在桥接网络上的地址,或使用 --network=host 运行。客户端发送的 Host 标头包含主机端口,因此发布的主机端口必须与 --port 相同(-p 8000:8000)。如果使用不同的端口映射,例如 -p 9000:8000,则添加 --allowed-hosts=localhost:9000。

迁移与安全政策

  • 从 crystaldba/postgres-mcp 0.3.0 迁移:参见 MIGRATION.md,其中列出了所有可观察到的差异。
  • 报告漏洞:参见 SECURITY.md。
  • 参与代码开发:参见 CONTRIBUTING.md。
  • 由一人尽力维护;欢迎提交问题和拉取请求。

许可证

MIT,参见 LICENSE。部分 SQL 改编自 crystaldba/postgres-mcp(MIT,Crystal Corp.),部分改编自 PgHero(MIT,Andrew Kane);两者的许可证副本均收录于 NOTICE。

评论

支持 Markdown

第一次接触 MCP?

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

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