mcp-postgres
by imangali01
README.md
# mcp-postgres
> **Хотите просто подключить сервер в Claude?** → [CLAUDE_SETUP.md](CLAUDE_SETUP.md)
> — пошаговая инструкция (Claude Code и Claude Desktop, со скриншотами). Дальше в
> этом файле — про то, как устроен сам проект.
Read-only MCP-сервер для PostgreSQL на Python.
Даёт LLM-клиенту (Claude Code, Claude Desktop, любой MCP-совместимый клиент) доступ
к базе **только на чтение**: интроспекция схемы и `SELECT`-запросы. Любые изменяющие
операции — `INSERT`, `UPDATE`, `DELETE`, `CREATE`, `DROP`, `ALTER`, `TRUNCATE`, `GRANT`,
`COPY`, `CALL` и т.д. — отклоняются.
Сервер доменно-нейтральный: он ничего не знает о бизнес-логике конкретной базы,
только механика доступа, защита и аудит.
---
## Какой транспорт когда запускать
| | **stdio** | **http** |
|---|---|---|
| Кто запускает процесс | MCP-клиент сам стартует сервер как дочерний процесс | вы: `docker compose up -d`, сервер работает постоянно |
| Где лежат креды к БД | в `mcp.json` у каждого пользователя | в `.env` на стороне сервера |
| Защита доступа | не нужна (процесс локальный) | bearer-токен `MCP_HTTP_AUTH_TOKEN` |
| Когда выбирать | персональный доступ аналитика к своей БД | общий сервис на команду / общая сервисная роль |
---
## Три уровня защиты от записи
1. **Валидатор SQL** ([sql_guard.py](mcp_postgres/sql_guard.py)) — разбирает запрос в AST
(`sqlglot`, диалект postgres) и пропускает только `SELECT` / `WITH ... SELECT` / `VALUES`.
2. **Сессия Postgres** ([db.py](mcp_postgres/db.py)) — соединение открывается с
`default_transaction_read_only=on`. Даже если валидатор пропустит запись, её отклонит сам Postgres.
3. **Права роли БД** — главный рубеж: отдельная роль с `GRANT SELECT`. Сервер не имеет
инструмента смены кредов — какие права у роли, такие и у клиента.
---
## Инструменты
| Инструмент | Назначение |
|---|---|
| `connection_info` | К чему подключены, какая роль, какие лимиты и гарантии read-only |
| `list_schemas` | Схемы, доступные роли, с числом объектов |
| `list_tables` | Таблицы и представления: тип, размер, оценка строк, комментарий |
| `describe_table` | Колонки, типы, NOT NULL, DEFAULT, комментарии, ограничения, индексы |
| `list_indexes` | Индексы с определением, размером и статистикой использования |
| `table_stats` | Размеры, live/dead tuples, последний vacuum/analyze |
| `explain` | План выполнения (`EXPLAIN VERBOSE`); `ANALYZE` — только если явно разрешён |
| `query` | Выполнение читающего запроса. Параметры: `sql`, `params`, `max_rows`, `format` |
---
## Запуск: stdio (локально)
Нужен Python 3.11+. Устанавливать пакет не надо — только зависимости:
```bash
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
```
В `mcp.json` клиента (полный пример — [examples/mcp.json](examples/mcp.json)):
```json
{
"mcpServers": {
"postgres": {
"command": "/путь/к/репозиторию/.venv/bin/python",
"args": ["/путь/к/репозиторию/run_server.py"],
"env": {
"PG_HOST": "db.example.com",
"PG_PORT": "5432",
"PG_DATABASE": "analytics",
"PG_USER": "mcp_readonly",
"PG_PASSWORD": "СЮДА_ПАРОЛЬ",
"PG_SSLMODE": "require"
}
}
}
}
```
## Запуск: http (Docker)
```bash
cp .env.example .env # заполнить креды и MCP_HTTP_AUTH_TOKEN
docker compose up -d --build
curl -s localhost:8000/health
```
В `mcp.json` клиента:
```json
{
"mcpServers": {
"postgres": {
"type": "http",
"url": "http://127.0.0.1:8000/mcp",
"headers": { "Authorization": "Bearer СЮДА_ТОКЕН" }
}
}
}
```
Логи — `docker compose logs -f`, остановить — `docker compose down`.
> На Linux каталог `./logs` монтируется внутрь контейнера: он должен быть доступен
> на запись пользователю `uid 10001`, иначе сервер откажется стартовать
> (`chown 10001 logs` или `chmod 777 logs`).
## Локальный Postgres для разработки
Нужен только для быстрой проверки, что сервер вообще работает — не для прод-данных:
```bash
docker compose -f docker-compose.postgres.yml up -d
```
Поднимает голый `postgres:15` с суперпользователем `postgres/postgres`. Оба
compose-файла используют общую сеть `mcp-net`, поэтому в `.env` сервера достаточно
`PG_HOST=postgres`. Данные лежат в `./data` (в git не попадают), `down` их не трогает.
Read-only роль под реальный доступ создаётся отдельно, вручную — см. SQL в разделе
[«Три уровня защиты»](#три-уровня-защиты-от-записи) выше.
---
## Конфигурация
Всё задаётся переменными окружения (в `mcp.json` → `env` или в `.env` для Docker).
Полный список с комментариями — в [.env.example](.env.example).
**Подключение:** `PG_DSN` (или `DATABASE_URL`) либо по частям — `PG_HOST`, `PG_PORT`,
`PG_DATABASE`, `PG_USER`, `PG_PASSWORD`, `PG_SSLMODE`.
**Транспорт:** `MCP_TRANSPORT` = `stdio` | `http`, `MCP_HTTP_HOST`, `MCP_HTTP_PORT`,
`MCP_HTTP_PATH`, `MCP_HTTP_AUTH_TOKEN`.
**Лимиты на запрос:** `MCP_MAX_ROWS` (1000), `MCP_MAX_RESULT_BYTES` (1 000 000),
`MCP_MAX_SQL_LENGTH` (20 000), `PG_STATEMENT_TIMEOUT_MS` (30 000), `MCP_ALLOWED_SCHEMAS`,
`MCP_ALLOW_EXPLAIN_ANALYZE` (по умолчанию `false`).
**Логи и аудит:** `LOG_LEVEL`, `LOG_FORMAT` (`text` | `json`), `LOG_FILE`, `LOG_SQL`, `AUDIT_FILE`.
---
## Логи и аудит
Два независимых потока.
**Логи работы сервера** — старт, подключение к БД, ошибки, отказы авторизации. Всегда идут
**в stderr** (и опционально в `LOG_FILE`): stdout занят JSON-RPC.
**Журнал аудита** — по одному JSONL-событию на каждый вызов инструмента: кто, что и когда
делал (`tool`, `status`, `db_user`/`db_name`, `sql`, `row_count` и т.д.), но без данных
результата — только их объём. Пишется в `AUDIT_FILE` (по умолчанию `./logs/audit.jsonl`);
если файл недоступен на запись, сервер не стартует.
---
## Тесты
Автотестами покрыт SQL-валидатор — та часть, где ошибка стоит дороже всего:
```bash
python -m pytest -q # именно `python -m`, из корня репозитория
```
Остальное проверяется вручную из MCP-клиента: `connection_info` → `list_tables`
→ `describe_table` → `query`.
---
## Ограничения
- Одна база на процесс. Смена подключения на лету не предусмотрена намеренно:
креды = identity, и менять их должен конфиг, а не модель.
- `params` работает с плейсхолдерами `%s` (psycopg), не `$1`.
- Курсоры/стриминг больших выборок не поддерживаются: ответ всегда ограничен
`MCP_MAX_ROWS` и `MCP_MAX_RESULT_BYTES`.
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues