Skip to main content
Glama
Yandex Eats MCP

Yandex Eats MCP

by sptmru

Yandex Eats MCP

面向 ChatGPT 的单用户 Streamable HTTP MCP 服务器。它直接与 https://eats.yandex.com 使用的私有 Web API 通信;不使用浏览器自动化。

当前版本:

  • 搜索服务器配置的配送点附近的餐厅和匹配商品;

  • 加载餐厅可用性和完整菜单;

  • 列出并读取服务端购物车;

  • 通过服务器指定的轮询、持久事件游标和可选的 Telegram 推送来监控活动订单;

  • 在显式功能标志启用后,可以添加、更新和移除餐厅购物车商品,包括标记为成人的商品;

  • 通过持久的单用户 OAuth 2.1 + PKCE 流程保护公共 MCP 端点;

  • 绝不将 Yandex cookie、Passport 令牌、精确坐标、电话、地址或支付数据作为 MCP 结果暴露。

结账、place_order、自提、SKU/零售购物车和 multiorder 均有意未实现。成人商品可以添加到餐厅购物车,但 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(例如通过 Scheduled Task),而 Telegram 提供近实时的推送通道。

MCP 端点为 /mcp。OAuth 发现、客户端注册、授权、令牌和受保护资源元数据端点由同一容器提供。OAuth 客户端和哈希后的访问/刷新令牌持久化在 yandex-eats-state Docker 卷中。所有者密码是 Docker secret,绝不会持久化在 OAuth 状态中。

要求

  • 带 Compose 的 Docker Engine;

  • 现有的 Cloudflare Tunnel 和你控制的主机名;

  • 已登录的 eats.yandex.com 浏览器会话;

  • 目标地址的配送纬度/经度。

配置 secrets

创建被忽略的本地文件。容器以非特权 node 用户身份运行,UID/GID 为 1000:1000,因此 secret 文件必须对 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. 在 Chrome 中登录 https://eats.yandex.com。

  2. 打开 DevTools → Network 并重新加载页面。

  3. 选择一个已认证的请求,仅复制其完整 Cookie 请求标头的值。

  4. 将该单行值放入 secrets/yandex_eats_cookie。不要包含 Cookie: 前缀。

  5. 授予容器组对两个 secret 的只读访问权限:

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

Docker Compose 将文件型 secret 实现为只读绑定挂载,因此由 root:root 所有、权限为 0600 的主机文件无法被此非特权容器读取,并会导致 EACCES 启动失败。

不要将任一 secret 粘贴到 ChatGPT、shell 命令参数、日志、issue 或提交中。

对于 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

从你选择的主机名创建一条已发布的应用路由,指向:

http://127.0.0.1:3000

当 cloudflared 在主机上运行时,该服务地址是正确的。如果 cloudflared 在另一个容器中运行,请将其放在同一 Docker 网络上,并路由到 http://mcp:3000,而不是主机回环地址。

重要的 Cloudflare 设置:

  • 不要缓存 /mcp、/authorize、/token、/register、/revoke、/oauth/* 或 /.well-known/*;

  • 保留流式响应和公共 Host 标头;

  • WAF 和速率限制规则没问题,但不要在这些路径前放置交互式 Cloudflare Access 登录——ChatGPT 必须直接访问 MCP OAuth 发现和回调;

  • 保持 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
  3. ChatGPT 应发现 OAuth 元数据并打开 Yandex Eats MCP 授权页面。

  4. 输入 secrets/mcp_oauth_password 中的值。这是 MCP 所有者密码,不是 Yandex 密码。

  5. 查看工具列表。remove_cart_item 具有破坏性;结账和下单不应出现。

OAuth 所有者页面明确说明其仅授予搜索/购物车访问权限。

工具

工具

状态变更

备注

auth_status

否

仅清理后的 cookie 会话状态

get_delivery_context

否

仅城市/标签,绝无精确坐标

search

否

带不透明游标的全文搜索

get_place

否

可用性和预计送达时间

get_menu

否

可选的本地查询/分类过滤

get_cart

否

列出购物车或加载一个最新购物车

get_active_orders

否

清理后的缓存活动订单和监控器健康状态

get_order_status

否

缓存状态或显式只读刷新

get_order_events

否

排他序列游标之后的持久事件

add_to_cart

是

验证当前菜单、必选选项和可用性;支持标记为成人的商品

update_cart_item

是

仅限显式用户请求

remove_cart_item

是,破坏性

绝不做自动优化

server_capabilities

否

报告已启用的安全边界

每个变更操作都接受可选的 UUID operationId;在十分钟内重复相同操作会返回相同的进程内结果,而不会重复上游变更。使用相同 ID 但不同参数会被拒绝。不安全的 upstream 请求绝不会自动重试。如果响应丢失或超时,工具返回 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 轮询,请保留 get_order_events 返回的 nextSequence,并将其作为 afterSequence 传回。该游标是排他的,读取不会确认其他客户端的事件。

启用购物车变更

在初始部署期间保持禁用:

YANDEX_EATS_ENABLE_MUTATIONS=false

首先验证 auth_status、search、get_menu 和 get_cart。然后准备一个没有必选配料的一次性餐厅商品,启用该标志,并执行一次受控的 add_to_cart,随后执行 get_cart,并仅移除新创建的 cartItemId。

YANDEX_EATS_ENABLE_MUTATIONS=true

更改标志后重启容器。切勿将 clear-cart 用作测试;它有意不对外暴露。

如果 auth_status 报告 AUTH_EXPIRED 或 needsRefresh: true,请用新的单行 Cookie 值替换 secrets/yandex_eats_cookie,并重新创建 Docker secret 挂载:

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

在挂载的 secret 就地更新的非 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。

  • 日志包含端点、状态、持续时间和 Yandex 关联 ID。请求/响应体、cookie、授权标头、会话 ID、电话、地址和支付字段会被脱敏或不记录。

  • AUTH_NOT_CONFIGURED:cookie secret 缺失或不可读。

  • 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