基于 麦当劳 MCP 的联名/限定周边 Skill:不错过任何一次联名发售,用最低成本拿到它,并在开抢瞬间自动下单。
麦当劳的联名周边(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 元 |
麦麦限定雷达把这三件事一次解决:扫描发售日历 → 算出最低成本组合 → 到点自动抢。
从 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 预热态最值得关注),并对比积分余额给出 ✅/❌ 结论。
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 只接受「城市 + 地标」,不接受经纬度。用户不同意就绝不调用定位工具,也不臆造位置。
核心设计原则:绝不用菜单价格做加减法。
优惠规则(随单购、满减、券的适用范围)只有 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
除了总价,还报告 周边边际成本(总价 − 凑单品价),回答"这个周边到底让我多花了多少"。
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 执行结果 │
└──────────────────────────────────┘
- 申请 麦当劳 MCP Token
- 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 521954python3 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 丝巾)
本项目为麦当劳程序员节创意开发大赛参赛作品,由参赛者独立开发,非麦当劳官方产品。 项目输出仅供参考;餐品信息、价格、库存及供应状态以麦当劳官方渠道的实时结果为准。