PostgreSQL MCP Server
README.md
# PostgreSQL MCP Server
[](https://www.python.org/downloads/)
[](LICENSE)
[](https://github.com/astral-sh/uv)
[](https://modelcontextprotocol.io/)
一个面向 PostgreSQL 的 Model Context Protocol (MCP) 服务。它基于 FastMCP 和 psycopg 构建,通过环境变量连接 PostgreSQL server,并以 **database + schema 双层白名单** 控制 MCP 客户端可以访问的范围。
PostgreSQL 的访问层级是:
```text
PostgreSQL server / cluster
└── database
└── schema
└── table / view / index / function ...
```
因此本服务的当前上下文也是:
```text
current_database + current_schema
```
切换 database 时会重新建立连接;切换 schema 时会在当前 database 内设置 `search_path`。
## 功能特性
- 🚀 **面向 PostgreSQL server 的多 database 访问**:允许在配置白名单内切换 database。
- 🗂️ **database + schema 双层白名单**:每个 database 都有独立的 schema 白名单。
- 🔐 **安全的 search_path 设置**:使用 psycopg 的 `Identifier` 处理 schema 名,避免手写拼接风险。
- 🧰 **清晰的 MCP 工具前缀**:所有工具均以 `pg_` 开头,便于和其他 MCP 服务共存。
- 👁️ **只读模式**:设置 `POSTGRES_READ_ONLY=true` 后,仅允许 `SELECT`、`WITH`、`EXPLAIN`、`SHOW`。
- 🛡️ **SQL 安全审计**:阻止多语句、注释注入、跨 schema 访问、权限变更、函数/过程/触发器/扩展等高风险操作。
- 📚 **常用元数据工具**:支持列出 database、schema、表,描述表结构,统计表数量。
- 📄 **分页查询**:`pg_execute_sql` 对 `SELECT/WITH` 支持 `limit` 和 `offset`。
## 工具列表
| 工具 | 说明 |
| :-- | :-- |
| `pg_test_connection` | 测试当前 database + schema 上下文是否可连接。 |
| `pg_get_current_context` | 返回当前 database、schema、允许的 database、当前 database 下允许的 schema。 |
| `pg_list_databases` | 列出配置允许访问的 database,并标记当前 database。 |
| `pg_switch_database` | 切换当前 database,可选同时切换目标 database 下的 schema。 |
| `pg_list_schemas` | 列出指定 database 或当前 database 中可见的非系统 schema。 |
| `pg_switch_schema` | 在当前 database 内切换当前 schema。 |
| `pg_execute_sql` | 在当前上下文执行 SQL。查询返回 `data` 和 `count`,写入返回 `affected_rows`。 |
| `pg_list_tables` | 列出指定 database/schema 或当前上下文下的表和视图。 |
| `pg_describe_table` | 返回指定表的字段、数据类型、是否可空、默认值、主键标记。 |
| `pg_count_tables` | 统计指定 database/schema 或当前上下文下的普通表数量。 |
## 安装
推荐使用 `uv`:
```bash
uv sync
```
运行服务:
```bash
uv run pg-mcp-server
```
## 环境变量
| 变量名 | 说明 | 必填 | 示例 |
| :-- | :-- | :-- | :-- |
| `POSTGRES_HOST` | PostgreSQL 主机地址 | 是 | `127.0.0.1` |
| `POSTGRES_PORT` | PostgreSQL 端口 | 否 | `5432` |
| `POSTGRES_USER` | PostgreSQL 用户名 | 是 | `postgres` |
| `POSTGRES_PASSWORD` | PostgreSQL 密码 | 是 | `your_password` |
| `POSTGRES_DEFAULT_DATABASE` | 启动后的默认 database | 是 | `appdb` |
| `POSTGRES_DEFAULT_SCHEMA` | 默认 database 下的默认 schema | 是 | `app` |
| `POSTGRES_ALLOWED_DATABASES` | 允许访问的 database,多个值用英文逗号分隔 | 是 | `appdb,reportdb` |
| `POSTGRES_DATABASE_SCHEMAS` | 每个 database 允许访问的 schema 映射 | 是 | `appdb:app,public;reportdb:reporting,analytics` |
| `POSTGRES_DATABASE_DEFAULT_SCHEMAS` | 每个 database 的默认 schema 映射 | 否 | `appdb:app;reportdb:reporting` |
| `POSTGRES_READ_ONLY` | 是否开启只读模式 | 否 | `false` |
| `POSTGRES_LOG_LEVEL` | 日志级别 | 否 | `INFO` |
不再使用旧的 `POSTGRES_DATABASE`、`POSTGRES_SCHEMA`、`POSTGRES_ALLOWED_SCHEMAS` 配置。
## 快速开始
### 单 database 配置
只允许访问 `appdb` 的 `app` 和 `public` 两个 schema:
```powershell
$env:POSTGRES_HOST="127.0.0.1"
$env:POSTGRES_PORT="5432"
$env:POSTGRES_USER="postgres"
$env:POSTGRES_PASSWORD="your_password"
$env:POSTGRES_DEFAULT_DATABASE="appdb"
$env:POSTGRES_DEFAULT_SCHEMA="app"
$env:POSTGRES_ALLOWED_DATABASES="appdb"
$env:POSTGRES_DATABASE_SCHEMAS="appdb:app,public"
$env:POSTGRES_READ_ONLY="false"
uv run pg-mcp-server
```
### 多数据库、多 Schema 配置
允许访问多个 database,并为每个 database 分别配置可访问的 schema 白名单:
```powershell
$env:POSTGRES_HOST="127.0.0.1"
$env:POSTGRES_PORT="5432"
$env:POSTGRES_USER="postgres"
$env:POSTGRES_PASSWORD="your_password"
$env:POSTGRES_DEFAULT_DATABASE="appdb"
$env:POSTGRES_DEFAULT_SCHEMA="app"
$env:POSTGRES_ALLOWED_DATABASES="appdb,reportdb,auditdb"
$env:POSTGRES_DATABASE_SCHEMAS="appdb:app,public;reportdb:reporting,analytics;auditdb:audit,archive"
$env:POSTGRES_DATABASE_DEFAULT_SCHEMAS="appdb:app;reportdb:reporting;auditdb:audit"
$env:POSTGRES_READ_ONLY="false"
uv run pg-mcp-server
```
上面的配置表示:
```text
appdb
default schema: app
allowed schemas: app, public
reportdb
default schema: reporting
allowed schemas: reporting, analytics
auditdb
default schema: audit
allowed schemas: audit, archive
```
配置规则:
- `POSTGRES_ALLOWED_DATABASES` 控制允许访问哪些 database。
- `POSTGRES_DATABASE_SCHEMAS` 控制每个 database 下允许访问哪些 schema,格式为 `database:schema1,schema2;database2:schema3,schema4`。
- `POSTGRES_DATABASE_DEFAULT_SCHEMAS` 控制切换到某个 database 时默认使用哪个 schema。
- `POSTGRES_DEFAULT_DATABASE` 和 `POSTGRES_DEFAULT_SCHEMA` 是服务启动后的初始上下文,必须在对应白名单中。
### 只读模式
```powershell
$env:POSTGRES_READ_ONLY="true"
uv run pg-mcp-server
```
只读模式开启后,`pg_execute_sql` 仅允许 `SELECT`、`WITH`、`EXPLAIN`、`SHOW` 开头的语句。
## MCP 客户端配置
### uvx 配置
适合包发布后使用:
```json
{
"mcpServers": {
"pg_mcp_server": {
"command": "uvx",
"args": ["pg-mcp-server@latest"],
"env": {
"POSTGRES_HOST": "127.0.0.1",
"POSTGRES_PORT": "5432",
"POSTGRES_USER": "postgres",
"POSTGRES_PASSWORD": "your_password",
"POSTGRES_DEFAULT_DATABASE": "appdb",
"POSTGRES_DEFAULT_SCHEMA": "app",
"POSTGRES_ALLOWED_DATABASES": "appdb,reportdb",
"POSTGRES_DATABASE_SCHEMAS": "appdb:app,public;reportdb:reporting,analytics",
"POSTGRES_DATABASE_DEFAULT_SCHEMAS": "appdb:app;reportdb:reporting",
"POSTGRES_READ_ONLY": "false",
"POSTGRES_LOG_LEVEL": "INFO"
}
}
}
}
```
### 多数据库、多 Schema 客户端配置
下面示例允许 MCP 客户端在 `appdb`、`reportdb`、`auditdb` 三个 database 间切换,并分别限制每个 database 可访问的 schema:
```json
{
"mcpServers": {
"pg_mcp_server": {
"command": "uvx",
"args": ["pg-mcp-server@latest"],
"env": {
"POSTGRES_HOST": "127.0.0.1",
"POSTGRES_PORT": "5432",
"POSTGRES_USER": "postgres",
"POSTGRES_PASSWORD": "your_password",
"POSTGRES_DEFAULT_DATABASE": "appdb",
"POSTGRES_DEFAULT_SCHEMA": "app",
"POSTGRES_ALLOWED_DATABASES": "appdb,reportdb,auditdb",
"POSTGRES_DATABASE_SCHEMAS": "appdb:app,public;reportdb:reporting,analytics;auditdb:audit,archive",
"POSTGRES_DATABASE_DEFAULT_SCHEMAS": "appdb:app;reportdb:reporting;auditdb:audit",
"POSTGRES_READ_ONLY": "false",
"POSTGRES_LOG_LEVEL": "INFO"
}
}
}
}
```
使用该配置时,`pg_switch_database` 只能切换到 `appdb`、`reportdb`、`auditdb`;`pg_switch_schema` 只能在当前 database 对应的 schema 白名单内切换。
### 本地开发配置
如果还没有发布到 PyPI,可以让 MCP 客户端在项目目录中通过 `uv run` 启动:
```json
{
"mcpServers": {
"pg_mcp_server": {
"command": "uv",
"args": ["run", "pg-mcp-server"],
"cwd": "E:/PycharmProjects/pg-mcp-server",
"env": {
"POSTGRES_HOST": "127.0.0.1",
"POSTGRES_PORT": "5432",
"POSTGRES_USER": "postgres",
"POSTGRES_PASSWORD": "your_password",
"POSTGRES_DEFAULT_DATABASE": "appdb",
"POSTGRES_DEFAULT_SCHEMA": "app",
"POSTGRES_ALLOWED_DATABASES": "appdb,reportdb",
"POSTGRES_DATABASE_SCHEMAS": "appdb:app,public;reportdb:reporting,analytics"
}
}
}
}
```
不同 MCP 客户端对 `cwd` 字段的支持可能不同;如果客户端不支持 `cwd`,请把命令改成在项目目录中执行的绝对路径包装脚本,或使用已发布的 `uvx pg-mcp-server` 方式。
## 使用示例
查看当前上下文:
```json
{}
```
切换到 `reportdb` 的默认 schema:
```json
{
"database": "reportdb"
}
```
切换到 `reportdb.analytics`:
```json
{
"database": "reportdb",
"schema": "analytics"
}
```
查询并分页:
```json
{
"sql": "SELECT id, name FROM users ORDER BY id",
"fetch_results": true,
"limit": 20,
"offset": 0
}
```
描述指定上下文中的表:
```json
{
"database": "reportdb",
"schema": "analytics",
"table": "daily_metrics"
}
```
## 安全策略
这个服务的安全策略用于降低 MCP 自动化场景中的误操作风险,但不能替代数据库侧权限控制。生产环境建议使用最小权限账号,并只开放必要 database 和 schema。
服务会在执行 SQL 前进行以下审计:
- 禁止多语句执行,例如 `SELECT ...; DROP TABLE ...`。
- 禁止 SQL 注释,降低注释截断和注入风险。
- 禁止 `CALL`、`DO`、`EXEC`、`GRANT`、`REVOKE` 等高风险语句。
- 只允许表和索引类 DDL,例如 `CREATE TABLE`、`ALTER TABLE`、`DROP INDEX`。
- 禁止角色、用户、数据库、扩展、函数、过程、触发器、策略等对象的 DDL。
- 禁止访问当前 database 白名单之外的 `schema.object` 或 `schema.table.column`。
- 允许查询中的别名、CTE、子查询和表名作为列限定符,例如 `u.id`、`orders.id`。
- 对三段式标识符只把第一段识别为 schema,例如 `app.users.id` 只校验 `app` 是否在当前 database 的 schema 白名单中,不会把 `users.id` 再误判为跨 schema 访问。
- 允许访问 PostgreSQL 系统 schema 的只读元数据,例如 `information_schema` 和 `pg_catalog`。
PostgreSQL 原生连接不能直接跨 database 查询。本服务不会伪装支持跨 database SQL;如果需要跨 database 汇总,请分别切换 database 查询,或在数据库侧配置 `postgres_fdw` / `dblink`。
## 开发与测试
同步依赖:
```bash
uv sync
```
运行单元测试:
```bash
uv run python -m unittest
```
语法检查:
```bash
uv run python -m py_compile pg_mcp_server.py
```
当前测试覆盖 SQL 安全审计、database/schema 白名单、database 切换、schema 切换、分页包装和 fake connection 执行顺序。
## 常见问题
### 为什么要同时配置 database 和 schema?
PostgreSQL 的 database 与 schema 是不同层级。客户端连接时必须连接某个 database;表和视图通常属于该 database 下的某个 schema。
### 切换 database 和切换 schema 有什么区别?
切换 database 会重新建立连接;切换 schema 只是在当前 database 内调整 `search_path`。
### 可以直接执行跨 database SQL 吗?
不可以。PostgreSQL 单个连接不能直接跨 database 查询。请使用 MCP 工具分别切换 database 查询,或在数据库侧配置 `postgres_fdw` / `dblink`。
### 这个服务可以替代数据库权限控制吗?
不可以。MCP 层的安全审计是额外保护,数据库账号仍应遵循最小权限原则。
## License
MIT License. See [LICENSE](LICENSE) for details.