MCP-сервер поиска лекарств по интернет-аптекам: где есть в наличии и по какому адресу
  • Python 96.9%
  • Nix 3.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Aleksandr aebf2ea20c Фильтры по умолчанию: только в наличии и без агрегаторов
По умолчанию apteki_search/apteki_where сужают выдачу:

- in_stock_only=true — скрыть отсутствующее. У Product добавлен
  нормализованный флаг in_stock, каждый провайдер проставляет его из своего
  сигнала (available_quantity, is_in_stock, «в N аптеках» и т.п.).
- include_aggregators=false — скрыть агрегаторы (Мегаптека, АСНА, ГорЗдрав,
  36,6): их выдача пересекается с одиночными сетями и между собой (ГорЗдрав и
  36,6 — почти один бэкенд), охваты «в N аптеках» складывать нельзя. Скрытые
  перечислены в ответе (hidden_aggregators). Провайдеры помечены aggregator.

Ответ apteki_search теперь структурирован по провайдеру (title, aggregator,
stock_kind, items), как и apteki_where.

Co-Authored-By: Eva
2026-08-10 16:24:13 +03:00
apteki_mcp Фильтры по умолчанию: только в наличии и без агрегаторов 2026-08-10 16:24:13 +03:00
.gitignore Убрать reverse/ из репозитория 2026-08-10 15:30:29 +03:00
AGENTS.md Первый релиз: MCP-сервер поиска лекарств по аптекам 2026-08-10 14:23:43 +03:00
CHANGELOG.md Фильтры по умолчанию: только в наличии и без агрегаторов 2026-08-10 16:24:13 +03:00
flake.lock Первый релиз: MCP-сервер поиска лекарств по аптекам 2026-08-10 14:23:43 +03:00
flake.nix АСНА, ГорЗдрав и 36,6: поиск, наличие и обход антиботов 2026-08-10 16:13:17 +03:00
pyproject.toml АСНА, ГорЗдрав и 36,6: поиск, наличие и обход антиботов 2026-08-10 16:13:17 +03:00
README.md Фильтры по умолчанию: только в наличии и без агрегаторов 2026-08-10 16:24:13 +03:00

apteki-mcp

MCP-сервер поиска лекарств по российским интернет-аптекам: что за препарат, по какой цене и — главное — в каких аптеках и по какому адресу его взять.

Инструменты

Инструмент Что делает
apteki_cities найти город и его слаг
apteki_search карточки препарата по площадкам: названия, цены, рецептурность
apteki_where аптеки с адресами, координатами и часами работы, ближайшие первыми

Город задаётся словом («Москва», «Питер») и резолвится сам; без него берётся APTEKI_CITY, иначе Москва.

apteki_where принимает опциональные координаты пользователя (lat, lon): если они заданы — область поиска и сортировка идут от них (чтобы дойти побыстрее), иначе от центра города. radius_km (по умолчанию 8) ограничивает охват; в каждой аптеке есть distance_km от точки отсчёта.

Что показывается по умолчанию

apteki_search и apteki_where по умолчанию сужают выдачу:

  • только в наличии (in_stock_only=true) — отсутствующее скрыто;
  • только одиночные сети; агрегаторы (Мегаптека, АСНА, ГорЗдрав, 36,6) скрыты, потому что их выдача пересекается с одиночными сетями и между собой (ГорЗдрав и 36,6 — почти один бэкенд), и их «в N аптеках» нельзя складывать или считать разными точками. Скрытые перечислены в ответе (hidden_aggregators).

Переопределить: include_aggregators=true — добавить агрегаторы; in_stock_only=false — показать и то, чего сейчас нет.

stock_kind

У каждой площадки в ответе есть stock_kind:

  • real — остаток на полке конкретной аптеки;
  • delivery — точка выдачи сети, куда заказ привезут (наличия «прямо сейчас» такая площадка не гарантирует).

Разница важна: сервер её проговаривает, чтобы «можно заказать» не выдавалось за «лежит на полке».

Площадки

Площадка Города Аптеки (по товару) Поиск товара stock
Мегаптека (Platfomni) вся РФ цена + охват наличия real
Вита (Platfomni) вся РФ по товару цена real
apteka.ru (Протек) вся РФ, 20+ анти-бот (410) delivery
Максавит (витрина сайта) 166 городов по товару цена + рецептурность real
Ригла (Magento GraphQL) 68 регионов, 866 городов по товару (шт.) цена + рецептурность real
АСНА (qHash + анон. OAuth) вся РФ, 85 регионов цена + рецептурность + охват delivery
ГорЗдрав (SDK ru.sdk, PoW) 14 регионов цена + рецептурность + охват delivery
Аптеки 36,6 (SDK ru.sdk, PoW) 8 регионов цена + рецептурность + охват delivery

Как читать столбцы:

  • Аптеки (по товару) — что возвращает apteki_where:

    • — справочник сети: все аптеки города. В какой из них лежит именно искомый препарат — по ответу не видно (есть лишь суммарный охват из поиска, «в N аптеках»);
    • ✅ по товару — только те точки, где искомый препарат есть, с ценой в каждой (наличие «есть/нет», без числа упаковок);
    • ✅ по товару (шт.) — то же и с количеством на полке («8 шт.»). Пока только Ригла.
  • Поиск товара — работает ли apteki_search: цена / + рецептурность / + охват («в N аптеках»); — ручка закрыта (см. «Что закрыто и почему»).

  • stock — это stock_kind (раздел выше): real — остаток на полке конкретной аптеки, delivery — точка выдачи, куда заказ привезут.

  • Мегаптека — агрегатор: apteki_search даёт цену и охват наличия («в 3191 аптеке»).

  • Вита — самая точная для apteki_where: возвращает именно те аптеки, где искомый товар лежит, с адресом и координатами.

  • Ригла — то же по остаткам (штуки на точке), плюс сеть «Будь Здоров!», Живика и точки ЗдравСити в одном справочнике. Регион задаётся поддоменом.

  • Максавит — отдаёт аптеки, где товар есть сегодня, и цену в каждой: она своя на город. Товар берётся не из подписанного мобильного API, а с витрины сайта (maksavit.ru/api/MobileApp) — каталога в мобильном контуре нет вовсе. Москвы в сети нет, ближайший её город — Пушкино; город назван в availability.

  • АСНА — франчайзинговый агрегатор: под её вывеской встречаются и 36,6, и Фармленд, и Планета Здоровья. Поиск даёт цену, рецептурность и охват («в 13382 аптеках»); справочник аптек Москвы — 2270 точек с координатами и метро. Остатка по точке API не отдаёт, поэтому delivery.

  • ГорЗдрав и Аптеки 36,6 — один SDK ru.sdk с криптографическим антиботом: X25519 + AES-GCM + proof-of-work на каждый запрос (отсюда зависимость cryptography). Оба дают поиск с ценой и охватом и общий справочник аптек (у ГорЗдрава — 2906 точек с адресом, координатами, метро). Остаток по конкретной точке закрыт за корзиной, поэтому delivery.

Разбор протоколов и происхождение констант — в reverse/notes/ (локально; в репозиторий не публикуется).

Что закрыто и почему

  • Полнотекстовый поиск apteka.ru закрыт и в мобильном контуре: приложение ищет тем же GET /Search/ByPhrase, но с андроид-сессией он отвечает 410 (тело {ip, requestId, identity, did}), как и /Search/CategoryUrl, /Search/Vendor, /Item/GroupInfo; несуществующий путь даёт 404 — роуты закрыты намеренно. Запрет не снимают ни свежая сессия, ни формат device-id, ни версия User-Agent, ни город. Открытые ручки фразу не ищут (/Search/AllItems отдаёт весь каталог), поэтому search там пуст — остаются города и сеть точек выдачи.
  • Всё семейство Platfomni вскрыто: Мегаптека/Вита (HMAC-подпись), Максавит (каталога в подписанном контуре нет — взят с витрины сайта), ГорЗдрав/36,6 (ушли с HMAC на SDK ru.sdk с PoW-антиботом), АСНА (своя схема — qHash + анонимный OAuth Bearer, без SMS).
  • Приложение Риглы написано на Flutter (логика в libapp.so), поэтому его разбирать не пришлось: сайт — Magento GraphQL без авторизации и подписи.

Запуск

nix run .            # или nix build && ./result/bin/apteki-mcp

Сервер stdio; подключается к ядру Евы как MCP (services.apteki-mcp в NixOS).

Разработка

nix develop          # python3 + httpx + cryptography + jadx/apktool/unzip