Skip to main content
Glama
README.md
# md-sql

MCP-сервер для работы с Markdown-документацией как с реляционной базой данных. Парсит `.md` файлы в SQLite-таблицу `md`, предоставляя два MCP-инструмента: `md_select` (SELECT-запросы) и `md_change` (INSERT / UPDATE / DELETE с автоматической записью изменений обратно в исходные файлы).

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

```bash
pip install -e .
DOC_ROOTS='["/path/to/docs"]' python -m mcp_md_sql.server
```

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

### Zed

```json
{
  "mcp_servers": {
    "sql-mcp": {
      "command": "python",
      "args": ["-m", "mcp_md_sql.server"]
    }
  }
}
```

### Claude Desktop

```json
{
  "mcpServers": {
    "sql-mcp": {
      "command": "python",
      "args": ["-m", "mcp_md_sql.server"]
    }
  }
}
```

> `DOC_ROOTS` можно не указывать — сервер получит корни от IDE через MCP-протокол. Переменная окружения используется как fallback.

## DOC_ROUTES

JSON-массив путей:

```bash
DOC_ROOTS='["/project/docs", "/project/specs"]'
```

Алиас корня генерируется из имени папки. Для путей выше это `docs` и `specs`.

Поддерживается и legacy-формат `alias=/path` (разделитель `;`):

```bash
DOC_ROOTS="docs=./docs;specs=./specifications"
```



## MCP-инструменты

Корни документации разрешаются **lazy** при первом вызове (на всю жизнь инстанса):
1. **IDE roots** — MCP `session.list_roots()` (один раз, кэшируется)
2. Fallback — `DOC_ROOTS` из env
3. Error — если ни один источник не дал корней

Параметр `root_path` опционален — используется только для override корня на конкретный вызов.

Инстанс MCP локальный для проекта → одна in-memory SQLite БД на весь lifecycle.

### `md_select`

Выполняет произвольный SELECT-запрос к таблице `md`.

```
SELECT * FROM md WHERE type = 'heading' AND depth = 0
```

### `md_change`

Модифицирует документацию через INSERT / UPDATE / DELETE. Хирургически патчит исходный `.md` файл и возвращает diff.

Ограничения:
- DDL и транзакции запрещены
- UPDATE и DELETE без WHERE запрещены
- Прямое изменение `id` запрещено
- Multi-statement в SELECT запрещены
- Таймаут SQL-запроса: 15 секунд

### `md_debug`

Диагностический инструмент (временный). Без параметров. Возвращает JSON с:
- состояние `_conn`/watcher
- env (`DOC_ROOTS`, CWD)
- статистика БД (если загружена)
- клиентские capabilities (есть ли `roots`)
- результат `list_roots()` probe

Используется для отладки разрешения корней.

## Таблица `md`

| Колонка      | Тип       | Описание                                     |
|-------------|-----------|------------------------------------------------|
| id          | INTEGER   | Автоинкрементный первичный ключ                |
| root        | TEXT      | Полный абсолютный путь к корню документации    |
| file        | TEXT      | Относительный путь к .md файлу                 |
| line_start  | INTEGER   | Начальная строка элемента в файле (1-based)     |
| line_end    | INTEGER   | Конечная строка элемента                        |
| pos_in_file | INTEGER   | Порядковая позиция в документе (1..N)           |
| parent_pos  | INTEGER   | pos_in_file родителя (NULL для корневых)        |
| depth       | INTEGER   | Уровень вложенности                             |
| type        | TEXT      | Тип элемента (heading, paragraph, list_item, code_block, blockquote, table, strong, emphasis, link, image, inline_code, strikethrough, hr, frontmatter, html, html_inline) |
| content     | TEXT      | Содержимое элемента                             |
| attrs_json  | TEXT      | JSON-строка с атрибутами                       |

## Структура проекта

```
src/mcp_md_sql/
├── config.py         # DOC_ROOTS → RootConfig
├── parser.py         # Парсинг .md через mistune → flat list
├── database.py       # SQLite in-memory: схема, загрузка, reload
├── reconstructor.py  # Обратная сборка Markdown из строк таблицы
├── tools.py          # md_select / md_change
├── watcher.py        # Live-синхронизация через watchdog
└── server.py         # Точка входа, MCP-сервер

tests/
├── conftest.py              # Глобальные фикстуры (cleanup кэша корней)
├── test_parser.py          # 30 тестов парсера (+ frontmatter, nested lists)
├── test_reconstructor.py   # 23 теста реконструктора (+ frontmatter)
├── test_integration.py     # 15 интеграционных тестов (+ integrity check)
├── test_select_queries.py  # 47 тестов (SELECT + валидация + config + timeout + FS errors)
├── test_watcher.py         # 4 теста файлового вотчера
├── test_server.py          # 26 тестов (_format_select_result + _handle_call_tool + _ServerState)
├── test_scenarios.py       # 33 сценария реальной работы (через _handle_call_tool)
└── fixtures/
    ├── test_basic.md       # Базовый фикстур
    └── complex/            # 5 сложных .md документов для 20 сценариев
        ├── api_docs.md
        ├── configuration_guide.md
        ├── technical_spec.md
        ├── tutorial_quickstart.md
        └── deployment_guide.md
```

## Запуск тестов

```bash
pip install -e ".[dev]"
python -m pytest tests/
```

> Тесты запускаются параллельно (через `pytest-xdist -n auto`).
> Полный прогон занимает ~7 секунд.

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

- `mistune >= 3.0` — парсинг Markdown в AST
- `watchdog >= 6.0` — отслеживание изменений файлов
- `mcp >= 1.0` — протокол Model Context Protocol
- Python >= 3.11