基于 麦当劳中国 MCP(McDonald's China MCP) 的会员优惠资产经营助手。 算盘算这一单,罗盘算这一季。 把「活动日历 × 券资产 × 积分账户」当成一支会过期、会错配、会静默失效的资产池来打理。
麦当劳程序员节创意开发大赛参赛作品 · 非麦当劳官方产品。
📊 实跑资产台面:docs/asset-board.html ·
实测发现:docs/FINDINGS.md ·
指标口径:docs/METRICS.md ·
MCP 集成:MCP_INTEGRATION.md
用户问「我那 800 积分该换什么?这周的券要过期了吗?这个东西现在买还是再等等?」,它把散落在积分账户、券包、活动日历、麦麦商城四个角落的数据拉齐,输出一页可执行的资产台面。
实测(2026-10-16,真实 MCP 返回):
积分占用率跨度 5.0×
最省 #1 50 分 / ¥ 6.90 = 7.25 分/元 6.9元香芋派/菠萝派任选
最贵 #7 800 分 / ¥21.90 = 36.53 分/元 21.9元巨无霸可乐组合
同样的积分,兑不同商品得到的消费额最多差 5 倍 —— 而这不是折扣力度的差异,是积分定价档位的差异。
麦当劳的优惠不是一次性折扣,而是三支会随时间变化的资产:
| 资产 | 失效方式 | 典型损失 |
|---|---|---|
| 优惠券 | 到期作废、渠道不匹配、商品不匹配 | 券摆在卡包里没领、领了没用、用错渠道 |
| 积分 | 本月/下月过期、兑换档位错配、只能换券不能抵现 | 过期归零;或同一份权益多花一倍积分 |
| 活动窗口 | 只在新品日开卖、只卖 3 天、下周三才上线 | 买早了没优惠,等过头了活动结束 |
用户的损失大多不是「不会比价」,而是「券过期了」「积分兑错了档」「要买的东西明天才开始的活动」。
罗盘的定位就在这个时间轴上:它不下单比价,它经营资产。
| 角色 | 职责 |
|---|---|
| 麦当劳 MCP | 积分账户、券池、活动日历、在售商品、兑换档位、抽奖资源判定 —— 唯一数据与数值来源 |
| 麦麦罗盘 | 归一化 → 分桶 → 排序 → 标记冲突与缺口 → 生成行动清单 —— 只做编排与确定性推导 |
| 用户 | 决定想吃什么、决定要不要抽奖、确认领券与兑换 |
这不是一个「转发请求」的壳,也不是一个「枚举组合取最小」的比价器。它回答的是另外一组问题:
| 点餐比价类助手 | 麦麦罗盘 | |
|---|---|---|
| 时点 | 下单那一刻 | 下单之前的一周 / 一个月 |
| 单位 | 一单 | 一个账户 |
| 输入 | 「我要 A+B+C,怎么最省」 | 「我手里有什么,先动哪个」 |
| 核心动作 | 枚举候选组合 → 逐组实算 → 取最小 | 拉齐资产 → 按失效紧迫度与积分占用率排序 → 标记冲突 |
| 输出 | 一个最优组合 | 一页资产台面 + 行动清单 |
| 典型问题 | 「同样的东西怎么少花 ¥33」 | 「800 积分换什么最值」「这周哪张券会过期」 |
两者可以串联:罗盘先解决「什么时候买、用哪个渠道的券」,再交给比价逻辑解决「这一单怎么拼」。
读 campaign-calendar 与 now-time-info,把日期 → 活动的扁平时间线归一化成活动窗口。
实测中「麦当劳 X PEACEMINUSONE」在 10/13、10/15、10/16 各出现一次 —— 直接罗列会让用户以为有三场活动。
罗盘按标题归一化合并日期,并从活动正文解析有效期区间(如 10月8日至10月21日):
原始 29 条日期条目压缩为 16 个活动(45% 为重复出现)
活动窗口 │ 剩 5 天 │ 超值9.9元早餐两件套陪你开工啦
读 available-coupons、auto-bind-coupons、query-my-coupons、query-my-account。
实测账户是一个零券零积分的冷启动状态(可用 0 分 / 累计 86 分 / 已过期 86 分,卡包为空但有 7 张可领券)。 罗盘的判断是明确的:第一动作是领券,不是比价。
券包 │ 持有 0 张 · 可领取 7 张 → 冷启动:第一动作应是领券
读 mall-points-products + mall-product-detail。
先分池:同一个工具返回两个完全不同的池子,混在一起排序会得出荒谬结论。
| pool | shopId | 特征 | 数量(实测) |
|---|---|---|---|
| 积分兑券池 | 2 | point > 0、price = 0 |
24 项 |
| 主题活动池 | 5 | point = 0、price > 0(现金购买派对/品鉴会) |
27 项 |
再排序:按 积分占用率 = points ÷ extTradePrice 升序。这是纯 MCP 口径,无需任何外部假设。
最后求最优解:在用户给定的「想要清单」上跑有界背包精确解。
读 query-lottery-info、query-my-prizes。
抽奖 │ 麦麦积分抽奖(进行中)| 资源不足:积分不足
门槛 24 分 · 可用 0 分
缺口:还差 24 分
这一层最重要的设计取舍:不计算中奖期望值。
query-lottery-info 只返回奖品名称与类型,不含任何中奖概率或权重字段。任何形如
「期望收益 = Σ 奖品价值 × 概率」的算法都只能是编造。因此罗盘只复述服务端自己的
drawDecision 资源判定,把概率决策交还给 MCP。
不做加权合成分。 券、积分、活动窗口三者的价值量纲不同(券没有到期日、积分没有单价、 活动没有折扣力度),强行合成一个「净值总分」只会得到看起来精确、实际上编造的数字。 因此台面采用三段式:
| 段 | 含义 | 内容 |
|---|---|---|
| ① 立刻可动 | 今天就能执行的、确定的事 | 账户快照、可领券清单、积分占用率排行、兑换方案 |
| ② 即将失效 | 有明确倒计时、会随时间贬值的事 | 临期积分、兑期将尽商品、活动窗口倒计时 |
| ③ 需人工确认 | 数据有冲突或有缺口、罗盘不能替你决定的事 | 同名冲突组、数据缺口声明 |
| 项目 | 性质 | 说明 |
|---|---|---|
| 积分账户、券池、活动日历、商城商品 | ✅ 真实 | 2026-10-16 通过 McD MCP 实跑返回,快照见 engine/samples/ |
| 积分占用率(7.25 ~ 36.53 分/元) | ✅ 真实 | 全部由 points ÷ skuList[].extTradePrice 得出,可直接复算 |
| 「900 分兑 5 件、券后 ¥53.50」 | ✅ 真实 | 有界背包精确解,单测锁定 |
| 同名冲突 spu 17076 / 17077 权益不同 | ✅ 真实 | 由 mall-product-detail 的 note【兑换内容】确认 |
status=2 却有 70.8% 不可兑 |
✅ 真实 | 24 项里 17 项兑期已关 |
| 日历压缩率 45% | ✅ 真实 | 29 条日期条目 → 16 个活动 |
| 抽奖期望收益 | ❌ 不计算 | MCP 不提供概率字段,计算即编造 |
| 「省了多少钱」 | 🟡 需扩展 | 商城数据只有券后价,无常规菜单价;须另接 query-meals |
cp -r skills/mcd-compass <你的 Agent 技能目录>/Skill 只依赖 MCP 原子工具,不引入任何私有 SDK 或第三方依赖,任何支持 MCP 的 Agent 都能直接跑。
- 申请麦当劳 MCP Token:前往 https://open.mcd.cn/mcp,登录后在控制台激活并复制 Token。
- 接入 MCP:把
mcp-config.example.json的内容填进你的 Agent 的 MCP 连接配置, 将${MCD_MCP_TOKEN}替换为真实 Token,保存并启用。 - 装上技能:把
skills/mcd-compass/复制进 Agent 的技能目录。 装好后直接说「我的积分该换什么」「这周有什么券要过期」「现在买还是等」即可触发。
引擎零第三方依赖,只用 Python 标准库(3.10+)。
cd engine
# 用仓库内真实快照跑一遍资产台面
python -m mcd_compass --samples ./samples
# 输出 JSON(便于接入其他程序)
python -m mcd_compass --samples ./samples --format json
# 生成可视化报告
python -m mcd_compass --samples ./samples --format html --out ../docs/asset-board.html
# 跑单元测试(44 项)
python -m unittest discover -s tests -t .引擎的作用是把口径固化下来:积分占用率怎么算、可兑性怎么判、并列解怎么裁决, 全部写成可测试的代码,而不是依赖模型每次即兴发挥。
- 积分囤积者:攒了几百分到几千分,从没系统兑过,不知道换什么不亏。
- 券多到用不完的人:卡包一堆券,不知道哪张快过期、哪张其实用不上。
- 价格敏感型上班族:想知道「早餐 9.9 的活动还剩几天」「下周三才上新,要不要等」。
- 麦门会员:手上有券有积分,需要一个台账而不是又一条推送。
- 分池纪律:同一个商城接口返回两个语义完全不同的池子,先分池再排序,避免「现金商品和积分商品比积分效率」的荒谬结论。
- 可兑性三因素判定:
status单独不可信 —— 实测 24 项里 17 项标着「上架」但兑期已关。罗盘一律用status × now × [upTime, downTime]交叉判定。 - 列表 / 详情字段互补:列表接口在积分兑券池里
price恒为 0,真实券后价extTradePrice只在详情接口;详情接口又没有status。正确做法是「列表做骨架、详情做覆盖」,整体替换会让所有商品被判为已下架。 - 显式消费约束:朴素的「按占用率吃满」会算出「900 分兑 18 个派」。罗盘把「想要清单」作为输入,在其上求有界背包精确解。
- 三级裁决、零权重:件数最多 → 积分最少 → 券后金额最大。三级全部可解释,没有加权合成分。
- 拒绝编造:抽奖不算 EV、券不算临期损耗、积分不折算成元。缺失的字段变成一条明确的「数据缺口」,而不是一个猜测值。
- 零私有依赖:Skill 只调 MCP 原子工具;引擎只用 Python 标准库。
.
├── README.md # 本文件
├── CONTEST_DECLARATION.md # 参赛声明(官方原文,未改动)
├── MCP_INTEGRATION.md # MCP 集成:Tool 清单 / 调用流程 / 业务价值
├── workbuddy.md # WorkBuddy 开发对话上下文
├── mcp-config.example.json # 脱敏 MCP 配置(仅环境变量占位符)
├── LICENSE
├── docs/
│ ├── ARCHITECTURE.md # 架构与决策流程
│ ├── METRICS.md # 指标口径定义(含诚实性边界)
│ ├── FINDINGS.md # 实跑发现(含证据与复现方式)
│ ├── SCENARIOS.md # 使用场景与话术
│ └── asset-board.html # 实跑资产台面(真实数据渲染)
├── skills/mcd-compass/ # ← Skill 本体
│ ├── SKILL.md
│ ├── references/ # timing / ledger / points / lottery / execute
│ └── assets/ # 字段契约 / 示例
└── engine/ # ← 确定性计算引擎(零第三方依赖)
├── mcd_compass/
│ ├── models.py # 字段解析、可兑状态机
│ ├── points.py # 分池、占用率、有界背包
│ ├── ledger.py # 券台账、账户视图、数据缺口
│ ├── timing.py # 日历归一化、窗口解析
│ ├── lottery.py # 资源决策门(明确不产出 EV)
│ ├── board.py # 资产台面组装
│ ├── render.py # HTML 渲染
│ └── cli.py # 命令行入口
├── samples/ # 真实 MCP 返回快照(凭证已脱敏)
└── tests/test_metrics.py # 44 项单测
本项目为麦当劳程序员节创意开发大赛参赛作品,由参赛者独立开发,非麦当劳官方产品。 项目输出仅供参考,不构成医疗、营养或其他专业建议;餐品信息、积分、价格及供应状态以麦当劳官方渠道的实时结果为准。 使用麦当劳 MCP 服务须遵守其《使用条款》与《麦当劳 MCP 服务规则》。
MIT License。
中文:麦当劳 · 麦门 · 麦当劳积分 · 积分兑换 · 积分商城 · 麦当劳优惠券 · 券过期 · 麦麦省 · 活动日历 · 麦当劳抽奖 · 麦当劳省钱 · 资产台账 · 积分性价比 · 麦当劳程序员节创意开发大赛 · AI 智能体技能
English: McDonald's / McDonalds China / MCD · MCP (Model Context Protocol) · mcd-mcp · points optimizer · loyalty asset ledger · coupon expiry · reward ROI · agent skill · AI agent
推荐搜索词:麦当劳 MCP · mcd mcp · 麦当劳 积分 换什么 · 麦当劳 券 过期