PostgreSQL-MCP-Server
by I8Zoro8I
README.md
# PostgreSQL 只读 MCP Server
该本地 MCP Server 使 Codex、Claude Desktop、Cursor 等 MCP 客户端能够安全地查询一个或多个 PostgreSQL 数据库。
## 安全模型
- `query_readonly` 仅接受一条 `SELECT`、`VALUES`、`SHOW` 或 `EXPLAIN` 语句,并始终在 `BEGIN READ ONLY` 事务中执行。
- 表结构相关工具同样运行在只读事务中。
- 所有只读工具都设置了语句超时;查询通过 PostgreSQL 游标至多读取 `MCP_MAX_RESULT_ROWS + 1` 行,用额外一行判断是否截断,不会先把完整结果集读入 Node 进程。
- 响应 JSON 受 `MCP_MAX_RESULT_BYTES` 限制;超限会返回错误,不输出可能被误认为完整数据的部分结果。
- 可通过 `DATABASE_URLS` 配置多个命名数据库。每次工具调用选择一个目标库,不支持把不同 PostgreSQL 实例直接作为同一 SQL 的跨库 JOIN。
- `create_function` 可直接创建任意 Schema 下的新函数;仅允许 `CREATE FUNCTION`,拒绝 `CREATE OR REPLACE`,同名时由 PostgreSQL 报错且不会覆盖。
- `create_pkg_bill_function` 保留为兼容别名,行为与 `create_function` 相同。
- 除函数创建外,其他任何可能修改数据库的语句,必须经你在本机明确审批后才允许执行。
- `execute_approved_write` 每次执行后都会消费审批记录,防止重复执行。
- 数据库角色权限是最终安全边界。日常查询应使用专用只读角色;确需执行已审批写操作时,应使用独立的受限写角色,并仅在需要时以该角色启动单独的 Server 实例。
MCP 协议无法保证客户端一定向人工展示交互式审批弹窗。因此审批边界刻意放在 MCP 工具之外:只有在本机终端执行命令,才能批准待执行的写操作。
## 安装与运行
```bash
npm install
cp .env .env
# 在 .env 中填写 DATABASE_URL 或 DATABASE_URLS 后执行:
npm start
```
Server 使用 stdio 通信。接入 MCP 客户端时不要手动启动;应由客户端负责创建和管理该进程。
## 配置 Codex
将以下内容加入 `~/.codex/config.toml`,并替换项目路径和连接字符串:
```toml
[mcp_servers.postgres_safe]
command = "node"
args = ["/Users/solot/Documents/workplace/python/mcp/src/server.js"]
[mcp_servers.postgres_safe.env]
# 单库旧配置仍然支持,并映射为名为 default 的数据库:
DATABASE_URL = "postgresql://ai_reader:replace_me@127.0.0.1:5432/taxon"
# 多库时可使用 JSON 对象,键名就是工具参数 database 的值:
# DATABASE_URLS = '{"orders":"postgresql://ai_reader:replace_me@127.0.0.1:5432/orders","users":"postgresql://ai_reader:replace_me@127.0.0.1:5432/users"}'
MCP_STATEMENT_TIMEOUT_MS = "10000"
MCP_MAX_RESULT_ROWS = "100"
MCP_MAX_RESULT_BYTES = "1048576"
MCP_APPROVAL_DIR = "/Users/solot/.local/share/postgres-safe-mcp/approvals"
```
保存配置后重启 Codex。
## 配置 Cherry Studio
在 Cherry Studio 的 MCP 服务配置中导入或新增该 Server,命令、参数和环境变量与上面的 Codex 配置保持一致。

## 只读工具
- `list_schemas`
- `list_tables`
- `describe_table`
- `describe_function`
- `query_readonly`
- `explain_query`
自由 SQL 查询中建议主动加入 `LIMIT`,Server 也会对返回结果行数进行限制。
所有数据库工具都必须提供 `database` 参数;缺失时 Server 会停止操作并要求先明确目标数据库,即使单库配置也要显式传入 `default`。多库数据需要分别查询后由客户端关联;若确实需要 SQL 层跨库 JOIN,应在 PostgreSQL 侧使用 FDW 并严格授予只读权限。
涉及跨库参考和创建函数时,AI 会根据自然语言区分“参考库”和“目标库”。例如“V3 在 `catches`,参考函数在 `tide`,先给我修改后的代码”会先从 `tide` 读取函数定义,再仅输出 SQL;只有明确要求“创建到 `catches`”时,才会向 `catches` 执行 `create_function`。
## AI 调用策略
Server 向 MCP 客户端发布“对话优先”指引:
- 数据库设计、SQL 编写、故障分析、优化建议和 SQL 草案默认由 AI 直接在对话中输出,不会自动访问数据库,也不会生成审批文件。
- 仅当你明确要求查询当前库中的实时数据、表结构、统计结果或执行计划时,AI 才应调用只读工具。
- 提到“参考某库函数,在另一库创建/修改”时,AI 应先通过 `describe_function` 读取参考定义;仅要求代码时不能执行创建。
- 提出写操作需求时,AI 应先说明影响范围、风险和建议 SQL;只有你明确要求提交本地审批并执行时,才会创建审批文件。
`MCP_WRITE_APPROVAL_ENABLED` 默认是 `false`,因此 `request_write_approval` 和 `execute_approved_write` 不会发布给 AI,日常咨询、方案讨论和 SQL 编写不会出现审批流程。需要恢复审批能力时,在 MCP Server 环境变量中设置:
```dotenv
MCP_WRITE_APPROVAL_ENABLED=true
```
然后重启 MCP 客户端。审批实现、审批文件和一次性执行保护均会保留。
## 查询权限不足提示
只读工具返回 PostgreSQL `42501` 权限不足错误时,Server 会根据数据库返回的 Schema、表、列信息和当前连接账号,自动在错误结果中输出最小化的 `GRANT CONNECT`、`GRANT USAGE`、`GRANT SELECT` 建议 SQL。
- 仅生成 SQL 文本,不会调用授权语句,也不会创建审批文件。
- 仅在 `list_schemas`、`list_tables`、`describe_table`、`query_readonly`、`explain_query` 等只读调用失败时触发。
- 无法精确判断对象时只输出需 DBA 补全的模板,不会自动扩大为整个 Schema 或数据库的表读取权限。
## 直接创建函数
使用 `create_function` 传入单条 `CREATE FUNCTION [schema.]<函数名>(...)` SQL,可直接执行,无需生成审批文件。
- 不允许 `CREATE OR REPLACE FUNCTION`。
- 不允许包含第二条 SQL,例如单独的 `COMMENT ON FUNCTION`。
- 函数同名或签名相同时,PostgreSQL 会返回错误,原函数保持不变。
## 显式审批写操作
1. 让 MCP 客户端调用 `request_write_approval`,提交一条 `INSERT`、`UPDATE`、`DELETE`、非函数创建 DDL 或其他修改类 SQL。
2. 在本机查看生成的待审批 JSON 文件,其中包含完整 SQL 与 SHA-256 摘要。
3. 在终端中批准该请求:
```bash
npm run approve-write -- <request_id>
```
4. 之后客户端才能使用该请求 ID 调用 `execute_approved_write`。每个审批请求仅能执行一次。
执行该进程的数据库角色必须具备对应的写权限。若仍使用只读 PostgreSQL 账号,已审批写操作会因权限不足而失败,这是预期行为。
## 推荐的 PostgreSQL 角色
为日常查询创建仅具备读取权限的角色:
```sql
CREATE ROLE ai_reader LOGIN PASSWORD 'replace_me';
GRANT CONNECT ON DATABASE taxon TO ai_reader;
GRANT USAGE ON SCHEMA public TO ai_reader;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO ai_reader;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT ON TABLES TO ai_reader;
```
日常 Codex MCP 配置中不要复用数据库超级用户。
## 验证 Server
```bash
npm test
npx @modelcontextprotocol/inspector node src/server.js
```
数据库调用需要设置 `DATABASE_URL` 或 `DATABASE_URLS`。审批请求会绑定创建时选择的数据库,执行时必须使用同一个 `database`。单元测试不依赖数据库。
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues