sales-analytics
by ibezgachev
README.md
# sales-analytics-mcp
Прототип аналитической системы на базе LLM: MCP-сервер и набор скиллов,
через которые модель загружает табличные данные (CSV/Excel/JSON),
очищает их, строит графики и пишет отчёт с выводами.
Собственного чат-интерфейса здесь нет и не предполагается — сервер
подключается к готовому клиенту (Claude Desktop), и всю цепочку вызовов
ведёт сама модель.
Ключевое архитектурное решение: **датафрейм не пересекает границу LLM**.
`load_data` кладёт данные в session store и возвращает короткий
`dataset_id`; все остальные инструменты принимают этот id, а не сами
данные. Обоснование и замеры — в [ARCHITECTURE.md](ARCHITECTURE.md).
- Пример сгенерированного отчёта: [reports/sample_report.md](reports/sample_report.md)
- Графики: [charts/](charts/)
- Описание архитектуры: [ARCHITECTURE.md](ARCHITECTURE.md)
- Спецификация инструментов для REST-интеграций: [openapi.json](openapi.json)
## Стек
Python 3.11+, [FastMCP](https://gofastmcp.com) (транспорты stdio и
streamable-http), pandas, matplotlib + seaborn (статичные PNG),
openpyxl, ruff, pytest.
## Установка
```bash
git clone https://github.com/ibezgachev/sales-analytics-mcp.git
cd sales-analytics-mcp
python -m venv .venv
# Windows
.venv\Scripts\activate
# Linux / macOS
source .venv/bin/activate
pip install -e ".[dev]"
```
Проверка, что всё встало:
```bash
pytest
ruff check .
```
## Запуск
Обычно сервер запускать руками не нужно — MCP-клиент делает это сам (см.
следующий раздел). Ручной запуск полезен, чтобы убедиться, что сервер
стартует без ошибок.
```bash
# транспорт stdio — для локальных клиентов вроде Claude Desktop
python server_stdio.py
# транспорт streamable-http — http://127.0.0.1:8000/mcp
python server_http.py
```
Оба файла собирают один и тот же набор инструментов через
`core.mcp_app.build_mcp_server()`; различается только транспорт.
## Подключение к Claude Desktop
> ### ⚠️ Где на самом деле лежит `claude_desktop_config.json`
>
> Стандартный путь `%APPDATA%\Claude\claude_desktop_config.json` верен
> **не для всех установок**. Если Claude Desktop поставлен как
> приложение из Microsoft Store (MSIX-пакет), этой папки не существует
> вовсе, а конфиг лежит в песочнице пакета:
>
> ```
> %LOCALAPPDATA%\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Roaming\Claude\claude_desktop_config.json
> ```
>
> Симптом: правишь файл по «правильному» пути (или создаёшь его) — и
> сервер не появляется в клиенте, сколько ни перезапускай. На поиск этого
> легко потерять полчаса и решить, что проект не работает.
>
> Надёжный способ определить свой вариант — найти файл по имени:
>
> ```powershell
> Get-ChildItem -Path $env:LOCALAPPDATA,$env:APPDATA -Recurse -Filter claude_desktop_config.json -ErrorAction SilentlyContinue
> ```
Добавьте в конфиг блок `mcpServers` (если файл уже есть — допишите ключ
`sales-analytics` внутрь существующего `mcpServers`, не затирая
остальное):
```json
{
"mcpServers": {
"sales-analytics": {
"command": "C:\\путь\\к\\проекту\\.venv\\Scripts\\python.exe",
"args": ["C:\\путь\\к\\проекту\\server_stdio.py"]
}
}
}
```
Пути — абсолютные, обратные слэши экранированы. На Linux/macOS —
`/путь/к/проекту/.venv/bin/python` без экранирования.
После правки **полностью завершите приложение** (через трей или
диспетчер задач — закрытия окна недостаточно) и запустите заново.
Проверить: в списке инструментов клиента должен появиться
`sales-analytics` с 13 инструментами.
## Инструменты
| Инструмент | Назначение |
|---|---|
| `load_data` | Загрузка CSV/Excel/JSON, автоопределение кодировки, разделителя и формата дат. Возвращает `dataset_id` и сводку |
| `describe_data` | Статистика по типу колонки: числа, категории, даты |
| `clean_data` | Дубли, пропуски, нормализация текста, выбросы по IQR. Возвращает новый `dataset_id` и лог операций |
| `aggregate` | Сводная таблица числами, без построения графика |
| `plot_trend` | Динамика числовой колонки по месяцам |
| `plot_distribution` | Гистограмма распределения |
| `correlation_analysis` | Тепловая карта корреляций |
| `plot_top_n` | Горизонтальная столбчатая диаграмма топ-N категорий |
| `auto_analyze` | Сам выбирает тип графика по типу колонки |
| `analyze_seasonality` | Распределение показателя по календарным месяцам и кварталам |
| `list_datasets` | Какие `dataset_id` доступны в этой сессии |
| `prepare_insights_context` | Собирает статистику, лог очистки и описания графиков в материал для отчёта |
| `export_report` | Сохраняет готовый текст отчёта в `reports/` |
Первые пять имён из ТЗ (`load_data`, `describe_data`, `plot_trend`,
`plot_distribution`, `correlation_analysis`) сохранены дословно.
Каждый график возвращает **путь к PNG и текстовое описание того, что на
нём видно** — модель не видит изображение, и без описания не смогла бы
сослаться на график в отчёте. Почему это оказалось критично и что
выяснилось при проверке — в [ARCHITECTURE.md](ARCHITECTURE.md).
## Пример диалога
Системный промпт с последовательностью шагов лежит в
[prompts/system_prompt.md](prompts/system_prompt.md) и дублируется как
MCP-примитив `prompt` с именем `sales_analysis_workflow` — клиент может
подтянуть его сам.
Первое сообщение может быть таким:
```
Проанализируй данные о продажах из файла
C:\путь\к\проекту\data\sales_data.csv
Загрузи их, посмотри структуру, почисти от дефектов, построй графики
и дай развёрнутый отчёт с выводами и практическими рекомендациями.
```
Дальше модель ведёт цепочку сама:
`load_data` → `describe_data` → `clean_data` → графики →
`prepare_insights_context` → `export_report`.
Что получилось на выходе — [reports/sample_report.md](reports/sample_report.md).
Скриншоты диалога: [docs/screenshots/](docs/screenshots/) — прогон
проведён в чистом чате, без подгрузки системного промпта, только по
описаниям инструментов.
## Тестовые данные
`data/sales_data.csv` — синтетический датасет (180 строк, 2023–2024), в
который **намеренно заложены дефекты**: пропуски, дубли, выбросы,
разнобой в форматах дат и в написании регионов. Без них очистке нечего
было бы чистить.
Точный состав дефектов с количествами — [data/README.md](data/README.md);
этот файл служит эталоном при проверке очистки.
Пересоздать (воспроизводимо, `random_state` зафиксирован):
```bash
python scripts/generate_data.py
```
## Интеграция через OpenAPI
[openapi.json](openapi.json) представляет каждый MCP-инструмент как
`POST /tools/{name}` с той же JSON-схемой параметров, которую видит
модель. Это не спецификация HTTP-маршрутов `server_http.py` (тот говорит
на протоколе MCP, а не на обычном REST), а совместимое представление для
интеграций, которым нужен именно OpenAPI — например, Custom GPT Action.
Живой публичный HTTPS-эндпоинт в рамках задания не разворачивался, это
осознанное ограничение — см. ARCHITECTURE.md.
Перегенерировать после добавления скилла:
```bash
python scripts/generate_openapi.py
```
## Разработка
```bash
ruff check . # линтер
ruff format . # форматтер
pytest # тесты
```
Добавление нового скилла — это один новый файл в `skills/`; правок в
`core/` и в серверных точках входа не требуется. Как именно — в разделе
«Как добавить новый скилл» в [ARCHITECTURE.md](ARCHITECTURE.md).
## Лицензия
[MIT](LICENSE).
### Расширяемость подтверждается диффом, а не декларацией
Последний скилл — `analyze_seasonality` — добавлен намеренно отдельно
от остальных, уже после того как система была написана и
задокументирована, именно чтобы это можно было проверить.
```bash
git show --stat "$(git log --format=%H --grep='скилл анализа сезонности' -1)"
```
В этом коммите — ровно два файла: `skills/seasonality.py` и правка
таблицы инструментов в `README.md`. Ни строки в `core/`, ни строки в
`server_stdio.py` и `server_http.py`. При этом после перезапуска клиента
инструмент появляется в списке тринадцатым, со схемой параметров,
построенной из сигнатуры и докстринга.
(Тесты на скилл добавлены следующим коммитом — отдельно, чтобы дифф
коммита-доказательства оставался минимальным и его можно было
прочитать целиком за полминуты.)
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues