Продвинутый · Инфраструктура

Базы данных

Как Claude Code безопасно работает с PostgreSQL и MySQL через MCP. Главный принцип — только чтение. Все изменения данных и схемы остаются за человеком.

Read-only по умолчанию MCP вместо shell 2 слоя защиты

Главный принцип: 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'; -- требует рестарта PG ALTER SYSTEM SET work_mem = '64MB'; ALTER SYSTEM SET max_parallel_workers_per_gather = '1'; SELECT pg_reload_conf(); -- применить без рестарта
ПараметрЗначениеПричина
shared_buffers4GBНе более 25% от WSL2 лимита (38GB). 16GB → OOM
work_mem64MBМножится на N workers × N processes. Большой = OOM
max_parallel_workers_per_gather1При 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;
// .mcp.json для MySQL: { "mcpServers": { "mysql": { "command": "npx", "args": ["-y", "@benborla29/mcp-server-mysql"], "env": { "MYSQL_HOST": "host.docker.internal", "MYSQL_PORT": "3306", "MYSQL_USER": "claude_ro", "MYSQL_PASSWORD": "STRONG_PASSWORD", "MYSQL_DATABASE": "my_database", "ALLOW_INSERT_OPERATION": "false", "ALLOW_UPDATE_OPERATION": "false", "ALLOW_DELETE_OPERATION": "false" } } } }

Чем Claude реально полезен с БД

Анализ схемы
Объясняет структуру таблиц, связи, индексы по information_schema
Оптимизация запросов
EXPLAIN ANALYZE → находит отсутствующие индексы, предлагает переписать
Поиск боттлнеков
Топ медленных запросов через pg_stat_statements
Генерация миграций
Пишет migration-файл — вы проверяете и запускаете сами
Объяснение данных
Читает выборку и описывает что в таблице, находит аномалии
Отладка запросов
Помогает понять почему запрос возвращает не то, что ожидалось

Миграции: кто что делает

Миграция меняет структуру БД — это всегда зона ответственности человека. Claude помогает, но не запускает:

1
Claude генерирует migration-файл CC
По вашему описанию создаёт файл миграции (Laravel database/migrations/... или Alembic versions/...).
2
Вы проверяете файл вручную Вы
Читаете что именно меняется. Особенно — down() для отката. Никогда не запускайте миграцию, не прочитав её.
3
Делаете бэкап перед prod Вы
pg_dump перед миграцией на проде — вручную, в терминале вне CC. Это страховка на случай ошибки.
4
Запускаете миграцию Вы
php artisan migrate или alembic upgrade head. Запрещены: migrate:fresh, db:wipe, downgrade base — они уничтожают данные.

Запуск через docker MCP

// Laravel migrate через docker MCP (запускает разработчик): mcp__docker__docker_exec({ container: "backend-1", command: "php artisan migrate" }) // Alembic (Python): mcp__docker__docker_exec({ container: "python-etl-app-1", command: "alembic upgrade head" }) // ❌ ЗАПРЕЩЕНО (hooks заблокируют): php artisan migrate:fresh // Удалит ВСЕ данные! php artisan db:wipe // Уничтожит БД! php artisan migrate:reset // Откатит все миграции!
Команды-убийцы данных блокируются 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 DESC LIMIT 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")

💾 Резервное копирование перед рискованными операциями

# PostgreSQL дамп ПЕРЕД миграцией (вручную!): mcp__docker__docker_exec({ container: "my-project-db-1", command: "pg_dump -U postgres my_db > /tmp/backup_before_migration.sql" }) # Полный Caddy backup (включает сертификаты): # proxy backup
Правило бэкапа: pg_dump должен выполняться разработчиком вручную, не Claude. Hooks блокируют pg_dump из bash. Если нужен дамп — выполняй ! docker exec ... напрямую в терминале вне Claude Code.

Плюсы и минусы подхода

Плюсы
  • Данные защищены: запись физически невозможна
  • CC видит реальную схему — точные ответы
  • Оптимизация запросов на живых данных
  • Prompt injection не приведёт к потере данных
  • Человек контролирует все изменения схемы
Минусы и нюансы
  • Нужна разовая настройка роли и MCP
  • Миграции запускаете сами — не «полный автопилот»
  • Connection string с паролем — хранить в env, не в репо
  • ETL и массовые операции требуют тюнинга памяти

Типовые ошибки

Частые вопросы

Технически да, но это опасно и не рекомендуется. Любая ошибка в промпте или 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 до запроса. Даже прямая просьба «удали строку» вернёт ошибку прав.