Skip to content

About

No description, website, or topics provided.

Resources

Stars

93 stars

Watchers

0 watching

Forks

Repository files navigation

🍔 麦麦限定雷达 · MCD Grail Radar

基于 麦当劳 MCP 的联名/限定周边 Skill:不错过任何一次联名发售,用最低成本拿到它,并在开抢瞬间自动下单。

MCP Python Deps Contest


💡 它解决了什么真实问题

麦当劳的联名周边(PEACEMINUSONE × G-DRAGON 棒球帽、联名徽章、限定丝巾……)有一个共同的发售形态:

任意餐品消费 + 39.9 元即可得 10:45 开售,APP 专享积分抢先购 10:40 开抢,限量发售

这给喜欢这些周边的人挖了三个坑:

坑 后果
记不住开抢时刻 10:45 才想起来,限量款已经没了;更别说还有个早 5 分钟的 10:40 积分抢先购
凑单凑贵了 随手买个 30 元的套餐去凑,比买 5 元的圆筒冰淇淋多花 25 元,拿到的周边一模一样
被"优惠"骗了 周边挂着「随单购麦金卡优惠」,单价 39.9 → 34.9 看着省 5 元,实际购物车被塞了一张 9.9 元的麦金卡,总价反涨 4.9 元

麦麦限定雷达把这三件事一次解决:扫描发售日历 → 算出最低成本组合 → 到点自动抢。


✨ 四大能力

1️⃣ 雷达扫描 — 联名/限定推送与倒计时

从 campaign-calendar 的 20+ 条月度活动中,用关键词模型筛出联名/限定,并抽取四个发售信号: 开售时刻 / 积分抢先购时刻 / 加价门槛 / 限购量。

▸ 2026-10-15  麦当劳 X PEACEMINUSONE
    开售 2026-10-15 10:45 | 积分抢先购 2026-10-15 10:40 | 任意消费 +39.9 元
    ⏳ 距开抢 4天17小时10分

▸ 2026-10-22  第三周周边即将开售✨限定主题丝巾!
    开售 2026-10-22 10:45 | 积分抢先购 2026-10-22 10:40 | 任意消费 +39.9 元
    ⏳ 距开抢 11天17小时10分

倒计时基于 now-time-info 的服务器时间,不是本地时钟——抢购是分钟级的胜负,差几秒就前功尽弃。 同时扫描积分商城周边(status=5 预热态最值得关注),并对比积分余额给出 ✅/❌ 结论。

2️⃣ 门店定位 — 推荐最近的可用门店

python3 grail.py stores --city 深圳市 --keyword 西丽 --auto --verify --save

最近的那家可能已经打烊。 所以雷达不做简单的距离排序,而是先做三级可用性判定:

级别 判定条件 含义
🟢 ready 营业中 + 在营业时段内 + 落在今天某个可预约时段 现在就能下单
🟡 open 营业中,但当前不在可预约时段 能到店,线上预约要等
🔴 closed 已打烊 / 不在营业时段 跳过

排序键是 可用性级别优先、距离次之 —— 宁可推荐 800m 外营业的店,也不推荐 100m 外打烊的店。

 1. 🟢 麦当劳深圳西丽银座餐厅   立即可下单
    距离 139 m | 到店自提 | 营业中且当前处于可预约时段
    营业时间: 07:00 - 23:00
 2. 🟡 麦当劳深圳乐普大厦餐厅   营业但暂不可预约
    ...

✅ 推荐门店(最近且可用)
  麦当劳深圳西丽银座餐厅
  距离 139 m | 到店自提 | 现在即可下单
  storeCode=1420891
  点餐复核: 可售餐品 118 项

细节:--auto 同时纳入得来速门店(多为 24 小时,夜间是重要补充); --verify 用真实 query-meals 复核可点餐性,不通过自动顺位降级; 营业时间跨零点(22:00-07:00)已正确处理。

🔒 隐私前置:MCP 只接受「城市 + 地标」,不接受经纬度。用户不同意就绝不调用定位工具,也不臆造位置。

3️⃣ 最低成本组合 — 枚举 + 真实试价

核心设计原则:绝不用菜单价格做加减法。 优惠规则(随单购、满减、券的适用范围)只有 calculate-price 知道,所以本 Skill 的做法是 枚举「凑单品 × 随单购卡 × 优惠券」的笛卡尔积,逐个真实试价,比真实总价。

候选凑单品 5 个: 圆筒冰淇淋¥5.0, 灰焰圆筒¥5.0, 脆薯饼¥8.0, 派¥8.0, 奶香大米派¥9.0

#    总价     凑单品        凑单价   省     方案
1    44.90   圆筒冰淇淋     5.00   0.00   不带卡/无券   ← 最优
2    44.90   灰焰圆筒       5.00   0.00   不带卡/无券
3    47.90   脆薯饼         8.00   0.00   不带卡/无券
6    49.80   圆筒冰淇淋     5.00   5.00   随单购卡      ← 陷阱!

实付总价: ¥44.90      周边到手边际成本: ¥39.90

⚠️  反直觉提示:「随单购麦金卡」让周边单价降了,但总价反而 +4.90 元
    不带卡 ¥44.90  vs  带卡 ¥49.80

除了总价,还报告 周边边际成本(总价 − 凑单品价),回答"这个周边到底让我多花了多少"。

4️⃣ 预约 + 自动抢购

python3 grail.py book --store 1420891 --item 521954 --side 515837 --at "2026-10-15 10:40"
python3 grail.py watch --lead 90        # 提前 90 秒预热,10:40:00 准时发单

为什么不用 reservationDate 预约? 门店的 reservationTimeOptions 只有「早餐 05:14-10:15 / 午餐 10:44-14:15」这种粗粒度时段, 10:40 不在任何时段内。预约 API 救不了精确抢购,所以抢购走「任务卡 + 守护进程到点发即时单」, 预约只用于"明天 12:00 取餐"这类场景。


🏗 架构

                        ┌──────────────────────────────────┐
                        │   麦当劳 MCP  mcp.mcd.cn (35 tools)│
                        └───────────────┬──────────────────┘
                                        │ Streamable HTTP + Bearer Token
                        ┌───────────────▼──────────────────┐
                        │  scripts/mcd_mcp.py               │
                        │  零依赖 MCP 客户端(仅标准库)      │
                        │  · initialize / tools/call         │
                        │  · SSE + JSON 双解析               │
                        │  · 副作用工具硬闸门                 │
                        └───────────────┬──────────────────┘
                                        │
        ┌───────────────┬───────────────┼───────────────┬────────────────┐
        ▼               ▼               ▼               ▼                │
   ┌────────┐    ┌───────────┐   ┌───────────┐   ┌───────────┐          │
   │ scan   │    │  stores   │   │   plan    │   │book/watch │          │
   │ 雷达   │───▶│  定位     │──▶│ 成本求解  │──▶│ 预约/抢购 │          │
   └────────┘    └───────────┘   └───────────┘   └─────┬─────┘          │
   calendar      nearby-stores    meals          create-order            │
   mall-products query-meals      calculate-price ──────┘                │
   my-account    ───────────────────────────────────────────────────────┘
                                        │
                        ┌───────────────▼──────────────────┐
                        │  ~/.mcd-grail-radar/              │
                        │  queue.json  抢购任务卡队列        │
                        │  plan.json   最优方案             │
                        │  radar.json  扫描结果             │
                        │  results.json 执行结果            │
                        └──────────────────────────────────┘

🚀 安装

前置条件

  1. 申请 麦当劳 MCP Token
  2. Python 3.8+(无第三方依赖,pip install 都不需要)

三步跑起来

git clone https://github.com/<你的用户名>/mcd-grail-radar.git
cd mcd-grail-radar

# 方式 A:已有 WorkBuddy mcd-mcp 连接器 → 自动发现 Token,无需配置
python3 scripts/mcd_mcp.py        # 自检:打印服务器时间 + 35 个工具 + 积分余额

# 方式 B:直接用环境变量
export MCD_MCP_TOKEN=<你的 MCP Token>
python3 scripts/mcd_mcp.py
在 WorkBuddy / Claude / 其他 Agent 中作为 Skill 使用

把整个目录放到 skills 目录下即可(WorkBuddy:左侧栏「专家·技能·连接器」→ 技能):

~/.workbuddy/skills/mcd-grail-radar/SKILL.md

SKILL.md 描述了完整的四流程工作流,Agent 会自行决定调用哪个 MCP 工具, 也可在需要精确抢购时调用 scripts/grail.py。


📖 使用示例

场景一:这个月有什么联名?

python3 scripts/grail.py scan --store 1420891

输出发售日历(含倒计时)、积分商城周边(含积分够不够)、门店菜单里的周边商品。

场景二:我在深圳西丽,我要那个棒球帽

python3 scripts/grail.py stores --city 深圳市 --keyword 西丽 --auto --verify --save
python3 scripts/grail.py plan --store 1420891 --item 521954

场景三:10 月 15 日 10:40 的徽章,帮我抢

python3 scripts/grail.py book --store 1420891 --item 521954 --side 515837 \
        --at "2026-10-15 10:40" --note "GD联名徽章 积分抢先购"

python3 scripts/grail.py watch --lead 90        # 挂机等到 10:38:30 预热

场景四:我现在就要下单

python3 scripts/grail.py grab --task T-202610151040
# 会先展示门店/商品/金额,输入 yes 才真正下单

全部命令

命令 作用
scan 扫描联名/限定活动与周边,输出发售日历 + 倒计时
stores 定位并推荐最近的可用门店(三级可用性判定,支持 --auto/--verify)
plan 枚举组合并真实试价,给出最低成本方案 + 陷阱告警
book 生成抢购任务卡写入队列
watch 守护进程,到点自动预热并下单(--dry-run 只试价)
grab 立即按任务卡下单(需确认)
status 查看队列/结果,--order <订单号> 查订单状态
points 积分余额与可兑换周边
tools 列出 MCP 全部 35 个工具

🎯 目标用户

用户 痛点 雷达给的价值
联名周边收藏者 限量款总是抢不到,发售时间记不住 发售日历 + 抢先购时刻 + 倒计时 + 到点自动抢
精打细算的麦门信徒 想拿周边但不想当冤大头 最低成本组合 + 周边边际成本 + 随单购陷阱告警
麦金卡/积分玩家 积分不知道能换什么、够不够 积分余额对比、预热态商品提示、兑换可行性判断
开发者 / Agent 用户 想让 Agent 帮自己抢限量 任务卡 + 守护进程,可嵌进任何自动化编排

🔒 安全与隐私

  • 零凭据入库:Token 只从环境变量或本地 ~/.workbuddy/mcp.json 读取,仓库中只有占位符
  • 副作用硬闸门:create-order / auto-bind-coupons / draw-lottery / cancel-order 等 在客户端层被拦截,必须显式传 allow_side_effects=True 才能调用
  • 下单必确认:默认交互式确认,展示门店/商品/金额后才执行
  • 不代付:create-order 返回的是待支付订单,支付链接交给用户本人完成
  • 位置需授权:用户不主动提供城市/地标,绝不调用定位工具
  • 失败不粉饰:限量售罄就如实说售罄,绝不虚构"已抢到"

📁 目录结构

mcd-grail-radar/
├── README.md                     项目介绍
├── CONTEST_DECLARATION.md        参赛声明(官方原文,未改动)
├── MCP_INTEGRATION.md            MCP 集成说明:工具、调用流程、业务价值
├── workbuddy.md                  WorkBuddy 开发对话上下文
├── SKILL.md                      Skill 主体:四流程工作流
├── LICENSE
├── .gitignore
├── .env.example
├── scripts/
│   ├── mcd_mcp.py                零依赖 MCP Streamable HTTP 客户端
│   └── grail.py                  CLI 主程序(scan/stores/plan/book/watch/grab…)
├── references/
│   ├── radar-rules.md            联名识别规则与发售时刻抽取
│   ├── store-selection.md        门店可用性三级判定与最近可用门店推荐
│   ├── cost-playbook.md          成本优化手册与随单购陷阱
│   ├── ordering-and-safety.md    下单参数决策树与安全护栏
│   └── tool-matrix.md            35 个 MCP 工具 × 场景映射
└── examples/
    └── gd-peaceminusone.md       真实案例:麦当劳 × PEACEMINUSONE 三周连发

🧪 实测数据

本项目所有示例输出均来自真实 MCP 调用,非模拟数据:

  • 抓取时间:2026-10-10 17:28 – 17:36 (GMT+8)
  • 门店:1420891 麦当劳深圳西丽银座餐厅
  • 命中活动:麦当劳 × PEACEMINUSONE(10/08 棒球帽、10/15 徽章、10/22 丝巾)

⚖️ 免责声明

本项目为麦当劳程序员节创意开发大赛参赛作品,由参赛者独立开发,非麦当劳官方产品。 项目输出仅供参考;餐品信息、价格、库存及供应状态以麦当劳官方渠道的实时结果为准。

📄 License

MIT

About

No description, website, or topics provided.

Resources

Stars

93 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages