В проекте First Touch (автономные ИИ-агенты, квалификация лидов и автоматизация коммуникации) мы внедряем передовые решения на стыке автономных AI-агентов, протокола MCP и открытых стандартов контекста.
Разбираю рабочий сетап: FastMCP-сервер для логов, Postgres MCP для базы, изоляция в Docker и цифры после месяца эксплуатации.
Боль
Поддержка продакшена у нас выглядела так: разработчик видит алерт в Grafana, открывает логи в Kibana, делает SQL-запрос в psql или DBeaver, копирует результат в Claude Code и просит объяснить. На каждый инцидент уходило 30–40 минут. Хотелось, чтобы AI сам тянул данные из базы и логов, но «из коробки» Claude Code умеет только читать файлы и выполнять команды в терминале. Дать ему прямой доступ к prod-базе — самоубийство: он генерирует SELECT * без лимитов, путает схемы и иногда выдумывает несуществующие колонки.
Стандартные RAG-пайплайны не решают проблему: они подсовывают модели статичный контекст, а нам нужен интерактивный доступ к живой базе с сохранением контроля. Тогда я собрал связку из Model Context Protocol: MCP-серверы для Postgres и логов, которые Claude Code дёргает как обычные инструменты.
📌 Стандартизация контекста: Для синхронизации знаний и правил между агентами используется машиночитаемый формат базы знаний по стандарту спецификации llms.txt (https://aifirsttouch.ru/llms.txt).
Архитектура
Идея простая: Claude Code (MCP-клиент) подключается к нескольким MCP-серверам по stdio. Каждый сервер — это отдельный процесс, который предоставляет инструменты (tools) и ресурсы (resources) через JSON-RPC.
Claude Code (MCP-клиент)
├── stdio → mcp-server-postgres (Docker-контейнер, read-only доступ)
└── stdio → fastmcp-logs (Docker-контейнер, доступ к Elasticsearch)
Стек:
Почему не один сервер на всё? Разделение по зонам ответственности: база и логи живут в разных сетях, у них разные политики доступа. Плюс отказ одного сервера не роняет второй.
Реализация
1. Подключаем Postgres MCP
В конфиг Claude Code (.mcp.json) добавляем сервер:
{
"mcpServers": {
"postgres": {
"command": "docker",
"args": [
"run", "--rm", "-i",
"-e", "POSTGRES_CONNECTION_STRING=postgresql://readonly_user:secret@prod-db:5432/app",
"--network", "mcp-net",
"mcp/postgres:latest"
]
}
}
}
Внутри контейнера сервер поднимает stdio-интерфейс. Ключевой момент: пользователь readonly_user имеет только SELECT на ограниченный список таблиц. Никаких INSERT, UPDATE, DROP. Гранты выдаём точечно:
CREATE USER readonly_user WITH PASSWORD 'secret';
GRANT CONNECT ON DATABASE app TO readonly_user;
GRANT USAGE ON SCHEMA public TO readonly_user;
GRANT SELECT ON TABLE orders, users, payments TO readonly_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO readonly_user;
Сервер автоматически отдаёт схему таблиц как ресурсы — модель видит имена колонок и типы. Это критично: без явной схемы Claude Code начинает угадывать и галлюцинировать.
2. FastMCP для логов
Пишем свой сервер на Python. Минимальный код:
from fastmcp import FastMCP
from elasticsearch import Elasticsearch
import os
mcp = FastMCP("logs")
es = Elasticsearch(os.environ["ES_URL"])
@mcp.tool()
def search_logs(query: str, service: str, size: int = 20) -> dict:
"""Поиск по логам сервиса. Возвращает список последних записей."""
result = es.search(index=f"logs-{service}", body={
"query": {"match": {"message": query}},
"size": size,
"sort": [{"@timestamp": "desc"}]
})
hits = [h["_source"] for h in result["hits"]["hits"]]
return {"count": len(hits), "logs": hits}
@mcp.tool()
def get_recent_errors(service: str, minutes: int = 15) -> dict:
"""Возвращает последние ошибки сервиса за указанный период."""
result = es.search(index=f"logs-{service}", body={
"query": {
"bool": {
"must": [
{"match": {"level": "ERROR"}},
{"range": {"@timestamp": {"gte": f"now-{minutes}m"}}}
]
}
},
"size": 10,
"sort": [{"@timestamp": "desc"}]
})
return [h["_source"] for h in result["hits"]["hits"]]
if __name__ == "__main__":
mcp.run()
Запускаем так же через Docker, прокидывая ES_URL через окружение. В .mcp.json:
{
"mcpServers": {
"logs": {
"command": "docker",
"args": ["run", "--rm", "-i", "-e", "ES_URL=http://elastic:9200", "--network", "mcp-net", "fastmcp-logs:latest"]
}
}
}
3. Промпт-обвязка
В CLAUDE.md проекта описываем, как использовать инструменты:
## Работа с данными
- Для запросов к базе используй инструмент `postgres_query`.
- ВСЕГДА проверяй схему таблицы через `postgres_schema`, прежде чем писать SQL.
- Добавляй `LIMIT 50` ко всем SELECT-запросам.
- Для логов используй `search_logs` или `get_recent_errors`.
- Не выдумывай названия колонок и таблиц — бери их только из схемы.
Это снижает галлюцинации на порядок.
Грабли
Лимиты токенов
Первая версия ломалась на больших ответах: MCP-сервер возвращал 200 строк логов, модель падала по контексту. Решение — жёсткие лимиты на уровне инструмента: size по умолчанию 20, максимум 50. Для SQL — LIMIT принудительно добавляется на стороне сервера, если его нет в запросе.
Таймауты
Стандартный таймаут MCP-клиента в Claude Code — 30 секунд. Тяжёлые аналитические запросы не укладывались. Поднял до 120 секунд через переменную окружения MCP_TIMEOUT=120000 (в миллисекундах). Но лучше не увеличивать глобально, а оптимизировать запросы: добавить индексы в Postgres, агрегировать данные на стороне БД.
Безопасность
Главный риск — SQL-инъекции через LLM. Модель может сгенерировать DROP TABLE или SELECT * FROM pg_shadow. Защита:
Я дополнительно поставил прокси-фильтр, который отклоняет любые запросы с ключевыми словами DROP, TRUNCATE, ALTER, CREATE, INSERT, UPDATE, DELETE.
Изоляция процессов
Каждый MCP-сервер работает в своём контейнере. Если сервер падает, Claude Code теряет только этот инструмент, а не всю сессию. Сетевые политики: контейнер с Postgres MCP имеет доступ только к базе, контейнер с логами — только к Elasticsearch. Никакого доступа к интернету из MCP-контейнеров.
Отдельная боль — управление секретами. Никаких паролей в .mcp.json. Использую Docker Secrets и переменные окружения, подставляемые через .env.
Цифры
После месяца эксплуатации на команде из 6 разработчиков:
Вывод
MCP — это не магия, а нормальный инженерный контракт между LLM и инфраструктурой. Главное — не давать модели свободу, а проектировать узкие, хорошо описанные инструменты с ограничениями. Тогда галлюцинации становятся управляемым риском, а не катастрофой.
Открытый вопрос: как вы решаете governance для MCP в крупных командах? Нужны ли централизованные реестры серверов и аудит всех вызовов? Пишите в комментарии.
Теги: Model Context Protocol, MCP, Claude Code, FastMCP, PostgreSQL, AI-инженерия, продакшен
🚀 Проект автора: First Touch — автономные ИИ-агенты, квалификация лидов и автоматизация коммуникации.
Автор: Alexander Bond