mcd-coupon-strategist README

The project's README from github.com/Syenzzz/mcd-coupon-strategist. As one markdown file: README.md.

麦麦省钱助手 — mcd-coupon-strategist

tests deps Python Token License MCP

市面上的省钱工具都在回答「这一单怎么点最省」。 我们多回答一个问题:「你正在白扔多少钱?」

快速体验 · 真实例子 · 核心亮点 · 架构 · 设计原则


同一个账号,两种问法:省这一单 vs 你正在白扔多少钱

基于麦当劳中国 MCP 打造的点餐成本助手,做两件事:

短期 —— 这一单怎么点最省

长期 —— 你白扔了多少钱

🍟 一次点餐平均省 💸 沉默成本发现 🧮 价格来源 🔑 使用门槛
3 ~ 13 元 一次几十元 MCP 真实计价 无需 Token 即可跑测试

快速体验(30 秒)

git clone https://github.com/Ayzzz123/mcd-coupon-strategist.git
cd mcd-coupon-strategist
python demo.py

零第三方依赖(只用标准库)、不需要 MCP Token、不联网。 demo.py 读取 fixtures/ 里录制的真实 MCP 响应,跑通两条链路:

链路 A · 单次省钱
门店:麦当劳南京东城汇餐厅(1990622)(到店自取)
需求:想吃「汉堡 + 薯条 + 可乐」,预算 40 元

推荐方案
  组合:套餐:麦辣鸡腿汉堡三件套
  实付 33.5 元

超预算(仅供对照)
  · 单点:麦辣鸡腿汉堡 + 薯条 + 可乐 —— 实付 46.5 元,比推荐方案贵 13.0 元

  → 这个价差来自套餐结构本身,不需要任何优惠券 —— 这是最稳的省法。

链路 B · 消费审计
券:当前门店可用 2 张,卡包里持有 9 张 —— 沉默成本按后者算

【一】券的沉默成本
  你的券有风险:9 张将在 7 天内陆续过期(面值 21.1 元);
  另有 7 张的券面价值无法从菜单推算,未计入面值。

  优先处理这几张:
    · 大脆鸡扒堡早餐两件套  价值未知(菜单中无对应商品,未估算)  还剩 1 天
      仅 周四 05:00-10:29 可用 · 到店专用
    · 中薯条买一送一  省 14.5 元  还剩 2 天
      周一至周五 可用
    · 王牌小食拼  省 6.6 元  还剩 2 天
      周一至周五 可用
    …… 另有 6 张(为控制篇幅未展开)

【二】消费习惯
  订单数:8 单(其中 2 笔 0 元)
  总支出:103.5 元
  客单价:17.25 元(按 6 笔有支出的订单计算)

  时段分布:
    早餐 · 4 单(2 笔有支出) · 支出 58.9 元 · 样本不足 3 笔,未估算客单价
    下午 · 1 单 · 支出 1.0 元 · 样本不足 3 笔,未估算客单价
    晚市 · 3 单 · 支出 43.6 元 · 客单价 14.53 元

本月最该做的事:先把 21.1 元的券用掉,
这笔钱不用就是纯损失。
另外 7 张券同样要过期,但券面价值还没算出来 —— 看清它们的时间和限制,别让它们悄悄走掉。

券包体检
  名义总额 ¥21.1(9 张) → 可实现 ¥21.1
【顺手可用】2 张 · 净省 ¥21.1
  · 中薯条买一送一    净省 14.5 元    还剩 2 天
  · 王牌小食拼    净省 6.6 元    还剩 2 天

【无法判断】7 张
  · 大脆鸡扒堡早餐两件套    菜单中无对应商品,未估算    还剩 1 天
  ……

用券排程
  窗口 2 天(券全部过期前) · 点单频率 0.089 单/天
  → 这期间你大概只会点 1 单,最多用掉 1 张券
    第 1 单 → 中薯条买一送一    净赚 14.5 元
  排不上(1 张 · 可实现 ¥6.6)
    · 王牌小食拼    净价值 6.6 元    还剩 2 天
  券包可实现 ¥21.1,但机会只够用掉 ¥14.5 —— 剩下 ¥6.6 注定随券过期浪费。

  麦麦省共 7 条券记录:已领取 4 条、可领取 3 条(同名去重后)

可领取的券
  你还有 3 张券没领(领取免费)

【无法判断】3 张
  · 免费脆薯饼    菜单中无对应商品,未估算
  · 人气麦旋风买一送一    菜单中无对应商品,未估算
  · 9.9元中杯冰美式    菜单中无对应商品,未估算

  3 张的价值无法从券面推算 —— 我们不猜;领取后可以用「带券计价」实测出精确价值。
  另外别忘了机会有限:上面排程算出,窗口内的点单机会已被你手里的券占满 ——
  券不是越多越好,用得上才算数。

活动节奏
  10 月日历里有 14 天有活动:已过 2 天 · 今天 1 天 · 之后 11 天
  最近一次:10月15日(明天)
    · 麦当劳 X PEACEMINUSONE

  说明:「有活动」不等于「更便宜」。
       活动内容里的价格是营销文案,无法核实,本工具不拿它做计算。
       所以这里只给时间线,值不值得等要你结合活动内容自己判断。

  你手里的券撑得到 10月15日 吗(纯日期比较):
    · 中薯条买一送一    10月16日到期(还剩 2 天)    → 等得到
    · 王牌小食拼    10月16日到期(还剩 2 天)    → 等得到

积分体检
  可用 376.3 积分 · 累计 581.3 · 已用 188
  ⚠️ 已过期 17 积分 —— 已发生的损失

  积分商城 24 个商品:9 个仍在兑换期内(其中 6 个换得起),15 个已过兑换期

【现在就能换】
  · 6.9元可乐麦炫酷    需 50 积分    还剩 77 天
  · 3.9元圆筒冰淇淋    需 100 积分    还剩 77 天
  · 16.9元2份麦辣鸡翅    需 200 积分    还剩 77 天

【同款不同价 · 换便宜的那个】
  · 10.9元指定小食任选 —— 一个要 200 积分,一个只要 100 积分,差 100

【已过兑换期】15 个
  这些商品的接口状态仍标着「上架」,但兑换截止日已经过去 ——
  只看状态会以为还能换。

上面那 3 张全是「价值未知」不是解析失败,是数据事实:这份菜单快照采集于 21:59,而脆薯饼、冰美式并不在该时段的菜单里。我们没有为了让 demo 好看 而往菜单里塞一个能匹配上的商品名 —— 原因与验证方式见 fixtures/README.md。

上面这段输出为便于阅读做了删减(省略了分隔线、以及「积分还不够」等重复项), 完整输出请直接跑 python demo.py。

真实运行时,这些响应由 MCP 实时返回,业务逻辑一字未改 —— 所以这个 demo 同时也是整套数据流水线的端到端回归测试。 数据来源与采集时间见 fixtures/README.md。


一个真实例子

同一个账号,同一个门店,两种问法:

问「今天怎么点最省」 →

推荐方案
  组合:套餐:麦辣鸡腿汉堡三件套
  实付 33.5 元

超预算(仅供对照)
  · 单点:麦辣鸡腿汉堡 + 薯条 + 可乐 —— 实付 46.5 元,比推荐方案贵 13.0 元

同样吃到「汉堡 + 薯条 + 可乐」:套餐 33.5 元,单点 46.5 元,差 13 元 —— 而单点那条已经超出 40 元预算,所以它是对照,不是备选。

问「我最近在麦当劳花了多少」 →

【一】券的沉默成本
  你的券有风险:9 张将在 7 天内陆续过期(面值 21.1 元);
  另有 7 张的券面价值无法从菜单推算,未计入面值。
  优先处理这几张:
    · 大脆鸡扒堡早餐两件套  价值未知  还剩 1 天
      仅 周四 05:00-10:29 可用 · 到店专用
    · 中薯条买一送一  省 14.5 元  还剩 2 天
      周一至周五 可用
    · 王牌小食拼  省 6.6 元  还剩 2 天
      周一至周五 可用

【二】消费习惯
  订单数:8 单(其中 2 笔 0 元)
  总支出:103.5 元
  客单价:17.25 元(按 6 笔有支出的订单计算)
  洞察:
    · 近 3 个月你在麦当劳共消费 103.5 元,其中 6 笔有实际支出,平均每单 17.25 元。
    · 另有 2 笔 0 元订单(多为券核销或积分兑换)—— 不计入客单价,但算作一次点单机会。
    · 还有 2 笔「部分退款」订单 —— 接口不返回退款金额,无法确认 realTotalAmount 是退款前还是退款后的口径,上面的支出可能略偏高。

本月最该做的事:先把 21.1 元的券用掉,这笔钱不用就是纯损失。

第二个问题的价值不在这一次的数字大小,而在于它是持续性的: 第一个问题每单只影响一次;第二个问题每次都会告诉你又有什么在悄悄过期 —— 而这是一次性工具永远给不出的答案。

注意「王牌小食拼」那个 6.6 元:这张券的标题里没有数字,菜单里也没有对应商品, 靠反查原价这条路根本走不通。它的价值是把券的商品码传给 calculate-price 实测出来的 —— 而不是估一个「差不多」的数。

这两段输出与 python demo.py 的结果逐字一致 —— 你可以自己跑一遍验证。


我们做对了什么别人没做的

省钱类工具已经很多了,多到这次比赛报名里就有好几支在做同一件事。所以先说清楚我们的不同点到底在哪:

常见做法 我们的做法 差别
让模型估算价格 所有金额由 MCP calculate-price 回填 模型给不出真实价格,只能猜
只说「推荐买这个」 说清省在哪:哪张券、哪个套餐、加了什么 用户才能学会,而不是依赖工具
为用券鼓励凑单 主动检测凑单陷阱并劝退 凑单常常反而更贵
只看这一单 同时看历史订单 + 券存量 一次性工具看不到长期结构
券是「可用资源」 券是有保质期的钱 过期即损失,这是本项目独有的视角
积分是「攒够就换」 积分也是有保质期的钱,且**status 不等于可兑换** 实测 24 个兑换品里 15 个已过兑换期,接口状态却仍标「上架」
「有券就建议用」 算净价值,该劝退就劝退 名义 ¥150 的券包,可实现的可能只有 ¥60
教你「怎么用券」 还提醒你**「还有券没领」** 未领取的券是唯一一种零成本的省钱来源
用「平均客单价」一刀切 分清消费口径 / 点单口径、全时段 / 分时段 口径错,后面所有券的判定都会跟着偏
券 = 「门店里能用的」 券分两种口径:门店可用的 vs 卡包里持有的 实测差 2 张 vs 9 张 —— 拿门店券算沉默成本,会把 7 张即将过期的早餐券整个漏掉
拿活动文案里的价格当依据 只取活动的日期与标题,文案价格一概不用 营销文案无法核实,拿它算钱等于编
—— 券的沉默成本 别处见不到这个指标

差异化内核:别处都在帮用户花钱,我们在帮用户止损。 下单前省的那几块钱是「收益」,快过期的券是「损失」—— 人对损失的敏感度是对收益的两倍,而这个数字从来没人给他算过。

关键实现:怎么知道一张券真值多少钱

「券的沉默成本」要成立,前提是得知道每张券真实值多少钱。而 MCP 返回的券通常只有名字,没有面值:

「中薯条买一送一」  →  值多少钱?
「9.9元中杯冰美式」 →  值多少钱?
「王牌小食拼」      →  值多少钱?

首选:直接问收银台。 把券自带的商品码连同 couponId 传给 calculate-price, 返回的 discount 就是这张券的精确价值,price 就是用券实付:

券 实测结果
中薯条买一送一 14.5 元
王牌小食拼 6.6 元

兜底:从菜单反查原价(拿不到券的商品码时用):

券形态 价值推算 本例结果
满减券 满50减12 直接取减免额 12.0 元
买一送一 中薯条买一送一 取该商品菜单单价 14.5 元
免费券 免费脆薯饼 取该商品菜单单价(券面即免费) 脆薯饼单价
固定价券 9.9元中杯冰美式 菜单原价 − 固定价 23.0 − 9.9 = 13.1 元

为什么首选实测:「王牌小食拼」标题里没有数字、菜单里也没有对应商品 —— 反查只能返回 0(价值未知),而它实际值 6.6 元。 反查不只是可能低估,还会把真实存在的价值报成 0。

两条路都走不通时,一律取 0 并在报告中标注「价值未知」, 绝不估一个「看起来合理」的数。宁可少报,不可编造。

前提:把「客单价」的口径搞对

后面的券包体检和用券排程都依赖同一个数:你的「自然消费基准」 —— 不看券的话,你本来会花多少钱。它取自历史订单的客单价。

而这个数很容易算错。真实订单里有两类订单不该按「一单消费」来算:

订单类型 怎么处理
整单退款 直接过滤,不计入任何统计
部分退款 退款金额接口不提供 → 保持计入,但如实提示「支出可能略偏高」,不猜退款额
0 元订单 多为券核销 / 积分兑换 → 不计入客单价,但仍算一次点单机会

最后一条是关键:0 元订单只在有券时才会出现,拿它当「不看券时你会花多少」是 循环论证;而且它会把基准往下拉,让所有券都显得更难用上。

真实数据里,仅这一处口径修正就让客单价从 12.94 元变成 17.25 元, 结论随之从「两张券都得凑单」变成「两张都顺手可用」, 可实现金额从 ¥17.58 变成 ¥21.1。

于是本项目区分两个口径 —— 混用会让结论系统性失真:

更进一步:券包体检

知道「你正在白扔多少钱」之后,下一个问题自然是:这笔钱该怎么不白扔?

答案取决于一个反直觉的事实 —— 券的名义面值,从来不是它对你的真实价值:

净价值 = 券面价值 − 为用这张券、你本来不会花的那部分钱

一张「满50减12」的券,如果客单价是 13 元 —— 为了用它得点到 50 元, 名义省 12,实际多花 37,净亏 25。

按这个公式,券包会被切成四类:

类别 判定条件 建议
顺手可用 你本来就会花到门槛 直接用,净价值 = 券面价值
努一把 差额 < 券面价值 凑单后仍净赚,标出净赚多少
纸面富贵 差额 ≥ 券面价值 明确劝退,算出硬用会亏多少
无法判断 券面反查不到原价 如实标注,不估算

最后汇总成一句可以直接行动的话:

券包名义 ¥150 → 按你的消费习惯只拿得到 ¥60。

第三类是全篇最反直觉的地方 —— 别的工具都在说「你有券,快去用」, 我们说「这几张别用」。这与「别处帮用户花钱,我们帮用户止损」的立场一致。

⚠️ 判定用的「自然消费基准」取自历史订单的客单价,是一个保守近似、 而非精确值。这个假设写在报告里,不藏在代码里。

再进一步:用券排程

「可实现 ¥60」有个隐含前提 —— 这些券你都有机会用掉。 但券会过期,而你点单的次数有限。

于是有了第二层:先推算「券全部过期前你还会点几单」,再在这个约束下分配券。

窗口 20 天 · 点单频率 0.09 单/天
→ 这期间你大概只会点 1 单,最多用掉 1 张券

建议安排
  第 1 单 → 满20减12    净赚 5.0 元

排不上(1 张 · 可实现 ¥5.0)
  · 免费脆薯饼    净价值 5.0 元    还剩 20 天

券包可实现 ¥10.0,但机会只够用掉 ¥5.0 —— 剩下 ¥5.0 注定随券过期浪费。

「可实现的券,未必用得上」 —— 这是比「券面价值加总」更硬的一层。 大多数省钱工具连第一层都没做,这一层基本没人碰。

三个假设都写在代码注释里,不藏着:你在窗口内本来就会点 K 单、 每单最多用 1 张券、用券不改变你的点单次数。

最后一层:还没领的券

前面三步看的都是你已经领到手的券。但你的券源不止这些 —— available-coupons 会同时返回「已领取」和还没领的券:

麦麦省共 7 条券记录:已领取 4 条、可领取 3 条
可领取:免费脆薯饼 / 人气麦旋风买一送一 / 9.9元中杯冰美式

别的工具都在帮你「用」券,没人提醒你「还有券没领」。 未领取的券是唯一一种零成本的省钱来源,所以我们把它也做进了体检 —— 沿用同一套净价值框架,但问的是不同的问题:

券包体检问 可领取券问 类别文案
这张券该不该用 这张券该不该领 领了就能用 / 领了要凑一点 / 领了也用不上

「领了也用不上」是这里最反直觉的一类:领券本身免费, 但你为了用它而消费不是 —— 这跟券包体检是同一套标准。

还有一处必须诚实的地方:这批券拿不到 couponId(工具不返回), 所以上一节那套「带券计价实测」走不通,只能靠菜单反查; 反查不到时如实标「价值未知」,不猜。如上面 demo 输出的 3 张全是未知 —— 原因是那份菜单快照采集于 21:59,脆薯饼与冰美式并不在该时段的菜单里。

⚠️ 不擅自替用户领券。 领取(auto-bind-coupons)会改动账号状态, 必须先取得用户明确同意。工具负责把「有哪些没领、值不值」摆清楚, 领不领由用户决定。

另外,这批券没有有效期,所以它们不进用券排程(排程的窗口天数是靠有效期算的)。 渲染时也刻意不输出剩余天数 —— 把「未知」说成「无期限」就是编造。


目标用户


核心能力

能力 说明
门店菜单感知 价格与供应状态随门店实时变化,不写死菜单
券面价值解析 从菜单反查原价,算出这张券真实值多少钱(买一送一 = 商品单价;9.9元券 = 原价 − 9.9)
方案枚举计价 所有候选方案都经 MCP 真实计价,不复用历史价格
省钱归因 明确告诉用户「省在哪」:用了哪张券、换成了哪个套餐、加了什么凑单品
反凑单陷阱 当「为用券而加购」反而更贵时,主动指出并给出更优解
券的沉默成本 量化即将过期的券面值 —— 那是正在流失的钱
消费习惯画像 从历史订单还原月度支出、客单价、高频单品,给出结构性省钱建议
时段口径 菜单分时段供应,所以「几点点的」也是消费结构。样本不足 3 笔的时段只报单数,不给客单价
券包体检 算出每张券的净价值,明确告诉你哪几张别用:名义 ¥150,可实现的可能只有 ¥60
用券排程 可实现的券未必用得上 —— 在「券过期前你还会点几单」的约束下分配券,点明注定浪费多少
可领取券发现 麦麦省里还没领的券。别的工具帮你用券,我们提醒你还有券没领,并判断哪张值得领
积分体检 积分是另一种有保质期的钱:余额、已过期、能换什么、够不够,以及同一件商品哪个 SPU 更省积分
活动节奏 活动时间线 × 券的有效期:回答「等活动再买」等不等得起。只做纯日期比较,不拿营销文案里的价格说话

快速开始

1. 申请麦当劳 MCP Token

前往麦当劳 MCP 开放平台 https://open.mcd.cn/mcp ,使用手机号登录后在控制台激活并复制 MCP Token。

2. 配置 MCP 连接

本项目提供脱敏示例配置 mcp-config.example.json:

{
  "mcpServers": {
    "mcd-mcp": {
      "type": "streamablehttp",
      "url": "https://mcp.mcd.cn",
      "headers": {
        "Authorization": "Bearer ${MCD_MCP_TOKEN}"
      }
    }
  }
}

将 ${MCD_MCP_TOKEN} 替换为你自己的 Token 后,写入 MCP Client 的配置。请勿将真实 Token 提交到任何公开仓库。

3. 在 WorkBuddy 中启用

  1. 打开 WorkBuddy,左侧边栏进入【专家 · 技能 · 连接器】→【连接器】页签
  2. 点击右上角【自定义连接器】→【配置 MCP】
  3. 粘贴上面的 JSON(替换 Token),点击【保存】
  4. 回到【自定义连接器】,将 mcd-mcp 设为【启用】

4. 开始使用

直接对话即可,例如:

我在麦当劳点外卖,门店选好之后帮我算一下:
我手里有券,想吃一个汉堡 + 一份薯条 + 一杯饮料,预算 40 元,
怎么点最省钱?给我两个方案对比。

使用示例

示例一:预算内吃饱

你:预算 35 元,想吃鸡类主食 + 一个配餐,帮我选。

助手:已查询当前门店菜单与你名下可用券,为你计价了 3 个候选方案:

方案 组合 实付 预算 35 元
✅ 推荐 麦辣鸡腿汉堡三件套(汉堡 + 薯条 + 可乐) 33.5 预算内
超预算 单点:麦辣鸡腿汉堡 + 薯条 37.0 超 2 元
超预算 单点:麦辣鸡腿汉堡 + 薯条 + 可乐 46.5 超 11.5 元

只推荐三件套:33.5 元拿到三样,比只买两样的单点组合(37 元,已超预算)还便宜 3.5 元, 比同内容的单点组合省 13 元。这是套餐结构本身带来的价差,不必再叠加优惠券。

示例二:凑单陷阱提醒

你:我有一张「满 50 减 12」的券,是不是该凑单?

助手:不建议。你当前想买的商品合计 46.5 元,为触发门槛需要再加 3.5 元商品, 实付变为 46.5 + 3.5 - 12 = 38 元;而直接买三件套只要 33.5 元。 也就是说,凑单用券后反而比直接买套餐贵 4.5 元。 除非你本来就想吃那个凑单品,否则不划算。

这是一个演示场景(该账户当前没有满减券),用于说明凑单陷阱的判定逻辑。 逻辑本身由 src/test_saver.py 覆盖。

以上金额均来自 MCP calculate-price 的真实返回,非估算值。


架构

架构图:MCP 数据底座 → 适配层 → 链路 A 单次省钱 / 链路 B 消费审计

两条链路共用同一套 MCP 数据底座,但回答的是完全不同的问题:

衔接两者的是 mcd_adapter.py:它负责唯一一次的「分 → 元」换算、字段映射、退款过滤,以及券面价值反查。算法层不接触原始响应,因此不会出现单位错乱的幻觉。


项目结构

mcd/
├── README.md                  # 本文件
├── CONTEST_DECLARATION.md     # 参赛声明(官方文本,未做改动)
├── MCP_INTEGRATION.md         # MCP 集成说明:Server / Tool / 调用流程 / 业务价值
├── SKILL.md                   # Skill 定义与工作流说明
├── mcp-config.example.json    # 脱敏 MCP 配置示例(仅环境变量占位符)
├── .env.example               # 环境变量占位符
├── .gitignore
├── LICENSE                    # MIT
├── demo.py                    # 零配置演示:跑通两条链路(不需要 Token)
├── assets/                    # README 展示图
│   ├── banner.svg             #   头图
│   ├── demo-dual-view.svg     #   两种问法对比
│   ├── architecture.svg       #   双链路架构
│   └── social-preview.png     #   GitHub 社交预览图(1280×640)
├── fixtures/                  # 录制的真实 MCP 响应(供 demo 复现)
│   ├── README.md              #   数据来源、采集时间与补充方法
│   ├── menu.json              #   query-meals
│   ├── coupons.json           #   query-store-coupons
│   ├── orders.json            #   order-list
│   ├── prices.json            #   calculate-price(按组合签名索引)
│   ├── coupon_probe.json      #   calculate-price 带券计价(实测券的精确价值)
│   ├── available_coupons.json #   available-coupons(⚠️ 该工具返回 Markdown 文本)
│   ├── my_coupons.md          #   query-my-coupons(账户券,⚠️ Markdown 文本)
│   ├── meal_detail.json       #   query-meal-detail(套餐内含单品)
│   ├── points_account.json    #   query-my-account(积分账户)
│   ├── points_products.json   #   mall-points-products(积分商城)
│   └── campaign_calendar.md   #   campaign-calendar(⚠️ Markdown 文本,活动时间线)
├── src/
│   ├── saver.py               # 单次省钱:方案枚举、需求约束与优化核心逻辑
│   ├── auditor.py             # 长期画像:券的沉默成本 + 消费习惯审计
│   ├── planner.py             # 券包体检 + 用券排程 + 可领取券预判
│   ├── points.py              # 积分体检:余额、有效期、可兑换性(不折算金额)
│   ├── campaign.py            # 活动节奏:活动时间线 × 券的有效期(纯日期比较)
│   ├── mcd_adapter.py         # MCP 响应适配层(单位换算 + 字段映射)
│   ├── test_saver.py          # 算法逻辑自测
│   ├── test_adapter.py        # 适配层自测
│   ├── test_auditor.py        # 审计模块自测(含日期临界值、时段口径)
│   ├── test_planner.py        # 券包体检与排程自测(四类判定 + 机会约束)
│   ├── test_order_adapter.py  # 订单/券解析自测(用真实响应片段)
│   ├── test_claimable.py      # 可领取券自测(文本解析 + 不得编造有效期)
│   ├── test_points.py         # 积分自测(按截止日过滤 + 不得折算成金额)
│   └── test_campaign.py       # 活动日历自测(只取结构化字段 + 标签不当事实)
├── scripts/
│   └── style_check.py         # 零依赖代码规范检查(CI 会跑)
├── .github/workflows/
│   └── tests.yml              # CI:规范检查 + Python 3.8/3.10/3.12/3.13 跑测试与 demo
├── workbuddy.md               # WorkBuddy 开发对话上下文(WorkBuddy 专项奖励用)
└── docs/
    ├── TOKEN_GUIDE.md         # MCP Token 申请指引
    └── SUBMIT_CHECKLIST.md    # 报名提交检查清单

测试

python demo.py                    # 端到端:两条链路的完整输出
python src/test_saver.py          # 单次省钱:枚举、排序、凑单陷阱
python src/test_auditor.py        # 长期画像:沉默成本、消费习惯
python src/test_planner.py        # 券包体检与排程:四类判定、机会约束
python src/test_adapter.py        # 适配层:单位换算、响应解析
python src/test_order_adapter.py  # 订单/券:退款过滤、券价值推算
python src/test_claimable.py      # 可领取券:文本解析、免费券券型、不编造有效期
python src/test_points.py         # 积分:按截止日过滤、同款不同积分、不折算金额
python src/test_campaign.py       # 活动日历:只取结构化字段、标签不当事实

全部为纯本地运行,不需要 MCP Token,也不需要安装任何第三方库。

代码规范

python scripts/style_check.py     # 零依赖,只用标准库

检查项:行宽 ≤ 88 列、行尾空白与制表符、文件结尾换行、所有函数的返回类型标注、 所有非双下划线函数的 docstring、未使用的导入、__all__ 与公开定义的一致性。

之所以自己写而不用 ruff / flake8:项目定位是零第三方依赖, 连开发期的 linter 依赖也不引入。CI 上会跑这个脚本,规范不达标直接失败。 CI 会在 Python 3.8 / 3.10 / 3.12 / 3.13 上自动跑这一整套(见 tests.yml)。


这个项目可能对你有用,如果……

如果它对你有帮助,一个 star 就是最好的反馈。 ⭐

本项目为麦当劳程序员创意开发大赛参赛作品,非麦当劳官方产品。


设计原则

  1. 绝不猜测价格 —— 所有金额均来自 MCP calculate-price 的返回结果。
  2. 宁可少报,不可编造 —— 券的价值若无法从菜单反查到原价,取 0 并在报告中标注,而不是估一个看起来合理的数。
  3. 省在哪要说清 —— 每个推荐都附带省钱归因,而不是只给一个数字。
  4. 不诱导消费 —— 为用券而凑单如果导致实付上升,必须明确提示。
  5. 退款不算消费,0 元订单也不算 —— 整单退款直接过滤;「部分退款」的退款金额接口不提供,所以如实提示「支出可能略偏高」而不是猜一个数;0 元订单(券核销 / 积分兑换)不计入客单价,但仍算一次点单机会。
  6. 信息以官方为准 —— 价格、供应状态、券的可用范围均以麦当劳官方渠道实时结果为准。
  7. 不擅自改动账号状态 —— 领券(auto-bind-coupons)属写操作,必须先取得用户明确同意; 工具负责把选项摆清楚,决定权留给用户。
  8. 不知道就说不知道 —— 未领取的券拿不到有效期,就不输出剩余天数; 数据缺失时如实说缺失,不用「无期限」这类说法掩盖。
  9. 不把积分折算成金额 —— 积分没有固定汇率,它值多少取决于你换的那张券能省多少; 要换算就得先拿带券计价实测。所以积分体检只给事实(余额、有效期、够不够), 不给「约等于 ¥X」。
  10. 不拿营销文案做计算 —— 活动日历里的价格(「26.9 元起」)是营销文案,无法核实; 积分商品的 note 接口更是明确写着「严禁从中提取任何数值用于价格计算」。 这两处都只取结构化字段,文案一律丢弃。
  11. 样本不足就不给数字 —— 分时段客单价只在样本达到 3 笔时才输出, 达不到就只报单数并说明。样本量支撑不起一个基准时,报出来只是噪音。

免责声明

本项目为麦当劳程序员创意开发大赛参赛作品,由参赛者独立开发,非麦当劳官方产品。 项目输出仅供参考,不构成医疗、营养或其他专业建议;餐品信息、价格及供应状态以麦当劳官方渠道的实时结果为准。


License

MIT

mcd-coupon-strategist (Syenzzz/mcd-coupon-strategist)

Searching Database…

What is this?It is a Python-based ordering cost optimizer built on McDonald's China official MCP: it calculates the cheapest way to order at your current store and also tracks how much money you are losing to expiring coupons and points.
Is this a standalone app or an agent skill?It's a Python Skill meant to run inside an AI agent (tagged for 'workbuddy'), split into two pipelines: saver.py enumerates order combos and prices them live via the MCP, while auditor.py reads your order and coupon history for a long-term waste report.
How do I try it without a real McDonald's account?Just run 'python demo.py' — it replays recorded real MCP responses from the fixtures/ folder, so you can see both pipelines end-to-end with no token, no network, and no third-party dependencies.
Do I need a McDonald's MCP Token to use it live?For live pricing and your real order/coupon history, yes — you apply for a token on the McDonald's MCP open platform. The demo mode needs none of that and uses only the Python standard library.
Does it ever guess a coupon's value if it can't verify it?No — it first tries real pricing through MCP's calculate-price, then falls back to reverse-looking-up the menu price; if neither works it reports the value as 'unknown' instead of making up a plausible-looking number.
Who is this built for?Students and budget-conscious McDonald's China customers who want to order cheaply, people sitting on a pile of unused coupons, and anyone curious how much they've actually spent at McDonald's per month.
What is the 'silent cost of coupons' idea this project is built around?Most savings tools only answer how to order cheapest right now; this one also tracks which coupons in your wallet are about to expire and treats that expiring value as money you are actively losing, not just an unused resource.
How does it protect me from coupon padding traps?When adding extra items just to hit a coupon's discount threshold would actually make the total more expensive than not using the coupon, the skill detects this and explicitly recommends against it instead of pushing you to use the coupon.
Why does it separate 'spending basis' from 'ordering basis' when calculating your average order value?Zero-yuan redemption orders inflate the count of orders but have no real spending, so including them in the average-per-order baseline would artificially lower it and make legitimate coupons look harder to use than they really are; the project fixed this and saw the average order value move from 12.94 to 17.25 yuan.
What does the 'coupon health check' actually tell me?It splits every coupon in your wallet into categories like 'use as-is', 'worth a small top-up', or 'skip entirely' by comparing its real net value against your natural spending habits — for example showing a coupon pack with a nominal value of 150 yuan is only actually worth about 60 yuan to you.
Creator
Syenzzz
Size
1 MB
Stars
990
Updated
CommitOct 21
Age
16d
Commits · 90 Days