Радик ddaf277703 Add heuristic warnings to plan summary
Detects 7 common GP plan anti-patterns (broadcast of large table,
redistribute of large table, hot node, nested loop with large outer,
large sort, spill to disk, stale row estimates, planning-heavy queries)
and surfaces them as summary.warnings[] with severity + evidence.

Raw plan and raw summary metrics are preserved so the agent can verify
each warning against the underlying numbers.
2026-05-31 14:17:52 +03:00
2026-05-31 14:06:21 +03:00
2026-05-31 14:06:21 +03:00
2026-05-31 14:06:21 +03:00

gp-mcp

MCP-сервер для оценки плана запросов dbt-моделей в Greenplum 6.x.

Запускается локально по stdio рядом с AI-агентом, который рефакторит легаси PL/SQL в dbt-модели. Сервер:

  1. компилирует выбранную dbt-модель (dbt compile --select <model>);
  2. подключается к Greenplum под read-only пользователем (SET default_transaction_read_only = on, statement_timeout);
  3. выполняет EXPLAIN (ANALYZE, VERBOSE, FORMAT JSON);
  4. возвращает JSON-план + краткую сводку с GP-метриками (motion-узлы, самый медленный узел, ошибка оценки строк).

Tools

Tool Параметры Что делает
explain_sql sql: str, statement_timeout_ms?: int EXPLAIN ANALYZE для произвольного SQL
explain_dbt_model model_name: str, statement_timeout_ms?: int dbt compile + EXPLAIN ANALYZE для модели

Возвращаемый JSON:

{
  "summary": {
    "total_cost": 12345.6,
    "plan_rows": 100000,
    "actual_rows": 98412,
    "execution_time_ms": 842.3,
    "planning_time_ms": 12.1,
    "slowest_node": { "node_type": "Seq Scan", "actual_total_time_ms": 700.2, "...": "..." },
    "motion_nodes": [{ "node_type": "Redistribute Motion", "...": "..." }],
    "rows_misestimation_factor": 1.02,
    "warnings": [
      {
        "code": "broadcast_large_table",
        "severity": "critical",
        "message": "Broadcast Motion ships 5,400,000 rows to every segment ...",
        "evidence": { "node_type": "Broadcast Motion", "actual_rows": 5400000, "...": "..." }
      }
    ]
  },
  "plan": [ /* raw EXPLAIN JSON  пруф для каждого warning */ ],
  "statement_timeout_ms": 300000,
  "compiled_sql": "select ...",
  "model_name": "fct_orders"
}

Warnings

summary.warnings[] — это эвристический разбор плана для агента: какие узлы выглядят неоптимально и что с этим делать. Сырые числа из плана остаются в summary.* и plan[] — каждый warning несёт evidence со ссылкой на конкретные значения, чтобы агент мог перепроверить рекомендацию, а не доверять ей слепо.

code severity Триггер
rows_misestimation warning plan vs actual rows расходятся в > 10× — статистика устарела
broadcast_large_table critical Broadcast Motion гонит > 1M строк на все сегменты
redistribute_large_table warning Redistribute Motion тасует > 10M строк — проверить distribution key
hot_node warning Один узел занимает > 60% execution_time_ms
nested_loop_large critical Nested Loop с > 10 000 итераций
large_sort warning Sort по > 10M строк
spill_to_disk warning Узел льётся на диск (Disk Usage > 0, Sort Method: external merge Disk и т.п.)
planning_heavy info planning_time_ms / execution_time_ms > 20%

Пороги — константы в верхней части src/gp_mcp/explain.py (ROWS_MISESTIMATION_FACTOR, BROADCAST_LARGE_ROWS, …), калибруются под кластер.

Установка

cd /Users/admin/Projects/vpn
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

Конфигурация

Все настройки — через переменные окружения. Скопируй .env.example в .env и заполни.

Переменная Обязательная Назначение
GP_HOST + Хост Greenplum master
GP_PORT + Порт
GP_USER + Read-only пользователь (см. ниже)
GP_PASSWORD + Пароль
GP_DATABASE + Имя БД
GP_SCHEMA search_path, можно через запятую
DBT_PROJECT_DIR + Каталог dbt-проекта (содержит dbt_project.yml)
DBT_PROFILES_DIR + Каталог с profiles.yml
DBT_TARGET + Имя target из profiles.yml (напр. dev)
DBT_EXECUTABLE Путь к dbt, по умолчанию dbt из PATH
STATEMENT_TIMEOUT_MS + Дефолтный statement_timeout для EXPLAIN ANALYZE
MAX_STATEMENT_TIMEOUT_MS + Верхняя граница, агент не сможет превысить
LOG_LEVEL DEBUG/INFO/WARNING/ERROR, дефолт INFO

Если обязательная переменная не задана — сервер не стартует и пишет в stderr имя недостающей переменной.

Read-only роль в Greenplum

Сервер требует, чтобы доступ был ограничен на уровне БД. Минимум:

CREATE ROLE dbt_explain LOGIN PASSWORD '...';
GRANT CONNECT ON DATABASE <db> TO dbt_explain;
GRANT USAGE ON SCHEMA <schema> TO dbt_explain;
GRANT SELECT ON ALL TABLES IN SCHEMA <schema> TO dbt_explain;
ALTER DEFAULT PRIVILEGES IN SCHEMA <schema>
  GRANT SELECT ON TABLES TO dbt_explain;

Сервер дополнительно ставит сессионный default_transaction_read_only = on, но GRANT-ы — единственная надёжная защита.

Запуск

Локально (для отладки):

python -m gp_mcp.server

Сервер ничего не печатает в stdout (это канал MCP) — все логи идут в stderr.

Подключение к клиенту

Сервер общается по stdio, поэтому клиент должен сам его запускать. Конфиг — стандартный MCP JSON: одинаковая форма для Claude Code и Cursor, различаются только пути к файлам настроек.

Общий блок, который пригодится ниже:

{
  "command": "/Users/admin/Projects/vpn/.venv/bin/python",
  "args": ["-m", "gp_mcp.server"],
  "cwd": "/Users/admin/Projects/vpn/src",
  "env": {
    "GP_HOST": "gp-master.internal",
    "GP_PORT": "5432",
    "GP_USER": "dbt_explain",
    "GP_PASSWORD": "REPLACE_ME",
    "GP_DATABASE": "analytics",
    "GP_SCHEMA": "analytics,public",
    "DBT_PROJECT_DIR": "/Users/admin/Projects/dbt-analytics",
    "DBT_PROFILES_DIR": "/Users/admin/.dbt",
    "DBT_TARGET": "dev",
    "STATEMENT_TIMEOUT_MS": "300000",
    "MAX_STATEMENT_TIMEOUT_MS": "900000",
    "LOG_LEVEL": "INFO"
  }
}

Важно:

  • commandабсолютный путь к Python из venv проекта. Клиенты MCP обычно стартуют без активированного окружения, поэтому полагаться на python из PATH нельзя.
  • cwd указан на src/, чтобы Python нашёл пакет gp_mcp без установки (pip install -e . не делаем).
  • Секреты держим в env соответствующего конфига клиента, не в коде и не в репозитории.

Claude Code

Есть три способа добавить сервер — выбери один.

1. Через CLI (быстрее всего)

claude mcp add gp-mcp \
  --scope user \
  --env GP_HOST=gp-master.internal \
  --env GP_PORT=5432 \
  --env GP_USER=dbt_explain \
  --env GP_PASSWORD=REPLACE_ME \
  --env GP_DATABASE=analytics \
  --env DBT_PROJECT_DIR=/Users/admin/Projects/dbt-analytics \
  --env DBT_PROFILES_DIR=/Users/admin/.dbt \
  --env DBT_TARGET=dev \
  --env STATEMENT_TIMEOUT_MS=300000 \
  --env MAX_STATEMENT_TIMEOUT_MS=900000 \
  -- /Users/admin/Projects/vpn/.venv/bin/python -m gp_mcp.server

Флаг --scope:

  • user — для всех проектов (пишется в ~/.claude.json);
  • project — общий для команды, кладётся в .mcp.json в корне проекта, его можно коммитить в git (секреты тогда задают через ${VAR}-подстановку из окружения, а не хардкодом);
  • local — только в текущем проекте, только у тебя.

2. Вручную, user-scope: ~/.claude.json

{
  "mcpServers": {
    "gp-mcp": { /* см. общий блок выше */ }
  }
}

3. Вручную, project-scope: .mcp.json в корне dbt-репозитория

{
  "mcpServers": {
    "gp-mcp": {
      "command": "/Users/admin/Projects/vpn/.venv/bin/python",
      "args": ["-m", "gp_mcp.server"],
      "cwd": "/Users/admin/Projects/vpn/src",
      "env": {
        "GP_HOST": "${GP_HOST}",
        "GP_PORT": "${GP_PORT}",
        "GP_USER": "${GP_USER}",
        "GP_PASSWORD": "${GP_PASSWORD}",
        "GP_DATABASE": "${GP_DATABASE}",
        "DBT_PROJECT_DIR": "${DBT_PROJECT_DIR}",
        "DBT_PROFILES_DIR": "${DBT_PROFILES_DIR}",
        "DBT_TARGET": "${DBT_TARGET}",
        "STATEMENT_TIMEOUT_MS": "300000",
        "MAX_STATEMENT_TIMEOUT_MS": "900000"
      }
    }
  }
}

Проверка:

claude mcp list             # gp-mcp должен быть в списке
claude mcp get gp-mcp       # детали конфига

В сессии /mcp покажет статус подключения и список tool'ов. Если статус failed, посмотри ~/Library/Logs/Claude/ — сервер пишет ошибки запуска (включая отсутствующие env-переменные) в stderr.


Cursor IDE

Cursor использует тот же MCP-формат, но свой файл настроек.

1. Через UI

SettingsCursor SettingsMCP & IntegrationsNew MCP Server → откроется mcp.json для редактирования.

2. Вручную, глобально: ~/.cursor/mcp.json

Доступно во всех проектах.

{
  "mcpServers": {
    "gp-mcp": {
      "command": "/Users/admin/Projects/vpn/.venv/bin/python",
      "args": ["-m", "gp_mcp.server"],
      "cwd": "/Users/admin/Projects/vpn/src",
      "env": {
        "GP_HOST": "gp-master.internal",
        "GP_PORT": "5432",
        "GP_USER": "dbt_explain",
        "GP_PASSWORD": "REPLACE_ME",
        "GP_DATABASE": "analytics",
        "DBT_PROJECT_DIR": "/Users/admin/Projects/dbt-analytics",
        "DBT_PROFILES_DIR": "/Users/admin/.dbt",
        "DBT_TARGET": "dev",
        "STATEMENT_TIMEOUT_MS": "300000",
        "MAX_STATEMENT_TIMEOUT_MS": "900000"
      }
    }
  }
}

3. Вручную, для проекта: .cursor/mcp.json в корне dbt-репозитория

Видно только в этом проекте. Удобно, когда у разных dbt-проектов разные DBT_PROJECT_DIR/DBT_TARGET.

Проверка:

SettingsMCP & Integrations — справа от gp-mcp должен загореться зелёный индикатор и появиться список tool'ов (explain_sql, explain_dbt_model). В чате tools будут доступны Agent-режиму.

Если индикатор красный — раскрой сервер в этом же окне, там показывается stderr запуска (включая Configuration error: Required environment variable '...' is not set).


Общие проблемы при подключении

Симптом Причина
Configuration error: Required environment variable 'X' is not set Переменная X не задана в env конфига клиента
ModuleNotFoundError: No module named 'gp_mcp' Неверный cwd — должен указывать на src/, или Python не из venv
ModuleNotFoundError: No module named 'mcp' command указывает не на Python из venv, где установлены зависимости
Сервер стартует, но tools не появляются Клиент не перезапущен / нет permissions в Cursor для MCP
dbt: command not found при вызове explain_dbt_model Поставь DBT_EXECUTABLE=/абсолютный/путь/к/dbt в env

Структура

vpn/
├── .env.example
├── .gitignore
├── requirements.txt
├── README.md
└── src/
    └── gp_mcp/
        ├── __init__.py
        ├── config.py       # загрузка и валидация env
        ├── db.py           # psycopg2 + read-only + timeout
        ├── dbt_runner.py   # subprocess dbt compile + чтение compiled SQL
        ├── explain.py      # EXPLAIN ANALYZE + summary
        └── server.py       # FastMCP, регистрация tools, stdio
Description
mcp
Readme 48 KiB
Languages
Python 100%