Skip to main content
Glama
sptmru

Yandex Eats MCP

by sptmru

Yandex Eats MCP

Однопользовательский MCP-сервер Streamable HTTP для ChatGPT. Он напрямую взаимодействует с приватным веб-API, используемым https://eats.yandex.com; автоматизация браузера не применяется.

Текущий релиз:

  • выполняет поиск ресторанов и подходящих позиций рядом с одной настроенной на сервере точкой доставки;

  • загружает доступность ресторанов и полные меню;

  • выводит список серверных корзин и читает их;

  • отслеживает активные заказы с опросом по указанию сервера, постоянным курсором событий и опциональной доставкой через Telegram;

  • может добавлять, обновлять и удалять позиции ресторанной корзины, включая позиции с пометкой 18+, после включения явного функционального флага;

  • защищает публичную MCP-конечную точку постоянным однопользовательским потоком OAuth 2.1 + PKCE;

  • никогда не раскрывает cookie Yandex, токен Passport, точные координаты, телефон, адрес или платёжные данные в результатах MCP.

Оформление заказа, place_order, самовывоз, SKU/розничные корзины и мультизаказ намеренно не реализованы. Товары 18+ могут быть добавлены в ресторанную корзину, но MCP не обходит проверки правомочности и не выполняет проверку возраста; эти требования по-прежнему обеспечиваются Yandex Eats. Корзина — это не заказ.

Уведомление о рисках

Этот проект зависит от недокументированного приватного API. Конечные точки и формы ответов могут меняться без предупреждения, поведение может управляться A/B-флагами, а автоматизированное использование может ограничиваться условиями Yandex или антибот-контролем. Никогда не автоматизируйте CAPTCHA или OTP. По возможности используйте отдельный аккаунт и не публикуйте этот сервис для других пользователей без отдельной юридической проверки и проверки безопасности.

Скопированный заголовок Cookie может предоставлять доступ к большему, чем Yandex Eats. Относитесь к нему как к паролю от аккаунта.

Related MCP server: bach-yelp_business_api

Архитектура

ChatGPT
   │ HTTPS + OAuth 2.1/PKCE
   ▼
Cloudflare hostname / Tunnel
   │ http://127.0.0.1:3000
   ▼
Docker: yandex-eats-mcp
   │ private Cookie + stable device/session IDs
   ▼
https://eats.yandex.com

Процессный OrderMonitor не зависит от сессий MCP-запросов. Он опрашивает доступные только для чтения конечные точки заказов Yandex, записывает очищенные данные состояния в /app/state/order-monitor-state.json, добавляет события, адресуемые курсором, в /app/state/order-events.jsonl и опционально отправляет минимальное сообщение в Telegram.

MCP-сервер не может инициировать сообщение в спящей беседе ChatGPT. ChatGPT может периодически вызывать get_order_events (например, из запланированной задачи), а Telegram обеспечивает канал push-уведомлений, близкий к реальному времени.

Конечная точка MCP — /mcp. Конечные точки обнаружения OAuth, регистрации клиента, авторизации, токена и метаданных защищённого ресурса обслуживаются тем же контейнером. OAuth-клиенты и хешированные токены доступа/обновления сохраняются в Docker-томе yandex-eats-state. Пароль владельца — это Docker-секрет, и он никогда не сохраняется в состоянии OAuth.

Требования

  • Docker Engine с Compose;

  • существующий Cloudflare Tunnel и управляемый вами hostname;

  • авторизованная браузерная сессия eats.yandex.com;

  • широта/долгота доставки для нужного адреса.

Настройка секретов

Создайте игнорируемые локальные файлы. Контейнер работает от непривилегированного пользователя node с UID/GID 1000:1000, поэтому файлы секретов должны быть читаемы для GID 1000, оставаясь недоступными для других пользователей хоста:

mkdir -p secrets
chown root:1000 secrets
chmod 750 secrets
umask 077
openssl rand -base64 32 > secrets/mcp_oauth_password

Для secrets/yandex_eats_cookie:

  1. Войдите в https://eats.yandex.com в Chrome.

  2. Откройте DevTools → Network и перезагрузите страницу.

  3. Выберите аутентифицированный запрос и скопируйте только значение полного заголовка запроса Cookie.

  4. Поместите это однострочное значение в secrets/yandex_eats_cookie. Не включайте префикс Cookie:.

  5. Дайте группе контейнера доступ на чтение к обоим секретам:

chown root:1000 secrets/yandex_eats_cookie secrets/mcp_oauth_password
chmod 640 secrets/yandex_eats_cookie secrets/mcp_oauth_password

Docker Compose реализует секреты на основе файлов как bind-монтирования только для чтения, поэтому файл хоста, принадлежащий root:root с режимом 0600, не может быть прочитан этим непривилегированным контейнером и вызывает сбой запуска с ошибкой EACCES.

Не вставляйте ни один из секретов в ChatGPT, аргументы команд оболочки, журналы, issues или коммиты.

Для уведомлений в Telegram создайте бота через BotFather, определите ID чата назначения и поместите два исходных значения в отдельные игнорируемые файлы:

printf '%s\n' '<bot token>' > secrets/order_notify_telegram_token
printf '%s\n' '<chat id>' > secrets/order_notify_telegram_chat_id
chown root:1000 secrets/order_notify_telegram_token secrets/order_notify_telegram_chat_id
chmod 640 secrets/order_notify_telegram_token secrets/order_notify_telegram_chat_id

Не включайте кавычки или имена переменных ни в один из файлов. Бот должен иметь возможность отправлять сообщения в выбранный чат.

Настройка сервиса

cp .env.example .env

Отредактируйте .env:

PUBLIC_BASE_URL=https://eats-mcp.example.com
YANDEX_EATS_LATITUDE=40.000000
YANDEX_EATS_LONGITUDE=44.000000
YANDEX_EATS_CITY=Yerevan
YANDEX_EATS_ADDRESS_LABEL=home
YANDEX_EATS_ENABLE_MUTATIONS=false
YANDEX_EATS_ENABLE_ORDER_MONITORING=true
ORDER_NOTIFY_PROVIDER=telegram

YANDEX_EATS_ADDRESS_LABEL — это безопасный текст, возвращаемый модели. Точные координаты остаются внутри клиента и никогда не включаются в ответы MCP.

Запустите сервер:

docker compose up -d --build
docker compose ps
curl http://127.0.0.1:3000/healthz

Порт Compose привязан к 127.0.0.1, а не ко всем интерфейсам хоста.

Cloudflare Tunnel

Создайте опубликованный маршрут приложения от выбранного hostname к:

http://127.0.0.1:3000

Этот адрес сервиса корректен, когда cloudflared работает на хосте. Если cloudflared работает в другом контейнере, поместите его в ту же Docker-сеть и направьте маршрут на http://mcp:3000 вместо адреса loopback хоста.

Важные настройки Cloudflare:

  • не кэшируйте /mcp, /authorize, /token, /register, /revoke, /oauth/* или /.well-known/*;

  • сохраняйте потоковые ответы и публичный заголовок Host;

  • правила WAF и ограничения частоты запросов допустимы, но не размещайте интерактивный вход Cloudflare Access перед этими путями — ChatGPT должен напрямую обращаться к обнаружению OAuth и колбэкам MCP;

  • сохраняйте PUBLIC_BASE_URL идентичным внешнему HTTPS-источнику, без /mcp или завершающего пути.

Cloudflare Tunnel делает источник доступным; поток MCP OAuth по-прежнему остаётся границей авторизации на уровне приложения.

Подключение из ChatGPT

  1. Убедитесь, что https://your-domain.example/healthz возвращает {"status":"ok"}.

  2. В режиме разработчика ChatGPT добавьте пользовательскую конечную точку MCP/плагина:

https://your-domain.example/mcp
  1. ChatGPT должен обнаружить метаданные OAuth и открыть страницу авторизации Yandex Eats MCP.

  2. Введите значение из secrets/mcp_oauth_password. Это пароль владельца MCP, а не пароль Yandex.

  3. Просмотрите список инструментов. remove_cart_item является деструктивным; оформление заказа и размещение заказа не должны появляться.

Страница владельца OAuth явно указывает, что она предоставляет только доступ к поиску/корзине.

Инструменты

Tool

State change

Notes

auth_status

Нет

Только очищенный статус cookie-сессии

get_delivery_context

Нет

Только город/метка, никогда точные координаты

search

Нет

Полнотекстовый поиск с непрозрачным курсором

get_place

Нет

Доступность и ETA

get_menu

Нет

Опциональная локальная фильтрация по запросу/категории

get_cart

Нет

Выводит список корзин или загружает одну свежую корзину

get_active_orders

Нет

Очищенные кэшированные активные заказы и состояние монитора

get_order_status

Нет

Кэшированный статус или явное обновление только для чтения

get_order_events

Нет

Постоянные события после эксклюзивного курсора последовательности

add_to_cart

Да

Проверяет текущее меню, обязательные опции и доступность; поддерживаются позиции с пометкой 18+

update_cart_item

Да

Только по явному запросу пользователя

remove_cart_item

Да, деструктивно

Никогда не является автоматической оптимизацией

server_capabilities

Нет

Сообщает о включённых границах безопасности

Каждая мутация принимает опциональный UUID operationId; повторение той же операции в течение десяти минут возвращает тот же результат в рамках процесса вместо повторения вышестоящей мутации. Повторное использование ID с другими аргументами отклоняется. Небезопасные вышестоящие запросы никогда не повторяются автоматически. Если ответ потерян или истёк тайм-аут, инструмент возвращает MUTATION_STATUS_UNKNOWN; вызовите get_cart для сверки.

После любой успешной мутации MCP перезагружает и возвращает серверную корзину. Проверки бюджета должны использовать эту свежую сумму и её нарушенные ограничения, а не локальную сумму.

Мониторинг заказов и Telegram

Мониторинг не зависит от мутаций корзины и использует только запросы, подобные чтению. Включите его с помощью:

YANDEX_EATS_ENABLE_ORDER_MONITORING=true
ORDER_NOTIFY_PROVIDER=telegram

Интервалы списка и отслеживания предоставляются Yandex и ограничиваются YANDEX_EATS_ORDER_POLL_MIN_MS / YANDEX_EATS_ORDER_POLL_MAX_MS. Сбои сети, 429 и 5xx используют ограниченную экспоненциальную паузу. 401/403 создаёт одно событие monitor.auth_expired; после замены cookie SIGHUP немедленно пробуждает монитор, и восстановление создаёт monitor.recovered.

Первый успешный снимок является базовым, поэтому развёртывание монитора не уведомляет о исторических заказах. Последующие обнаружения и изменения отпечатков создают монотонно упорядоченные события. Сообщения Telegram маскируют номер заказа и опускают адрес, координаты, телефон, платёж, данные карты и личность курьера.

Для опроса из ChatGPT сохраняйте nextSequence из get_order_events и передавайте его обратно как afterSequence. Курсор является эксклюзивным, и чтение не подтверждает события для других клиентов.

Включение мутаций корзины

Оставьте это отключённым на время первоначального развёртывания:

YANDEX_EATS_ENABLE_MUTATIONS=false

Сначала проверьте auth_status, search, get_menu и get_cart. Затем подготовьте одно одноразовое ресторанное блюдо без обязательных модификаторов, включите флаг и выполните контролируемый add_to_cart, за которым следуют get_cart и удаление только что созданного cartItemId.

YANDEX_EATS_ENABLE_MUTATIONS=true

Перезапустите контейнер после изменения флага. Никогда не используйте очистку корзины в качестве теста; она намеренно не предоставлена.

Если auth_status сообщает AUTH_EXPIRED или needsRefresh: true, замените secrets/yandex_eats_cookie свежим однострочным значением Cookie и пересоздайте монтирование Docker-секрета:

chown root:1000 secrets/yandex_eats_cookie
chmod 640 secrets/yandex_eats_cookie
docker compose up -d --force-recreate mcp

В развёртывании без Compose, где смонтированный секрет обновляется на месте, SIGHUP перезагружает cookie:

docker compose kill -s HUP mcp

Не реализованы автоматический вход в Passport, обработка OTP, CAPTCHA или сбор токенов.

Разработка и тесты

npm install
npm run check
docker compose config
docker compose build

Обычный набор тестов использует очищенные фикстуры и имитированные ответы вышестоящего сервиса. Он проверяет устойчивость мапперов, точную маршрутизацию запросов, отсутствие повторов для неоднозначных мутаций, сериализацию/идемпотентность мутаций, сохранение аутентификации и аннотации инструментов MCP.

Живые контрактные тесты только для чтения являются опциональными и требуют вашего локального cookie и координат:

export YANDEX_EATS_COOKIE_FILE="$PWD/secrets/yandex_eats_cookie"
export YANDEX_EATS_LATITUDE="40.000000"
export YANDEX_EATS_LONGITUDE="44.000000"
npm run test:live:readonly
npm run test:live:orders:readonly

Ни один живой тест в этом репозитории не создаёт заказ. Обычные тесты никогда не связываются с Yandex.

Эксплуатационные заметки

  • Состояние в /app/state включает чувствительные данные cookie-jar и OAuth; создавайте резервные копии и соответствующим образом защищайте Docker-хост.

  • /readyz сообщает о состоянии монитора без выполнения вышестоящего запроса или раскрытия ID заказов.

  • Журналы включают конечную точку, статус, длительность и корреляционные ID Yandex. Тела запросов/ответов, cookie, заголовки авторизации, ID сессий, телефон, адрес и платёжные поля редактируются или не логируются.

  • AUTH_NOT_CONFIGURED: секрет cookie отсутствует или нечитаем.

  • AUTH_EXPIRED: скопируйте свежий заголовок Cookie из браузера.

  • DELIVERY_LOCATION_NOT_CONFIGURED: настройте и широту, и долготу.

  • MUTATIONS_DISABLED: ожидаемо, пока функциональный флаг не будет намеренно включён.

  • REQUIRES_CONFIGURATION: изучите get_menu и попросите пользователя выбрать обязательные опции.

  • UNSUPPORTED_CART_MODE: текущий релиз отклоняет неподдерживаемые потоки SKU/самовывоза корзины.

  • MUTATION_STATUS_UNKNOWN: не повторяйте вслепую; сверьтесь с get_cart.

Related MCP Connectors

Related MCP Servers