不问"离我最近的门店是哪家",只回答"最近一家有我想要的东西、还用得上我那张券的门店在哪,要花多少钱"。
麦当劳程序员创意开发大赛参赛作品 · 基于麦当劳中国 MCP 服务开发
打开麦当劳 App 点餐,通常要经历这么一串折腾:
- 想吃的巨无霸,家附近三家店,不知道哪家现在有卖
- 挑了一家点进去,发现早餐档刚过,巨无霸要等到十点半
- 换成套餐,翻半天券包,不确定这张"满 30 减 8"在这家店能不能用
- 到结算页才看到配送费 9 块,总价比预想多了十块
- 放弃,随便点了个别的
四步里每一步都在做信息核对,而且核对的是分散在不同接口里的三类信息:门店距离、门店在售状态、门店可用券。
mcd-nearest-deal 把这三件事串成一条链路:从最近的店开始,逐家验证"卖不卖",命中即停;锁店之后再取"门店券 ∩ 我的券"的交集;最后用 MCP 算价接口算出真实到手价,把 2–3 个方案并排给用户。下单是最后一步,且必须用户明确确认。
大多数同类作品做的是"在已有门店里比价"或"在已有菜单里算卡路里"。本项目的落点在券与库存的交叉验证:一家店有券但不卖、有货但券不能用,这两种情况在实际点餐里比"哪家便宜两块"更常见,也更影响体验。
| 用户 | 场景 | 他真正想知道的 |
|---|---|---|
| 上班族 | 午休 40 分钟,想快速解决一顿 | 公司楼下哪家店现在能买到,多少钱,几分钟能取 |
| 有券的人 | 券包里有十余张券,懒得逐张比 | 我这张券哪家店能用,怎么搭最划算 |
| 预算敏感学生 | 一天伙食预算固定 | 25 块以内能吃到什么,含不含配送费 |
| 家庭用户 | 一次点多人份 | 附近哪家店品类全,凑单能不能吃到满减 |
- 最近在售点定位:给地址或定位,返回最近的、确实在售目标餐品的门店,附距离与替代选项
- 券可用性交叉核对:门店券与账户券取交集,避免"看着能用结算不行"
- 组合搭配:按预算组 2–3 个方案,单点 / 套餐 / 凑满减各一
- 真实算价:调
calculate-price得到含配送费、优惠的应付金额,不只报原价 - 多方案比价:附带脚本做确定性排序,标出差额与"最近≠最便宜"的提示
- 确认后下单:复述门店、餐品、就餐方式、券、应付五项后,用户确认才创建订单
- 订单查询与取消:查进度、查历史、取消未支付订单
明确划清边界,避免预期错位:
- 不代付。订单支付链接由用户本人打开完成付款。项目不接触任何支付凭证。
- 不推荐"最省"。只给"当前可查到的券与门店条件下"的较优解,不承诺全局最优。
- 不做营养建议。营养数据可作为参考展示,但不构成医疗或膳食指导。
- 不缓存个人数据。地址、手机号、券码、订单内容仅在单次任务内使用,不落盘。
- 不覆盖非点餐场景。积分抽奖、商城兑换、活动预约虽在 MCP 能力范围内,但不是本技能主线,仅在用户明确要求时按对应工具处理。
- 一个麦当劳中国 MCP Token
- 访问 https://open.mcd.cn/mcp
- 右上角【登录】,手机号验证
- 点【控制台】→【激活】→ 同意服务协议
- 复制生成的 Token
- 一个支持 Streamable HTTP 的 MCP 客户端。推荐 WorkBuddy——本项目的开发与验证环境。
在 WorkBuddy 中:左侧边栏【专家·技能·连接器】→【连接器】页签 → 右上角【自定义连接器】→【配置 MCP】,填入:
{
"mcpServers": {
"mcd-mcp": {
"type": "streamablehttp",
"url": "https://mcp.mcd.cn",
"headers": {
"Authorization": "Bearer YOUR_MCP_TOKEN"
}
}
}
}两处容易踩的坑:
YOUR_MCP_TOKEN必须替换成真实 Token;保存后要回到连接器列表点【启用】,首次启用会弹"信任确认",点信任才会加载工具。任一遗漏都会得到401。脱敏后的示例见本仓库
mcp-config.example.json,其中只使用环境变量占位符。
本仓库根目录即技能目录:SKILL.md 在根目录,references/、scripts/ 也在根目录下。安装就是把整个仓库放进 WorkBuddy 的技能目录,并且目录名必须是 mcd-nearest-deal(与 SKILL.md 里 name 字段一致):
# macOS / Linux
git clone https://github.com/<你的账号>/mcd-nearest-deal.git ~/.workbuddy/skills/mcd-nearest-deal
# Windows (PowerShell)
git clone https://github.com/<你的账号>/mcd-nearest-deal.git "$env:USERPROFILE\.workbuddy\skills\mcd-nearest-deal"若从 GitHub 下载 zip:解压得到 mcd-nearest-deal-main/,重命名为 mcd-nearest-deal 后放进 ~/.workbuddy/skills/。目录名不一致时技能可能不被识别。
重启 WorkBuddy,在【技能】列表中应能看到「麦当劳最近在售点」。
在对话框里说一句:
帮我在附近找家能买到早餐的麦当劳,看看券怎么用最划算
技能应依次调用 now-time-info、query-my-account、query-nearby-stores、query-meals,然后给出带距离与价格的方案。
提示:如果当前是早餐时段(约 10:30 前),问"巨无霸"会查不到——这不是故障,是正餐菜单还没上线。技能会说明时段并给早餐档的替代。
用户
我想吃板烧鸡腿堡,公司附近哪家麦当劳现在能买到?我在上海市黄浦区南京东路 300 号。
技能行为
now-time-info确认当前时段(是否在早餐档)query-nearby-stores按地址取候选门店,距离升序- 对最近一家调
query-meals,检查板烧鸡腿堡是否在售 - 若在售,命中即停;若不在售,验下一家,并说明"最近的 A 店当前时段不售,B 店 400 米外有货"
- 输出门店名、地址、距离、当前是否在售
用户
25 块以内,一个人吃,能用券就用券。
技能行为
- 建基线:时间 + 积分
- 选最近在售门店
- 组两个方案:单点主力餐品 / 单点 + 小食凑券门槛
query-store-coupons∩query-my-coupons取可用券- 分别
calculate-price - 用
scripts/compare_deals.py排序输出对比表,标出实付差额 - 等用户选,确认后才下单
用户
我券包里有张满 30 减 8,附近哪家店能用?
技能行为
query-my-coupons确认这张券确实存在,读出它的真实门槛字段(不看券名推断)query-nearby-stores+ 逐店query-store-coupons,找出接受该券的门店- 在可用门店里筛出"凑单到 30 元以上仍划算"的组合
calculate-price验证折扣是否真的生效(规则上说能叠加、实际不叠加的情况存在)- 给出"券能用 + 凑单方案 + 真实到手价"
技能的复述格式:
确认一下:
门店 麦当劳(南京东路步行街店),南京东路 300 号 1 层
方式 到店自取
餐品 巨无霸套餐 ×1(可乐换无糖)、中薯 ×1
用券 满 30 减 8
应付 ¥34.50
确认后我就下单,付款链接会给你,支付前随时可取消。
用户回"可以"才调 create-order。用户回"随便"不算确认。
根目录即技能目录,git clone 下来直接可用:
mcd-nearest-deal/ # 仓库根目录 = 技能目录
├── SKILL.md # 技能主文件:五步工作流 + 核心约束
├── references/
│ ├── mcd-tools.md # 工具分组、调用时机、错误码、待核对清单
│ └── order-rules.md # 就餐方式配对、预约、券规则、算价、隐私
├── scripts/
│ └── compare_deals.py # 多方案比价排序(无第三方依赖)
├── README.md # 本文件
├── CONTEST_DECLARATION.md # 参赛声明(官方原文,未改动)
├── MCP_INTEGRATION.md # MCP 接入与调用流程说明
├── mcp-config.example.json # 脱敏配置示例
└── workbuddy.md # WorkBuddy 开发对话上下文
参赛要求文件(README.md、CONTEST_DECLARATION.md、MCP_INTEGRATION.md、mcp-config.example.json、workbuddy.md)与技能文件同处根目录,互不冲突:WorkBuddy 只读取 SKILL.md 及其引用的 references/、scripts/,其余文件不参与技能运行。
几个刻意的决定,说明为什么这么做:
为什么"命中即停"而不是查完全部门店。
查完所有店再排序,接口调用量与门店数线性相关,且用户只关心结论。逐家验证到第一家命中就停,平均只需 1–3 次 query-meals 调用,也天然满足 600 次/分钟的限流约束。
为什么券要查两层。
query-store-coupons 回答"这店收不收",query-my-coupons 回答"我有没有"。只查一层就会给出用户实际用不了的优惠。取交集是唯一正确的做法。
为什么金额只认 calculate-price。
原价减券面与实际应付之间常差配送费、包装费、餐品级折扣。手算看起来对,结算页会多出几块。所以技能里明令:calculate-price 成功前只能叫"预估"。
为什么把写操作单列成约束。 下单、领券、取消、抽奖都会改变账号状态。这类操作在自然语言驱动下最大的风险不是技术失败,而是"用户只是问问,AI 真给下单了"。约束条款写在 SKILL.md 最前面,优先于任何流程步骤。
为什么不硬编码枚举值。
beType、orderType、beCode 是这套接口里最容易出问题的地方:不同就餐方式下同一个字段语义不同,跨场景复用会静默下错单——接口返回成功,订单却下到了别处。所以规则是"全部从返回里读原值,同一次流程内复用,切换就餐方式必须重新取"。
2026-10-24 用真实 Token 与真实账号跑通了完整只读链路(时间 → 账号 → 门店 → 菜单 → 券 → 算价)。诚实标注当前状态:
| 项 | 状态 |
|---|---|
| MCP 服务地址、协议、鉴权方式 | ✅ 已核实 |
| 端点存活性 | ✅ 已实测:https://mcp.mcd.cn 可访问,约 0.9s 响应 |
| 鉴权失败行为 | ✅ 已实测:无 Token → 403;无效 Token → 401 当前鉴权码不存在或已失效! |
| 33 个工具的名称与职责 | ✅ 已核实(官方仓库 v1.0.9),且已程序化校验:文档引用的工具名 100% 存在于官方清单 |
now-time-info / query-my-account |
✅ 已实测,返回真实时间与积分账户 |
query-nearby-stores |
✅ 已实测两种 searchType 的差异:收藏查询距离恒为 0 且无营业状态;搜索查询有真实距离(米)与 businessStatus |
query-meals |
✅ 已实测,确认早餐/正餐是两套菜单(10:27 仅有早餐品) |
query-store-coupons ∩ query-my-coupons |
✅ 已实测交集逻辑:账户 4 张券,三店交集仅 1 张,另两店为空数组 |
calculate-price |
✅ 已实测三组对比:到店无券 ¥11.90 / 到店带券 ¥9.90 / 外送 ¥17.90(含 ¥6.00 配送费) |
| 外送链路 | ✅ 已实测 delivery-query-addresses → delivery-query-stores → 算价,确认 beCode 在到店/外送下取值不同 |
| 券是否真生效的验证方法 | ✅ 已实测:带券后 discount 从 710 分升至 910 分 |
| 比价脚本 | ✅ 已用真实算价数据跑通排序、差额、节省比例 |
create-order 及之后的下单链路 |
⚠️ 未实测:测试不应产生真实订单。写操作规则见 references/order-rules.md |
query-meal-detail、营养查询、商城/抽奖/活动 |
⚠️ 未实测,非主链路 |
references/mcd-tools.md 已按实测结果重写:标 ✅ 的字段名与返回结构可直接信任,标 ⚠️ 的首次调用时按报错回写。金额单位「分」、price 与 productPrice 的区别、beCode 的条件传参,这三处是最容易出错的地方,文档里都有实测例子。
- 不含真实 Token、密钥、账号凭证;配置文件仅用
${MCD_MCP_TOKEN}占位符 - 不含任何个人信息
- 不采集、不上传用户数据;所有请求直达麦当劳 MCP 服务
- 支付环节完全由用户操作,项目不接触支付凭证
- 遵循《麦当劳 MCP 服务规则》与 WorkBuddy 软件许可协议
本项目使用腾讯 WorkBuddy 开发,开发过程见 workbuddy.md。
MCP 集成细节见 MCP_INTEGRATION.md。
本项目为麦当劳程序员创意开发大赛参赛作品,由参赛者独立开发,非麦当劳官方产品,与麦当劳中国无隶属或代理关系。
项目输出仅供参考,不构成医疗、营养或其他专业建议。餐品信息、价格及供应状态以麦当劳官方渠道的实时结果为准。
© 2026 参赛者保留本项目原创内容的相关权利。麦当劳、巨无霸等相关商标归其权利人所有。