Skip to main content
Glama
MP-Trade

clickhouse-readonly-mcp

by MP-Trade
README.md
# clickhouse-readonly-mcp

Минимальный MCP-сервер для read-only доступа к ClickHouse: даёт AI-агенту
(Claude Code, Cursor и т.п.) возможность смотреть схему баз/таблиц/вью
и делать SELECT-запросы напрямую — без ручных экспортов CSV из DataLens.

Написан по образцу [`azure-sql-mcp`](../azure-sql-mcp). Весь код
(`server.py`) прозрачен и лежит в этом репозитории. Сторонних MCP-пакетов нет.

## Почему это безопасно (read-only в двух независимых местах)

1. **На уровне базы**: сервер подключается под отдельным пользователем
   ClickHouse, у которого DBA выдал только `SELECT` на нужные таблицы/вью
   (`GRANT SELECT ON database.table TO readonly_user`). Никакого
   `INSERT / UPDATE / DROP / ALTER`.

2. **В коде**: `ch_run_select` до отправки запроса проверяет, что это ровно
   один `SELECT` (в т.ч. `WITH … SELECT`), без `INSERT / DELETE / DROP /
   ALTER / CREATE / SYSTEM / TRUNCATE` и без `;` внутри (чтобы нельзя было
   дописать второй оператор). Плюс сессия открывается с `readonly=1` и
   `max_execution_time=30` — даже если проверка ошибётся, ClickHouse не даст
   ничего записать.

Даже если код-проверка №2 где-то ошибётся — база физически не даст ничего,
кроме `SELECT`, потому что у пользователя нет прав на запись.

## Установка

Требуется Python 3.10+.

```bash
cd clickhouse-readonly-mcp
pip install -r requirements.txt
cp .env.example .env        # Windows: copy .env.example .env
```

Открой `.env` и заполни:

```env
CH_HOST=clickhouse.mptrd.ru
CH_PORT=443
CH_DATABASE=default_marts
CH_USER=your_readonly_user
CH_PASSWORD=your_password
CH_SECURE=true      # true = HTTPS/TLS (у нас порт 443, не 8443); false = HTTP (порт 8123)
CH_VERIFY=true      # false — если self-signed сертификат
```

`.env` не попадает в git (см. `.gitignore`) — пароль остаётся только на
локальной машине.

## Подключение к Claude Code / Cursor

В конфиге MCP-серверов (`~/.cursor/mcp.json` или аналогичный для Claude Code):

```json
{
  "mcpServers": {
    "clickhouse-readonly": {
      "command": "python",
      "args": ["ПОЛНЫЙ_ПУТЬ/clickhouse-readonly-mcp/server.py"]
    }
  }
}
```

После добавления нужен полный перезапуск клиента.

## Доступные инструменты

| Инструмент | Что делает |
|---|---|
| `ch_connection_status` | Проверить подключение, вернуть пользователя / версию CH |
| `ch_list_databases` | Список баз данных, видимых текущему пользователю |
| `ch_list_tables` | Таблицы и вью базы: имя, движок, примерное кол-во строк, размер |
| `ch_describe_table` | Колонки таблицы/вью: имя, тип, default, комментарий |
| `ch_table_sample` | Быстрый `SELECT * LIMIT N` — посмотреть форму данных |
| `ch_run_select` | Произвольный read-only `SELECT` (с лимитом строк и защитой от записи) |

## Что попросить у DBA (пример)

```sql
-- Создать пользователя
CREATE USER agent_readonly IDENTIFIED BY 'strong_password_here';

-- Выдать SELECT только на нужные вью в Marts
GRANT SELECT ON default_marts.bv_ozon_product_queries_details TO agent_readonly;
GRANT SELECT ON default_marts.bv_gg_turnover_daily             TO agent_readonly;
GRANT SELECT ON default_marts.bv_gg_turnover_summary           TO agent_readonly;
GRANT SELECT ON default_marts.bv_ozon_orders_by_clusters       TO agent_readonly;
GRANT SELECT ON default_marts.bv_ozon_orders_stock             TO agent_readonly;

-- Проверка (запустить от имени agent_readonly)
SELECT * FROM default_marts.bv_ozon_product_queries_details LIMIT 1;
```

## Диагностика

- **`Connection refused` / `timeout`** — проверь `CH_HOST`, `CH_PORT` и что
  firewall разрешает входящие соединения с твоего IP. Частая ошибка онбординга:
  порт `8443` (типичный для ClickHouse HTTPS) у MP-Trade **не слушается** —
  нужен `CH_PORT=443` при `CH_SECURE=true`.
- **`TLS handshake failed`** — попробуй `CH_VERIFY=false` (если self-signed
  сертификат), или убедись что `CH_SECURE=true` и порт `443`.
- **`ch_run_select` отклоняет обычный SELECT** — скорее всего, в тексте
  запроса встретилось запрещённое слово как часть идентификатора
  (например, алиас `into_basket`). Правь `_FORBIDDEN_KEYWORDS` в `server.py`
  осознанно, с пониманием, а не в обход.
- **Список инструментов не обновился после правки `server.py`** — нужен
  полный перезапуск клиента (не только чата).

## Зависимости

| Библиотека | Для чего |
|---|---|
| `fastmcp` | Реализация протокола MCP (тот же выбор, что в `azure-sql-mcp`) |
| `clickhouse-connect` | Официальный Python-клиент ClickHouse Inc., поддерживает HTTP/HTTPS |
| `python-dotenv` | Загрузка `.env` без хардкода пароля в коде |