Metadata-Version: 2.4
Name: postgresql-mcp-server
Version: 1.1.0
Summary: PostgreSQL Model Context Protocol (MCP) server
Author-email: CleanCode <15706058532@163.com>
License-Expression: MIT
Keywords: postgresql,postgres,database,mcp,model-context-protocol
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Topic :: Database
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Operating System :: OS Independent
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: mcp>=1.0.0
Requires-Dist: psycopg[binary]>=3.2
Dynamic: license-file

# PostgreSQL MCP Server

[![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![uv](https://img.shields.io/badge/managed%20by-uv-5C2D91.svg)](https://github.com/astral-sh/uv)
[![MCP](https://img.shields.io/badge/MCP-FastMCP-green.svg)](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 postgresql-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 postgresql-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 postgresql-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 postgresql-mcp-server
```

只读模式开启后，`pg_execute_sql` 仅允许 `SELECT`、`WITH`、`EXPLAIN`、`SHOW` 开头的语句。

## MCP 客户端配置

### uvx 配置

适合包发布后使用：

```json
{
  "mcpServers": {
    "postgresql_mcp_server": {
      "command": "uvx",
      "args": ["postgresql-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": {
    "postgresql_mcp_server": {
      "command": "uvx",
      "args": ["postgresql-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": {
    "postgresql_mcp_server": {
      "command": "uv",
      "args": ["run", "postgresql-mcp-server"],
      "cwd": "E:/PycharmProjects/postgresql-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 postgresql-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 postgresql_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.
