Skip to main content
Glama
README.md
# Ариадна

Docker-контейнер с MCP-сервером для быстрого поиска по метаданным конфигураций 1С и анализа кода BSL.

AI-агенты (Claude Code, Cursor, Codex) находят нужный объект, реквизит или процедуру за один MCP-вызов вместо сканирования всей кодовой базы — экономя контекст и токены. Есть и Web UI — искать и индексировать можно и без агента, прямо в браузере.

## Возможности

- **Поиск по метаданным** — объекты, реквизиты (и реквизиты форм), типы,
  формы; по имени или своими словами («где лежат долги по аренде земельных
  долей»), формы слов совпадают — годится и для помощника по данным базы.
  Поиск процедур по описанию — `semantic_search_code`. Типичный вызов
  инструмента — доли секунды (замер: `search_procedures` на крупной типовой конфигурации, около
  550 тыс. процедур, — 0,1–0,15 с). Дольше — `diagnose_index` (читает все
  файлы выгрузки) и первый запрос к проекту после перезапуска Docker
  Desktop, пока индекс читается с диска (несколько секунд)
- **Граф вызовов BSL** — кто вызывает / что вызывает процедура, с резолвингом
  рёбер по факту (включая перехваты `&Вместо`/`&Around` из расширений)
- **Полнотекстовый поиск по коду** — FTS5 по всему тексту модулей, а не только по вызовам
- **RU + EN конфигурации** — русские и английские ключевые слова, директивы,
  менеджеры; регистронезависимый поиск, EN-алиасы видов (`Catalog` = `Справочник`)
- **Несколько проектов**, **конфигурация + расширения** (несколько source в одном проекте)
- **Инкрементальная фоновая индексация** — reindex не блокирует, повторный проход
  перечитывает только изменённые файлы (по хэшу); при изменении кода
  самого парсера индекс сам делает полный переразбор, вручную удалять
  `index.db` не нужно
- **Web UI** — управление проектами, поиск, playground для MCP tools;
  светлая и тёмная тема, шрифты и иконки локальные — интерфейс полностью
  работает без интернета
- **MCP Server** — streamable HTTP, project-scoped tools
- **Эксплуатация** — `/ready` (готовность проектов), манифест индекса,
  резервная копия и перенос — [docs/operations.md](docs/operations.md)

## Быстрый старт

Поддерживаемый способ запуска сервера — Docker. Запуск без Docker
(`python -m src.main`) — только для разработки и тестов, см.
«Разработка» и SETUP.md.

### 1. Запуск

```bash
git clone https://github.com/amorozov1310/ariadna-mcp.git
cd ariadna-mcp
docker compose up -d
```

Если образ ещё не собран, а в `docker-wheels/` нет .whl-файлов — сборка
упадёт: зависимости ставятся в образ офлайн из этого каталога (обход
нестабильной сети в docker build). Один раз скачайте колёса (нужен только
Docker, сеть используется из runtime-контейнера):

```bash
docker run --rm -v "$PWD:/repo" -w /repo python:3.12-slim sh docker-wheels/fetch.sh
```

Команда запускается из корня репозитория: список зависимостей берётся из
`pyproject.toml`. На Windows — только из PowerShell с полным путём
(`-v D:\путь\до\ariadna:/repo`): Git Bash искажает пути монтирования.
Повторять при изменении зависимостей в `pyproject.toml` — старые колёса
заменяются новыми, при сбое сети прежний набор остаётся.

Web UI: http://127.0.0.1:19878
MCP (streamable HTTP): http://127.0.0.1:19879/mcp

Адреса — именно `127.0.0.1`, а не `localhost`: на Docker Desktop под
Windows каждое новое HTTP-соединение к `localhost` стоит около 0,26 с
(против ~4 мс к `127.0.0.1`), а streamable HTTP открывает соединение на
каждый вызов инструмента.

### 2. Создание проекта

Откройте Web UI → "Создать проект":
- ID: `demo`
- Название: `Демо-конфигурация`

### 3. Добавление данных

Источник — это XML-выгрузка конфигурации (Конфигуратор → «Выгрузить
конфигурацию в файлы»). Отчёт по конфигурации выгружать **не нужно**:
метаданные (объекты, реквизиты, типы, формы, подсистемы) индексатор
читает прямо из этой же XML.

**Вариант A — Upload через Web UI:**

На странице проекта нажмите «+ Добавить источник» и загрузите
XML-выгрузку (.zip или .tar.gz). Архив проверяется до распаковки:
пути только внутри выгрузки, без ссылок, не больше 1 млн файлов и
64 ГБ в распакованном виде (`ARIADNA_ARCHIVE_MAX_FILES`,
`ARIADNA_ARCHIVE_MAX_BYTES`). Отклонённый архив не трогает прежнюю
выгрузку источника.

**Вариант B — Монтирование volume:**

```bash
# Положить выгрузку на хосте:
mkdir -p data/projects/demo/sources/main/xml
cp -r ВыгрузкаКонфигурации/* data/projects/demo/sources/main/xml/

# Для расширения:
mkdir -p data/projects/demo/sources/ext1/xml
cp -r ВыгрузкаРасширения/* data/projects/demo/sources/ext1/xml/

# Затем в Web UI добавить source и переиндексировать
```

### 4. Индексация

На странице проекта нажмите «Переиндексировать». Прогресс (процент, фаза,
оценка оставшегося времени) виден на дашборде, в том числе если открыть
страницу заново или запустить индексацию из другой вкладки или из MCP.
По завершении страница обновляется сама.

Индексация — фоновый поток внутри процесса и перезапуск не переживает.
Если сервер остановили посреди прогона, при следующем старте статус
`indexing` снимается автоматически (в лог пишется предупреждение), а
прежний индекс остаётся пригодным — прогон достаточно повторить.

Без сервера — командой `ariadna-index <проект> [--source <источник>]`
(ставится с пакетом, `pip install .`); без установки пакета из корня
репозитория — `python -m src.core.indexer <проект>`. Не запускайте её
параллельно с работающим сервером на тех же `data/`: реестр `projects.json`
у них общий.

### 5. Подключение MCP к агенту (по желанию)

Шаги 1–4 уже дают рабочий сервер: искать по метаданным, коду и графу
вызовов можно прямо в Web UI, без какого-либо агента (см. «Web UI» ниже).
Этот шаг — только если нужен доступ из Claude Code, Cursor, Codex и т.п.

Сервер должен быть запущен (`docker compose up -d`), проверка —
`curl http://127.0.0.1:19878/health`.

**Claude Code** — одной командой, для всех проектов на машине:

```bash
claude mcp add --transport http --scope user ariadna http://127.0.0.1:19879/mcp
```

Без `--scope user` сервер подключится только в текущем каталоге. Чтобы
подключение ехало вместе с репозиторием (у всей команды), положите в
корень проекта `.mcp.json` — Claude Code спросит подтверждение при первом
запуске:

```json
{
  "mcpServers": {
    "ariadna": {
      "type": "http",
      "url": "http://127.0.0.1:19879/mcp"
    }
  }
}
```

`"type": "http"` обязателен. `.claude/settings.json` MCP-серверы **не
читает** — конфигурация, положенная туда, молча игнорируется.

Проверка: в Claude Code команда `/mcp` показывает `ariadna` со статусом
connected и 17 инструментами.

**Cursor** — `.cursor/mcp.json` в проекте или `~/.cursor/mcp.json`:

```json
{
  "mcpServers": {
    "ariadna": { "url": "http://127.0.0.1:19879/mcp" }
  }
}
```

Если Cursor запустился раньше сервера (например, сразу после входа в
систему, пока поднимается Docker Desktop), он помечает `ariadna` ошибкой
подключения и **сам не переподключается** — инструментов Ариадны у агента не
будет, хотя Web UI уже работает. Переподключить: Settings → MCP → выключить и
включить `ariadna` (или Ctrl+Shift+P → Reload Window). Ариадну достаточно
прописать в одном месте — либо в `~/.cursor/mcp.json`, либо в
`.cursor/mcp.json` проекта: с одинаковым именем в обоих Cursor использует
одну запись, и вторая только путает.

**Codex CLI** — одной командой:

```bash
codex mcp add ariadna --url http://127.0.0.1:19879/mcp
```

Или вручную в `~/.codex/config.toml` (либо `.codex/config.toml` в
проекте — только для доверенных проектов):

```toml
[mcp_servers.ariadna]
url = "http://127.0.0.1:19879/mcp"
```

Проверка: в composer Codex команда `/mcp` показывает `ariadna` со
статусом connected.

**Сервер на другой машине.** По умолчанию порты публикуются только на
`127.0.0.1` — с других машин сервер недоступен. Чтобы открыть доступ,
задайте адрес публикации явно:

```bash
BIND_ADDR=0.0.0.0 MCP_ALLOWED_HOSTS=192.168.1.10,ariadna.lan docker compose up -d
```

и в клиенте замените `127.0.0.1` на адрес машины. `MCP_ALLOWED_HOSTS` —
имена и адреса, под которыми к серверу обращаются (через запятую; `host` —
любой порт, `host:port` — только этот). MCP и Web UI отклоняют запросы с
другим заголовком `Host` — это защита от DNS rebinding: без неё страница в
браузере могла бы обращаться к серверу на `127.0.0.1` от имени своего
домена. Без `MCP_ALLOWED_HOSTS` пускаются только `127.0.0.1`, `localhost`
и `[::1]`. **Авторизации нет:**
любой, кто достаёт до этих портов, может искать по конфигурации,
запускать переиндексацию и удалять источники. Открывайте порты только в
доверенной сети.

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

```python
> list_projects()                                   # если проектов несколько — узнать project_id
> search_metadata(project_id="demo", query="Номенклатура")
> get_object_details(project_id="demo", full_name="Справочник.Номенклатура")
> search_procedures(project_id="demo", query="ЗначениеРеквизитаОбъекта", export_only=true)
> get_procedure_code(project_id="demo", module_name="ОбщегоНазначения",
                     procedure_name="ЗначениеРеквизитаОбъекта")
> get_call_tree(project_id="demo", procedure_name="ЗначениеРеквизитаОбъекта",
                module_name="ОбщегоНазначения", direction="up", depth=2)
> search_code(project_id="demo", query="ВЫБРАТЬ ПЕРВЫЕ 1")
> semantic_search_code(project_id="demo", query="где рассчитывается себестоимость")
```

Без своих правил ничего настраивать не нужно: инструкция сервера уже
объясняет агенту и поиск по описанию (`semantic_search_code`).

**Правила для агента в проекте 1С.** Если в проекте уже есть свои правила
(Cursor rules, `CLAUDE.md`, `AGENTS.md`) и другие MCP-серверы для 1С, агенту
нужно явно сказать, когда звать Ариадну. Готовые шаблоны — в
[docs/agent-rules](docs/agent-rules/README.md): Ариадна как основной индекс,
как вспомогательный рядом с другим сервером или блок для встраивания в уже
существующую цепочку серверов.

## Web UI

http://127.0.0.1:19878 — работает сразу после `docker compose up -d`,
агент не нужен. Здесь же выполняются шаги 2–4 быстрого старта:

- **Проекты** — создание, добавление источников (upload .zip или volume),
  запуск переиндексации, прогресс и статистика по каждому.
- **Поиск** — по метаданным (объекты, реквизиты, типы, формы) и по коду
  (FTS5 полнотекстовый).
- **Playground** — вызвать любой из 16 MCP-инструментов вручную с формой
  параметров и увидеть тот же ответ, что получит агент, — удобно для
  проверки сервера и данных без настройки Claude Code/Cursor/Codex.
  Инструменты, параметры, их описания и умолчания берутся из схемы
  MCP-сервера, ответ строит тот же код, что отвечает агенту; `project_id`
  подставляется из проекта страницы. Ошибка инструмента (у агента —
  `isError`) выделена. `remove_source` без флажка `confirm` только
  показывает, что будет удалено.
- Светлая/тёмная тема, шрифты и иконки — локальные файлы, работает без
  интернета.

## Порты

| Сервис            | Контейнер | Хост (по умолчанию) | Настройка                   |
|-------------------|-----------|---------------------|------------------------------|
| MCP streamable HTTP | 9879    | 19879               | `MCP_HTTP_HOST_PORT=33002`  |
| Web UI            | 9878      | 19878               | `WEB_HOST_PORT=33001`       |

```bash
# Свои порты:
MCP_HTTP_HOST_PORT=33002 WEB_HOST_PORT=33001 docker compose up -d
```

## MCP Tools

17 инструментов. Все, кроме `reindex` и `remove_source`, только читают
(помечены `readOnlyHint`), `remove_source` помечен `destructiveHint`.

| Tool                | Для какого вопроса                                |
|---------------------|---------------------------------------------------|
| `list_projects`     | Какие проекты есть, их id и статус                |
| `list_sources`      | Из чего состоит проект: конфигурация, расширения, версии |
| `search_metadata`   | Найти объект по имени, синониму или своими словами; фильтр `kind` (RU/EN) |
| `get_object_details`| Карточка объекта: реквизиты с типами, ТЧ, формы; `detail="full"` — целиком |
| `search_attributes` | Найти реквизит (объекта или формы) по имени, синониму или типу |
| `find_references`   | Где объект используется как тип реквизита других объектов |
| `list_objects`      | Перечень объектов вида (`kind`)                   |
| `search_procedures` | Найти процедуру/функцию по имени; точные совпадения первыми |
| `get_procedure_code`| Код процедуры с номерами строк                    |
| `get_module_outline`| Оглавление модуля без чтения кода                 |
| `get_call_tree`     | Кто вызывает (`direction="up"`) / что вызывает (`"down"`) |
| `search_code`       | Строка внутри кода: запросы, присваивания, комментарии |
| `semantic_search_code` | Процедура по описанию своими словами; ответ — текст и `structuredContent` с режимом поиска, строками и отпечатками |
| `get_index_status`  | Статистика индекса и прогресс переиндексации      |
| `reindex`           | Переиндексировать (в фоне, инкрементально)        |
| `diagnose_index`    | Полнота индекса и качество графа вызовов (медленно на крупных конфигурациях) |
| `remove_source`     | Удалить источник — без `confirm=true` только показывает, что будет удалено |

Соглашения, общие для всех инструментов:

- **`project_id`** — необязателен, только если в реестре один проект;
  иначе ошибка перечисляет доступные id.
- **Имена** — как в конфигураторе, русские или английские, регистр не
  важен: `kind="Catalog"` равно `kind="Справочник"`.
- **Имя модуля** (`module_name` — в `get_procedure_code`,
  `get_module_outline`, `get_call_tree`) — полное
  (`ОбщийМодуль.ОбщегоНазначения.Модуль`,
  `Документ.РеализацияТоваровУслуг.МодульОбъекта`), короткое имя общего
  модуля (`ОбщегоНазначения`) или часть имени. Точное совпадение
  выигрывает у частичного; самый надёжный вариант — скопировать имя из
  вывода `search_procedures`.
- **`source_id`** — сужает выдачу до конфигурации или одного расширения.
- **Пагинация** — `limit`/`offset`; строка `use offset=N for more` в конце
  выдачи значит, что есть продолжение. `limit` больше 500 урезается до 500
  (об этом сказано в конце ответа).
- **Ошибки** приходят клиенту как ошибка инструмента (`isError`) с
  понятным текстом: какой проект или источник не найден и какие есть.
- **`search_code`** ищет подстроку без учёта регистра, не регулярное
  выражение.

## Структура данных

```
data/
├── projects.json              # Реестр проектов
└── projects/
    └── demo/
        ├── index.db           # SQLite индекс (FTS5) — при запуске без Docker
        └── sources/
            ├── main/
            │   ├── xml/       # XML-выгрузка конфигурации — единственный вход
            │   └── report.txt # отчёт по конфигурации, только если нет xml/
            └── ext1/
                └── xml/
```

В Docker индексы лежат не в `data/`, а на именованном томе
`ariadna-index` (в контейнере — `/index/{проект}/index.db`, переменная
`INDEX_DIR`). В `./data` остаются выгрузки и `projects.json`.

### Индексы в Docker: том `ariadna-index`

Зачем отдельный том. На Docker Desktop под Windows `./data` виден
контейнеру через файловую систему 9p. Поиск процедур делает полный проход
по индексу, и через 9p файл перечитывается при каждом вызове: 3,5–4,5 с на
вызов против 0,1 с на томе. Кроме того, через 9p не работают WAL и
блокировки SQLite.

**Первый старт после обновления.** Сервер один раз переносит существующие
`data/projects/*/index.db` на том: копирует через SQLite backup API (с
данными из WAL) до запуска MCP и Web UI. Каждый перенос с размером и
временем пишется в лог (`docker compose logs`). Время — однократное
чтение файла с `./data` через 9p и запись на том: на Docker Desktop под
Windows индекс крупной конфигурации (~1,2 ГБ) переносится за 25–30 с. Если
копирование проекта не удалось, это тоже будет в логе, а проект просто
переиндексируется.

**Старые файлы.** После переноса `data/projects/*/index.db` (и `-wal`,
`-shm` рядом) контейнером больше не используются — их можно удалить
вручную, когда убедитесь, что поиск работает. Сервер их не удаляет.

**Пересобрать индекс с нуля:** `reindex` в Web UI или через MCP
(переиндексация одного проекта), либо целиком:
`docker compose down && docker volume rm ariadna-index && docker compose up -d`
— затем reindex каждого проекта. Если старые `data/projects/*/index.db`
ещё не удалены, при следующем старте они снова будут перенесены.

**Доступ к индексу с хоста.** В Docker-режиме `index.db` лежит на томе и
с хоста напрямую не виден — открыть его `sqlite3` или DB Browser можно
только внутри контейнера (`docker compose exec ariadna bash`). Поэтому
прежнее правило «не открывать `data/projects/*/index.db`, пока контейнер
запущен» для Docker больше не актуально: контейнер эти файлы не читает.
Оно остаётся в силе, только если запускать контейнер без `INDEX_DIR`
(индексы снова в `./data` через 9p): тогда процесс на хосте, закрывая
соединение, удаляет WAL-файлы из-под сервера, и MCP с Web UI отвечают
`unable to open database file` (лечится `docker restart ariadna`).

**Не запускайте сервер без Docker одновременно с контейнером на одном
`data/`** (`docker compose stop` перед `python -m src.main`): у них общие
`projects.json` и выгрузки. Slow-тесты (`pytest -m slow`) в `data/` не
пишут — для них контейнер останавливать не нужно.

### Эксплуатация

Готовность проектов для оркестратора и скриптов — `GET /ready` (`200` /
`503`, `?project=ID` — один проект), отдельно от `/health` (процесс жив).
Из чего и чем построен индекс — `GET /api/projects/{id}/manifest`: версии
конфигураций, время индексации, версия парсера, покрытие поисковыми
карточками и что требует переиндексации. Резервная копия, перенос на другую
машину и восстановление — [docs/operations.md](docs/operations.md).

## Разработка

Для работы сервер запускается в Docker (см. «Быстрый старт»). Запуск без
Docker — для правки кода и тестов; подробно — SETUP.md, «Разработка без
Docker». Серверы слушают только `127.0.0.1` (`LISTEN_ADDR`).

```bash
# Окружение по pyproject.toml — без него тесты сразу остановятся
# со списком неподходящих пакетов
python -m venv .venv
source .venv/bin/activate   # Windows: .venv\Scripts\activate
pip install -e ".[dev]"

# Переменные (необязательно — ниже значения по умолчанию)
export DATA_DIR=./data
export INDEX_DIR=./index    # индексы отдельно от data/ (без неё — data/projects/{id}/index.db)
export WEB_PORT=9878
export MCP_HTTP_PORT=9879   # streamable HTTP

# Сервер без Docker — режим разработки (контейнер на том же data/ остановить)
python -m src.main

# Тесты (быстрые)
python -m pytest tests -q

# Полная переиндексация своих проектов из data/ (id через запятую)
ARIADNA_CORPUS=demo,myconf python -m pytest tests -q -m slow

# Проверка работающего сервера по сети (Docker или без него)
python scripts/live_check.py --project demo
```

### Проверка работающего сервера: `scripts/live_check.py`

Быстрые тесты гоняют код в одном процессе; часть ошибок видна только против
запущенного сервера. `live_check.py` проверяет сервер по сети — так, как его
видят агент (MCP, streamable HTTP) и браузер (Web UI):

- регрессия: версия, 17 инструментов, ошибки, проверка `Host`/`Origin`;
- индексация: сразу после `reindex` статус `indexing`, затем `ready`;
- playground против MCP: для всех 17 инструментов ответы совпадают;
- пересоздание и удаление проекта под нагрузкой MCP и Web UI (ни одного 500,
  только «not found»/«проект удалён»);
- стресс: создание и удаление проектов рядом с чтением реестра;
- с `--docker-container ariadna` — нет `/index/<id>` удалённого проекта и
  ни одной трассировки или строки ERROR в логе контейнера;
- с `--project ID` — несколько инструментов на вашем проекте (только чтение)
  и время `search_procedures`.

Работает на своём временном проекте `zz_live_<суффикс>` из
`tests/fixtures/xml_ru` и удаляет его сам в конце, даже при падении
проверок; существующие проекты не трогает. После обновления сервера:

```bash
python scripts/live_check.py --project demo              # Docker, адреса по умолчанию
python scripts/live_check.py --quick --web http://127.0.0.1:9878 --mcp http://127.0.0.1:9879/mcp   # без Docker
```

`--quick` — один прогон удаления под нагрузкой и 5 с стресса вместо 5 и
30 с. Код выхода 1 при любом FAIL, в конце — сводка. В CI то же самое идёт на
каждый PR: Docker-образ целиком на Linux и запуск без Docker на Windows.

## Статус

Текущая версия — 1.2.0, история изменений — [CHANGELOG.md](CHANGELOG.md).
Проверено вживую на реальных конфигурациях: две крупные типовые (одна с
расширением), конфигурация с одиннадцатью расширениями и собственная
конфигурация в работе — через Cursor и Claude Code.

Контракт стабилен: в пределах 1.x имена инструментов MCP, их параметры и
смысл ответов не меняются ломающе (подробно — CHANGELOG, «Что гарантирует
1.0»). Новые инструменты и необязательные параметры могут появляться в
минорных версиях; ломающие изменения — только в 2.0.

Замечания из работы (неверный или неполный ответ, лишние вызовы агента,
непонятное описание инструмента) — в GitHub Issues по шаблону «Замечание
из работы» (метка `dogfood`).