Как Claude Code безопасно работает с PostgreSQL и MySQL через MCP. Главный принцип — только чтение. Все изменения данных и схемы остаются за человеком.
Read-only по умолчаниюMCP вместо shell2 слоя защиты
Главный принцип: Claude = только чтение
Claude видит данные, но не меняет их
CC подключается к БД через read-only роль. Он анализирует структуру, читает данные, оптимизирует запросы — но любое изменение данных или схемы выполняет человек вручную. Это исключает катастрофу от ошибки в промпте или prompt injection.
Почему это критично: одна неверная команда (DROP TABLE, UPDATE без WHERE, migrate:fresh) уничтожает данные необратимо. Read-only роль делает такие ошибки физически невозможными — даже если CC попросят их выполнить.
Что Claude можно и что нельзя
Может
SELECT-запросы к любым таблицам
EXPLAIN ANALYZE — анализ планов
Просмотр схемы и индексов
Поиск медленных запросов
Написать SQL и migration-файл
Нельзя
INSERT / UPDATE / DELETE данных
DROP TABLE / TRUNCATE
ALTER TABLE напрямую
Запускать миграции (migrate:fresh)
pg_dump / менять права и пользователей
Как Claude обращается к БД
CC не запускает psql в shell. Вместо этого он использует MCP-сервер (например postgres-mcp), который сам подключается к БД под read-only пользователем и возвращает результат:
Claude Code
«Покажи структуру таблицы payments»
→
postgres-mcp
restricted mode — только SELECT
→
PostgreSQL
роль claude_readonly
Почему MCP, а не shell: MCP-сервер даёт структурированный, предсказуемый доступ — без рисков shell-инъекций, без зависимости от PATH, с явным ограничением операций на уровне сервера. Claude получает данные, но не «руки» к базе.
Два слоя защиты
Защита эшелонирована: даже если один слой обойдён, второй останавливает запись.
1
Уровень PostgreSQL claude_readonly
Роль БД с правом только SELECT. Физически не может выполнить запись или изменить структуру — отказ на уровне СУБД, независимо от того, что попросили Claude.
2
Уровень MCP --access-mode=restricted
MCP-сервер сам блокирует DDL и операции записи ещё до того, как запрос дойдёт до БД. Двойная страховка: ошибка конфигурации одного слоя компенсируется другим.
Главная ошибка: дать CC обычного пользователя БД «для удобства». Это нарушение принципа наименьших привилегий — при ошибке в промпте или prompt injection Claude может изменить или удалить данные. Read-only роль — обязательный минимум, не опция.
Настройка доступа — PostgreSQL и MySQL
🐘 PostgreSQL — read-only роль
Шаг 1: создать роль с правом только на чтение (выполнить от суперпользователя):
-- Выполнить от суперпользователя:CREATE ROLE claude_readonly WITH LOGIN PASSWORD 'STRONG_PASSWORD_HERE';
-- Дать доступ к схемеGRANT CONNECT ON DATABASE my_database TO claude_readonly;
GRANT USAGE ON SCHEMA public TO claude_readonly;
-- Только SELECT на все существующие таблицыGRANT SELECT ON ALL TABLES IN SCHEMA public TO claude_readonly;
-- И на будущие таблицыALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT ON TABLES TO claude_readonly;
Шаг 2: подключить postgres-mcp в .mcp.json проекта:
// project/.mcp.json
{
"mcpServers": {
"postgres-pro": {
"command": "uvx",
"args": [
"--from", "postgres-mcp",
"postgres-mcp",
"--access-mode=restricted"// ← Только SELECT, блок DDL на уровне MCP
],
"env": {
"DATABASE_URI": "postgresql://claude_readonly:PASS@host.docker.internal:5432/my_db"
}
}
}
}
Двойная защита: роль claude_readonly (уровень PostgreSQL) + --access-mode=restricted (уровень MCP). Даже если Claude попытается выполнить UPDATE — PostgreSQL откажет.
Настройки PostgreSQL против OOM при ETL
При ETL-операциях с большими объёмами данных PostgreSQL потреблял всю память WSL2 и убивался OOM. Зафиксированные параметры:
-- Применить без рестарта (кроме shared_buffers):ALTER SYSTEM SET shared_buffers = '4GB'; -- требует рестарта PGALTER SYSTEM SET work_mem = '64MB';
ALTER SYSTEM SET max_parallel_workers_per_gather = '1';
SELECT pg_reload_conf(); -- применить без рестарта
Параметр
Значение
Причина
shared_buffers
4GB
Не более 25% от WSL2 лимита (38GB). 16GB → OOM
work_mem
64MB
Множится на N workers × N processes. Большой = OOM
max_parallel_workers_per_gather
1
При ETL каждый воркер = отдельный work_mem блок
ETL режим: перед массовым INSERT/UPDATE установить max_parallel_workers_per_gather=1, после ETL вернуть. Иначе OOM при параллельных операциях.
🐬 MySQL / MariaDB — read-only пользователь
Для MySQL/MariaDB — аналогичный подход с read-only пользователем:
-- Создать read-only пользователя MySQL:CREATE USER'claude_ro'@'%'IDENTIFIED BY'STRONG_PASSWORD';
GRANT SELECT, SHOW VIEW ON my_database.* TO'claude_ro'@'%';
FLUSH PRIVILEGES;
Команды-убийцы данных блокируются hooks и deny-листом: migrate:fresh, db:wipe, migrate:reset, alembic downgrade base, TRUNCATE. Для отката используйте один шаг назад (alembic downgrade -1), не «в начало».
Типичные запросы через postgres-mcp
Анализ структуры таблицы
-- Через mcp__postgres-pro__query:-- Структура таблицыSELECT
column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_name = 'payments'ORDER BY ordinal_position;
-- Индексы таблицыSELECT
indexname, indexdef
FROM pg_indexes
WHERE tablename = 'payments';
-- Медленные запросы (pg_stat_statements)SELECT
query, calls, mean_exec_time, total_exec_time
FROM pg_stat_statements
ORDER BY total_exec_time DESCLIMIT 10;
EXPLAIN для анализа производительности
-- Анализ плана запроса (только читает, не меняет):EXPLAIN ANALYZE SELECT *
FROM payments p
JOIN users u ON p.user_id = u.id
WHERE p.status = 'pending'AND p.created_at > NOW() - INTERVAL'7 days';
-- Это безопасно! EXPLAIN не модифицирует данные
ETL — правила безопасного импорта
При ETL-пайплайнах Claude Code помогает писать код, но сами операции выполняются в контейнере через MCP:
# Python ETL — правила из CLAUDE.md:# 1. Тестовая БД — всегда SQLite in-memory:from sqlalchemy import create_engine
engine = create_engine("sqlite:///:memory:") # Никогда не prod в тестах!# 2. Батчинг — не более 10K строк за транзакцию:BATCH_SIZE = 10_000
for i in range(0, len(records), BATCH_SIZE):
batch = records[i:i + BATCH_SIZE]
with session.begin():
session.bulk_insert_mappings(Model, batch)
# 3. Логирование каждого шага:
logger.info(f"ETL step=extract start")
# ... логика ...
logger.info(f"ETL step=extract end count={len(records)}")
# 4. Rollback при ошибке:except Exception as e:
session.rollback()
etl_errors_table.insert(error=str(e), step="transform")
💾 Резервное копирование перед рискованными операциями
Правило бэкапа: pg_dump должен выполняться разработчиком вручную, не Claude. Hooks блокируют pg_dump из bash. Если нужен дамп — выполняй ! docker exec ... напрямую в терминале вне Claude Code.
Плюсы и минусы подхода
Плюсы
Данные защищены: запись физически невозможна
CC видит реальную схему — точные ответы
Оптимизация запросов на живых данных
Prompt injection не приведёт к потере данных
Человек контролирует все изменения схемы
Минусы и нюансы
Нужна разовая настройка роли и MCP
Миграции запускаете сами — не «полный автопилот»
Connection string с паролем — хранить в env, не в репо
ETL и массовые операции требуют тюнинга памяти
Типовые ошибки
Дать CC пользователя с правами на запись
«Для удобства» — и одна ошибка стирает данные. Всегда отдельная роль claude_readonly только с SELECT.
Закоммитить .mcp.json с паролем БД
.mcp.json содержит DATABASE_URI с паролем. Добавьте в .gitignore, используйте ${ENV_VAR} вместо хардкода.
Разрешить migrate:fresh «чтобы быстрее»
Эта команда удаляет ВСЕ данные. Должна быть в deny-листе. Для тестов — отдельная тестовая БД, не продовая.
Запустить миграцию не прочитав файл
CC мог сгенерировать неверный down() или лишний DROP. Всегда читайте миграцию перед запуском, особенно на проде.
Большой work_mem при ETL
Память множится на число воркеров → OOM. При массовых операциях: work_mem=64MB, max_parallel_workers_per_gather=1, батчинг по 10K строк.
Тесты на продовой БД
ETL и тесты — только на отдельной БД (SQLite in-memory или тестовый контейнер). Никогда не направляйте тесты на prod.
Частые вопросы
Технически да, но это опасно и не рекомендуется. Любая ошибка в промпте или prompt injection приведёт к изменению данных. Правильный паттерн: CC генерирует SQL/миграцию, человек проверяет и запускает. Контроль остаётся за вами.
Создайте роль claude_readonly с GRANT SELECT (без INSERT/UPDATE/DELETE/DDL), подключите её в .mcp.json через postgres-mcp с флагом --access-mode=restricted. Это даёт два слоя защиты.
Да, подход аналогичен: создайте пользователя 'claude_ro' с GRANT SELECT, SHOW VIEW, подключите MySQL MCP-сервер с флагами ALLOW_INSERT/UPDATE/DELETE_OPERATION=false. Принцип read-only тот же.
PostgreSQL потребляет всю память WSL2. Решение: shared_buffers ≤ 25% от лимита WSL2, work_mem=64MB, max_parallel_workers_per_gather=1, батчинг данных по 10K строк за транзакцию. Подробности — на странице DevOps.
Только вы, вручную. pg_dump заблокирован для CC через hooks. Перед любой рискованной операцией на проде делайте дамп в терминале вне Claude Code. Это ваша страховка.
Нет, при правильной настройке. Read-only роль откажет в записи на уровне СУБД, а MCP в restricted-режиме блокирует DDL до запроса. Даже прямая просьба «удали строку» вернёт ошибку прав.