datalake mcp · mcp-data-lake.first-bit.ru

Вся база компании.
Одним вопросом.

MCP-сервер к внутреннему озеру данных по российским бизнес-субъектам: 1,16 млн компаний, 48 тыс. сделок, 3 млн строк финотчётности. Сотрудник спрашивает на естественном языке — агент сам формирует запрос, ходит в PostgreSQL и возвращает ответ.

01Данные

Одно озеро.
Три контура. Двенадцать витрин.

Всё живёт в PostgreSQL 15 и отдаётся агенту через представления схемы mcp. Базовых таблиц для агента не существует. Объёмы на полосах — квадратичная шкала, иначе сделки стали бы волосяными.

3,0 млнстрок финотчётности ФНС
3 064 767
1,16 млнсубъектов, ЕГРЮЛ и ЕГРИП
1 156 766
139 тыс.сайтов и их стек
139 926
48 тыс.сделок, 2010–2025
47 972
32 тыс.клиентов в учётной системе
32 156

Плюс 750 тыс. email и 559 тыс. телефонов в контактном контуре — они не на этой полосе, потому что дальше в раздел о безопасности. 43% субъектов — ИП, ответ только по юрлицам теряет их молча.

02Контуры

и при этом

разграничение — по данным,
а не по людям.

Права у всех сотрудников одинаковые. Контур определяет, что за данные, а не кто спрашивает. Роль свободного SQL не видит контактные представления даже в information_schema. Холодная база — для поиска лидов. Клиентский контур — для продаж. Контактный — только поштучно, по одному ИНН за вызов.
Лимит выдачи1 000
строк
  1. 01Холодный контур — публичные реестры ЕГРЮЛ и ЕГРИП1 124 610 субъектов · свободный SQL
  2. 02Клиентский — продажи и привязка к ответственному32 156 клиентов · свободный SQL
  3. 03Контактный — телефоны, email, ФИО, адреса физлицтолько по одному ИНН · роль mcp_contacts
03Как работает

Семь шагов
от вопроса до ответа.

Агент не имеет прямого доступа к базе: он вызывает инструменты сервера, те выполняют запросы под read-only ролями. Каждый запрос привязан к сотруднику.

  1. 01Сотрудник задаёт вопрос на русском«топ клиентов за 2025»
  2. 02MCP-клиент — Claude Code, Codex, OpenCode, Cursorлюбой MCP-клиент
  3. 03OAuth 2.1 + PKCE через ZitadelJWT привязан к сотруднику
  4. 04MCP Toolbox выбирает инструментиз tools.yaml — единственного источника правды
  5. 05Роль mcp_agent или mcp_contactsвитрины без ПДн · контакты по одному ИНН
  6. 06PostgreSQL 15, схема mcpтолько представления · только чтение
  7. 07Агент показывает SQL и ответи каждый запрос виден в аудите

Если агент не понял вопрос, запрос будет валидным, а число — неверным. Поэтому инструкция требует показывать SQL и называть трактовку там, где от неё зависит число: «компания» — юрлицо, ИП или оба; «выручка» — наши продажи или выручка контрагента по ФНС.

04Инструменты

Четырнадцать инструментов.
Один свободный SQL и справочник, который не врёт.

Готовые отчёты нужны там, где ручной SQL легко даёт правдоподобный, но неверный результат: привязка заказа к юрлицу и к ИП считается в обеих ветвях сразу, группы товаров берутся из справочника, а не из текстового поля выгрузки.

14инструментовtools.yaml
12витрин схемы mcpтолько представления
8готовых отчётовдля развилок семантики
query_marts --sql "SELECT …"

Свободный SQL.

Один SELECT по представлениям схемы mcp. Основной рабочий инструмент — агент пишет запрос сам. Только чтение, лимит 1000 строк.

describe_data --view_name v_order

Справочник из самой базы.

Витрины, колонки с типами, пояснения к ловушкам — amount против sum, type_activity — это не ОКВЭД — и перечень того, чего в базе нет.

find_subject_by_name --name "…"

Точка входа по названию.

Остальные инструменты принимают только ИНН. Сначала поиск по названию, потом всё остальное.

get_contacts_by_inn

Контакты — строго поштучно.

Телефон, email, ФИО и адреса — по одному ИНН за вызов, под отдельной ролью. Массовая выгрузка невозможна архитектурно.

top_clients_by_revenue · find_leads

Готовые отчёты.

Топ покупателей, профиль контрагента, продажи по периодам и группам товаров, поиск лидов по холодной базе, финотчётность по ИНН, клиенты без сделок, отчёт о качестве данных.

column "registration_date" does not exist

Отказ вместо выдумки.

Полей, которых нет в базе, в представлениях нет намеренно: похожих колонок не существует, подставить нечего. Запрос по ним даёт ошибку, а не пустую выборку.

05Безопасность

Граница — права роли,
а не парсер приложения.

Read-only выставлен на самой роли Postgres, а не в коде: обойти настройку роли нельзя. Контакты доступны только поштучно: чтобы выгрузить 750 тысяч контактов, нужно 750 тысяч вызовов — и это видно в логах на первой сотне.

38попыток инъекции и эскалацииsecurity_probe.py, в CI
0успешныхвсё отклонено ролью
100%запросов под аудитомсотрудник в application_name
ПопыткаРезультат
SELECT * FROM public.emailspermission denied for table emails
SELECT * FROM mcp.v_contact из свободного SQLpermission denied for view v_contact
CREATE TABLE …cannot execute in a read-only transaction
SELECT 1; CREATE TABLE …cannot insert multiple commands
Запрос без токена · с чужим токеном401
SET ROLE master_chiefpermission denied to set role
pg_read_file('/etc/passwd')permission denied for function
COPY … TO PROGRAM 'id'требует pg_execute_server_program
SELECT * FROM pg_authidpermission denied for table pg_authid
CREATE EXTENSION dblinkpermission denied to create extension
ALTER SYSTEM SET …permission denied to set parameter

Модель внешняя: каждая отданная строка покидает контур компании. Это осознанное решение, отсюда лимиты на выдачу и поштучный доступ к контактам. Сервер не должен быть доступен из интернета: только корпоративная сеть или VPN.

06Выгрузка

Большой список — это файл,
а не контекст модели.

Предел в 1000 строк — не про безопасность, а про физику: строка из десяти колонок стоит 30–60 токенов, тысяча строк съедает почти всё окно модели. Плюс модель внешняя. Поэтому выгрузка идёт мимо модели: SQL не дублируется — скрипт берёт запрос из того же tools.yaml.

$ python mcp/export.py --tool find_leads \
    --param region=77 --param min_employees=100 \
    --param require_site=true --param row_limit=20000

Готово за 3.0 с
  файл:    outputs/leads.csv (1.2 MB)
  строк:   4100
  колонок: 10 — subject_type, inn, name, region, …
в контекст модели попадает только эта сводка
  1. 01
    Вопрос на самом деле про агрегат. «Каких клиентов больше всего» — это count, sum, топ-N. Ответ в двадцать строк.
  2. 02
    Надо посмотреть, что там. Сначала count(*) с тем же WHERE, потом решение. Для «посмотреть глазами» хватает двадцати строк.
  3. 03
    Нужен весь список — это выгрузка. CSV и XLSX до миллиона строк, серверный курсор порциями по 5000, таймаут 120 с. Контакты недоступны и здесь.
07Подключение

Одна команда.
Потом спросить по-русски.

claude mcp add --transport http datalake \
    https://mcp-data-lake.first-bit.ru/mcp \
    --client-id 389975564900892674 -s user

Вход — рабочей учётной записью, той же, что в GitLab и вики. После подключения перезапустить клиент: инструменты читаются при старте сессии. Подробно про каждый клиент — в CONNECT.md.

«что у нас по "Планета Софт"»
профиль контрагента одним вопросом
«топ-20 клиентов по выручке за 2025»
готовый отчёт, обе ветви привязки
«что известно про ИНН 2460049048»
реквизиты, продажи, сайты, лицензии, финотчётность
«найди не-клиентов с сайтом на Bitrix»
поиск лидов по холодной базе, 1,1 млн субъектов
«покажи SQL»
агент обязан показывать; так ловится подмена смысла
«а чего в базе нет?»
справочник перечислит: дат регистрации, ОКВЭД, руководителей