浏览 MCP与Skills教程与资源

PostgreSQL MCP 只读接入:从专用角色到同一条查询的权限验证

用独立教学库 analytics_lab、角色 mcp_reader 和一张汇总表演示可执行授权;同时说明 Server restricted 模式、数据库 GRANT 和查询成本各自解决什么问题。

限定产品与环境

本例使用 Postgres MCP Pro,不泛指所有数据库 MCP

Server 选用 crystaldba/postgres-mcp(Postgres MCP Pro),这是第三方开源项目,并非 PostgreSQL 核心项目。需要本机已准备好的 PostgreSQL、psql、uv/uvx 和 Codex;本文只演示同机 127.0.0.1:5432 的专用教学库。管理员准备环境后,MCP 全程使用 mcp_reader,不能使用 postgres 管理员连接串。

restricted 模式把工具 SQL 放进只读事务;数据库 GRANT 决定这个角色本来能读哪些表;statement_timeout 限制单条语句等待和执行的总时间。这三层可以互补,任何一层都不能保证查询没有 CPU、磁盘或连接开销。下面是可复现的教学工件与预期,尚未在本站连接真实数据库执行。

管理员仅做准备

在全新 analytics_lab 中创建两行教学数据

把下面内容保存为 setup.sql,由获准管理员用 psql -U postgres -d postgres -v ON_ERROR_STOP=1 -f setup.sql 执行;使用其他管理员身份时替换 -U 值。analytics_lab 或 mcp_reader 已存在就停止,换名称并统一替换全文,不要套到现有生产库。CREATE DATABASE 不在事务块内执行。密码通过 psql 的交互式命令设置,不写进 SQL 文件。

setup.sql / 管理员运行

建立教学库、汇总表与最小角色

\set ON_ERROR_STOP on
DO $mcp_prereq$
BEGIN
  IF EXISTS (SELECT 1 FROM pg_database WHERE datname = 'analytics_lab')
     OR EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'mcp_reader') THEN
    RAISE EXCEPTION 'analytics_lab 或 mcp_reader 已存在;停止,勿覆盖现有对象';
  END IF;
END;
$mcp_prereq$;

CREATE DATABASE analytics_lab;
\connect analytics_lab
CREATE SCHEMA reporting;
CREATE TABLE reporting.monthly_orders (
  month date PRIMARY KEY,
  order_count integer NOT NULL CHECK (order_count >= 0)
);
INSERT INTO reporting.monthly_orders VALUES
  ('2026-08-01', 12), ('2026-09-01', 18);

CREATE ROLE mcp_reader LOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION;
\password mcp_reader
REVOKE ALL ON DATABASE analytics_lab FROM PUBLIC;
GRANT CONNECT ON DATABASE analytics_lab TO mcp_reader;
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
GRANT USAGE ON SCHEMA reporting TO mcp_reader;
GRANT SELECT ON reporting.monthly_orders TO mcp_reader;
ALTER ROLE mcp_reader IN DATABASE analytics_lab SET default_transaction_read_only = on;
ALTER ROLE mcp_reader IN DATABASE analytics_lab SET statement_timeout = '3s';

开头先检查数据库和角色名,发现冲突即停止;CREATE DATABASE 仍单独执行。建库不能包进这段事务,因此后续若因密码输入或其他错误中止,可能留下部分对象,应由管理员检查后处理,不能直接重跑或自动删库。本例 GRANT SELECT 只针对单张教学表,未授予未来所有新表;default_transaction_read_only 可以被会话改变,不能代替表权限。

先绕过 MCP 验证基础连接

以 mcp_reader 登录,核对身份、结果和真实权限

先使用 psql 的密码提示连接,再执行以下 SQL。如果直连就失败,应先处理数据库监听、pg_hba.conf 或账号权限;MCP 无法修复数据库自身的拒绝。

终端

通过交互提示输入密码

psql -h 127.0.0.1 -p 5432 -U mcp_reader -d analytics_lab -W
psql / mcp_reader

核对相同身份与业务结果

SELECT current_database(), current_user;
SHOW default_transaction_read_only;
SHOW statement_timeout;
SELECT month, order_count
FROM reporting.monthly_orders
ORDER BY month DESC LIMIT 10;
SELECT has_table_privilege(current_user, 'reporting.monthly_orders', 'SELECT') AS can_read,
       has_table_privilege(current_user, 'reporting.monthly_orders', 'INSERT') AS can_insert,
       has_table_privilege(current_user, 'reporting.monthly_orders', 'UPDATE') AS can_update;

预期身份是 analytics_lab / mcp_reader;业务查询返回 2026-09-01→18、2026-08-01→12;权限检查为 true、false、false。这里说明预期值,没有虚构查询日志。系统目录仍可能可见,不能据此声称该角色‘看不见任何其他 schema’。

接入同一角色

连接串与 Codex 配置都保持 mcp_reader

通过本机凭证管理方式提供 DATABASE_URI,形式为 postgresql://mcp_reader:密码@127.0.0.1:5432/analytics_lab;密码中的 URL 特殊字符需正确编码。不要把实际连接串写入仓库。以拥有该环境变量的进程启动 Codex,并合并下面配置。uvx 首次会获取第三方包,先核对项目来源;本文不替读者执行安装。

config.toml

restricted + 单一查询工具

[mcp_servers.postgres_lab]
command = "uvx"
args = ["postgres-mcp", "--access-mode=restricted"]
env_vars = ["DATABASE_URI"]
enabled_tools = ["execute_sql"]
给 Codex 的任务

只重复基线 SQL

仅使用 postgres_lab 的 execute_sql。
先运行 SELECT current_database(), current_user;
只有返回 analytics_lab 和 mcp_reader 时才继续。
随后运行:
SELECT month, order_count FROM reporting.monthly_orders ORDER BY month DESC LIMIT 10;
逐项报告两行日期与数量,不添加其他 schema 查询或性能分析任务。
结果与我在 psql 的同一查询不一致时,先停止并保留实际 SQL。

表只有两行,所以 LIMIT 10 返回两行是正确结果。若 restricted 模式或 Server 改写了事务超时,核对 MCP 连接内的实际 SHOW 值,不把 psql 会话的参数截图当作另一个连接的证据。

验证拒写的层级

只读事务拒绝与角色权限拒绝是两种证据

仅在本页新建的教学库中,以 mcp_reader 的 psql 会话执行这个零行更新。它不修改数据,但会检查 UPDATE 权限;显式 READ WRITE 是为了排除‘只读事务提前拦住’的影响,观察角色本身是否有写权限。

psql / mcp_reader / 教学库

零行更新后回滚

BEGIN READ WRITE;
UPDATE reporting.monthly_orders SET order_count = order_count WHERE false;
ROLLBACK;

预期在 UPDATE 处得到表权限拒绝,再用 ROLLBACK 退出失败事务。如果更新没有被拒绝,说明角色获得了额外权限或身份不对;虽然 WHERE false 避免改行,也不能把权限配置标为通过。restricted MCP 中的拒写另由只读事务拦截,二者要分开记录。

迁移到真实业务前

只读不等于便宜,LIMIT 也不保证少扫描

LIMIT 限制返回行数,排序、聚合或缺少索引的筛选仍可能扫描大量数据。真实业务应使用脱敏视图或副本,并由数据库负责人评估查询计划、连接数和超时。EXPLAIN 可以查看计划;EXPLAIN ANALYZE 会实际执行查询,不能在未知成本的生产查询前随手加上。

如果 SQL 能运行但 MCP 失败,保留原 SQL、角色、数据库名、SQLSTATE 和耗时,检查 Server 事务模式与工具错误。不要换成管理员账号尝试‘解决’。本例没有授予性能扩展权限,因此不能据此要求所有健康检查、索引建议工具都可用。

依据与适用范围

本页的一手资料