burger-quest
Health Warn
- No license — Repository has no license file
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Low visibility — Only 5 GitHub stars
Code Pass
- Code scan — Scanned 12 files during light audit, no dangerous patterns found
Permissions Pass
- Permissions — No dangerous permissions requested
No AI report is available for this listing yet.
Burger Quest 麦门异世界 — 基于麦当劳中国官方 MCP Server 的游戏化点餐规划 Skill(麦当劳程序员创意开发大赛参赛作品,非官方)
🍔 Burger Quest · 麦门异世界
输入预算、心情、口味和用餐偏好,RPG 游戏主持人替你从麦当劳中国官方 MCP Server 的真实菜单里,组出一份不超预算的点餐方案,并生成一张可以分享的任务成就卡。
非麦当劳官方项目 —— 本项目为「麦当劳程序员创意开发大赛」参赛作品,由参赛者独立开发。餐品信息、价格及供应状态以麦当劳官方渠道的实时结果为准。
1. 项目介绍
「麦门异世界」是一个游戏化点餐规划 Skill:把「今天吃什么」变成一场 RPG 冒险。
- 你(冒险者)说出预算、口味、心情、人数、用餐方式;
- AI(游戏主持人)调用麦当劳中国官方 MCP Server 查询真实可售菜单、真实价格、真实优惠券和营养数据;
- 主持人生成任务书:任务名称、故事背景、推荐装备(真实餐品)、选择理由、总价与剩余预算;
- 一键生成分享成就卡(HTML / Markdown)。

↑ 实际运行生成的成就卡(DEMO 数据;真实 MCP 模式带 LIVE 徽章)。查看 HTML · Markdown · 更多卡片
三条铁律(贯穿全部代码与测试)
- 不编造餐品:进入方案的每一项都来自
query-meals返回的真实菜单; - 不编造价格:总价只来自
calculate-price的权威返回,菜单价缺失的餐品直接出局; - 不编造优惠与库存:优惠券必须来自
query-store-coupons/query-my-coupons,库存状态以 MCP 返回为准。
MVP 只生成方案,不自动下单;create-order 等具有实际影响的操作需要用户明确确认后才可调用(本仓库未接入)。
2. 目标用户
- 选择困难症患者:一句话需求 → 直接可执行的点餐方案;
- 预算敏感用户:预算内多组合对比,剩余预算一目了然;
- 关注营养的用户:低卡 / 高蛋白偏好筛选,热量与蛋白质来自
list-nutrition-foods真实数据; - 任何想在 AI 对话里「玩着点餐」的开发者和玩家。
3. 安装
# 要求 Python >= 3.10
git clone https://github.com/yaohanbo1-hue/burger-quest.git
cd burger-quest
# 开发/测试(推荐,含 pytest):
pip install -e ".[dev]"
# 或只装运行时(demo 演示数据随包分发,安装后离线可用):
pip install .
安装后有三种等价入口(演示模式完全离线,不需要 MCP Token):
burger-quest demo "我想吃辣,预算35,外送到南山科技园,一个人" # 控制台入口
python -m burger_quest demo "我想吃辣,预算35" # 模块入口
python -m pytest tests/ -q # 测试(568 个)
依赖分层(单一事实来源 pyproject.toml):运行时仅 mcp>=1.2.0(官方 SDK,仅真实 MCP 模式需要;
引擎核心解析/规划/文案/卡片零第三方依赖);测试依赖 pytest>=8.0(dev extra)。requirements.txt 不再重复罗列,避免两边漂移。
3.1 Windows 用户(PowerShell)
要求 Windows 10/11 + Python 3.11 或 3.12(也兼容 3.10/3.13;安装时勾选 Add to PATH)。
一键安装(幂等,可重复执行;自动创建 .venv、装依赖、引导配置 Token):
powershell -ExecutionPolicy Bypass -File scripts\setup-windows.ps1
# 只装运行时(不装 pytest):加 -SkipDev
# 强制重建虚拟环境:加 -RecreateVenv
日常使用(自动用 .venv,无需手动激活):
powershell -File scripts\run-windows.ps1 demo "我想吃辣,预算35" # 离线演示(无需 Token)
powershell -File scripts\run-windows.ps1 quest "我想吃辣,预算35,到店自取" # 真实模式(需 Token)
powershell -File scripts\run-windows.ps1 progress --progress .\burger-quest-progress.json # 本地虚拟进度/成就图鉴
powershell -File scripts\check-mcp-windows.ps1 # 真实连接诊断
powershell -File scripts\run-windows.ps1 # 不带参数 = 查看帮助与退出码图例
也可以激活虚拟环境后直接用命令:.\ .venv\Scripts\Activate.ps1,然后 burger-quest --help。
退出码与安全退出(run-windows.ps1 透传 CLI 语义):0=成功;1=未生成方案(含等待选择门店);2=配置或连接错误(如缺 Token)。脚本无后台任务,随时 Ctrl+C 干净退出。
Token 配置(真实模式必需,三种方式按安全级排序):
- 仅当前 PowerShell 进程(最安全,关窗口即失效):
$env:MCD_MCP_TOKEN = "你的Token" .env文件(方便,明文落盘):setup 脚本引导写入仓库根.env;该文件已被.gitignore排除,不会进 Git。⚠️ 它是明文文件,能读你磁盘的程序都能看到;清理方式:Remove-Item .env。共享屏幕/远程协助时注意遮挡。- 持久化到用户环境变量(
[Environment]::SetEnvironmentVariable("MCD_MCP_TOKEN", $token, "User"))会写入注册表——请自行评估风险。切勿把 Token 写进源码、提交 Git 或粘贴进聊天窗口。
连接诊断(真实 MCP 不可用时先跑这个,逐步 PASS/FAIL + 下一步建议):
powershell -File scripts\check-mcp-windows.ps1
# 等价 CLI: burger-quest doctor
诊断依次检查:Python 版本 → 官方 mcp SDK → L1 服务可访问(HTTP 级探针,无需 Token、不携带 Token) → MCD_MCP_TOKEN 存在性(只查存在性,绝不打印值)→ L2 认证(MCP 初始化)→ L3 工具发现 → L4 一次只读调用;末尾输出五层验证状态汇总(L5 业务流程由 smoke/quest 覆盖);失败分类 TOKEN_MISSING / 401 / 403 / 429 / NETWORK / TIMEOUT_CONNECT / TIMEOUT_READ / RESPONSE / SDK / TOOL_MISSING / BUSINESS,退出码 0=全过 / 1=有失败 / 2=配置错误。缺 Token 直接失败,不会偷偷切换演示数据。
Windows 脚本细节:UTF-8 BOM(PowerShell 5.1 无 BOM 会把中文脚本按 ANSI 解析成乱码/语法错误)、控制台编码头(
[Console]::OutputEncoding=UTF8+PYTHONIOENCODING=utf-8,避免子进程中文输出乱码)、幂等设计(复用.venv、不覆盖已有.env)、Token 全程不回显不落日志——这些不变量由tests/test_windows_scripts.py与 CI 的windows-latestjob 守护。
3.1.1 Windows 真实验证报告(2026-10-17,本机 PowerShell 5.1 实跑)
| 验证项 | 方法 | 结果 |
|---|---|---|
| 脚本语法(PS 5.1 解析器语义) | Language.Parser::ParseFile 三个脚本 |
✅ 0 错误,UTF-8 BOM 在位 |
| 帮助命令 | 不带参数运行 run-windows.ps1(且从 C:\ 错误工作目录启动) |
✅ 显示帮助+退出码图例,脚本自定位仓库根 |
| 中文参数全链路 | demo "我想吃辣,预算35,外送到南山科技园,一个人" --json out.json |
✅ JSON 内容正确(麦辣鸡腿堡+鸡翅、¥34 权威、预算内) |
| 真实模式缺 Token | quest "我想吃辣,预算35"(无 Token) |
✅ 退出码 2,未生成任何结果文件,未回退 Demo |
| 预算不足诚实失败 | demo "预算5块,想吃汉堡" |
✅ 退出码 1,说明无解 |
| 真实 MCP 诊断 | check-mcp-windows.ps1(真实 Token) |
✅ 退出码 0(doctor L1–L4 全过) |
| 中文+空格路径 | junction 到「D:\麦门 测试\burger-quest」后运行脚本 | ✅ 退出码 0(无需管理员权限) |
| 非管理员 | 以上全部在当前用户权限下完成 | ✅ 无提权、无关闭安全软件 |
| 全量测试 | pytest tests/ -q |
✅ 568 passed |
关于输出乱码的说明:在 PowerShell 里把脚本输出重定向到文件或管道时,父进程会按系统代码页重新编码(中文 Windows 默认 GBK),这是 Windows 控制台机制、非脚本缺陷;交互式控制台窗口显示始终正常。需要 UTF-8 捕获时:[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false) 后再重定向,或用 --json/--card-html 让 Python 直接写 UTF-8 文件。
4. 快速开始
4.1 演示模式(离线,无需 MCP Token)
python -m burger_quest demo "我想吃辣,预算35,外送到南山科技园,一个人" \
--card-html examples/cards/demo_spicy.html \
--card-md examples/cards/demo_spicy.md
输出(节选):
🍔 无名冒险者的第一道门槛 · 精打细算的游侠·辣味偏好(麦乐送远征)
【推荐装备】
⚔️ 烈焰骑士的招牌巨剑 —— 麦辣鸡腿堡(¥21.00)
🎟️ 券灵加持 —— 麦辣鸡腿堡立减3元(演示券)
【价格结算】
原价合计:¥21.00
优惠减免:-¥3.00
配送费:¥9.00
应付总价:¥27.00(calculate-price 权威结果)
剩余预算:¥8.00
⚠️ 演示模式使用包内
burger_quest/data/demo_data.json的演示数据(随包分发,安装后离线可用)(结构与 MCP 返回兼容,数值非实时),用于离线体验与自动化测试。
4.2 真实模式(调用麦当劳 MCP)
# 1. 到 https://open.mcd.cn/mcp 手机号登录,控制台申请 MCP Token
# 2. Token 只通过环境变量提供(切勿写进代码或提交仓库)
export MCD_MCP_TOKEN="你的Token"
# 3a. 到店场景:按位置规划(官方 query-nearby-stores 查附近门店,多家则列出候选)
python -m burger_quest quest "我想吃辣,预算35,在深圳市南山区科技园,到店自取"
两种场景走不同的官方工具链(不可互相替代):
| 场景 | 门店确定方式 | 前提条件 |
|---|---|---|
| 到店自取 / 堂食 | query-nearby-stores(按 city/keyword,beType=1) |
提供位置或门店编码 |
| 外送(麦乐送) | delivery-query-addresses(已保存地址)→ delivery-query-stores(可配送门店 + beCode) |
账户须已在麦当劳官方 App/小程序保存配送地址;没有符合条件的地址时明确提示,不伪造外送能力 |
# 3b. 外送场景(需要账户已有保存的配送地址)
python -m burger_quest quest "我想吃辣,预算35,外送到深圳市南山区科技园"
行为约定(与演示模式彻底分离):
| 情况 | 行为 |
|---|---|
未配置 MCD_MCP_TOKEN |
明确报错(退出码 2),不会静默切换到演示数据 |
| 找到多家附近门店 | 列出全部候选门店及编码并暂停,不默默选一家;选定后加 --store-code 重新运行 |
| 没有位置信息 | 明确要求补充地址或门店编码(不会假设门店) |
| 查不到门店 / 菜单失败 / 无法确定用餐方式 | 逐项说明原因并停止,不生成任何价格 |
# 先查附近门店,获取真实门店编码
python -m burger_quest stores "南山科技园"
# 指定门店运行
python -m burger_quest quest "我想吃辣,预算35" --store-code <编码>
⚠️
DEMO-001/DEMO-002只存在于演示数据(包内 data/demo_data.json),真实模式使用会查询失败;真实门店编码请用stores子命令获取。
🔐 凭据安全:Token 只放环境变量或 WorkBuddy 连接器配置,不要写进代码、提交 Git 或粘贴进聊天窗口;本项目的调用审计、日志与卡片输出均不包含 Token(有专项测试断言);
.gitignore已排除.env、mcp-config.json等真实配置。
只读冒烟测试(验证连接 / 工具发现 / 关键查询,不调用任何写操作):
python -m burger_quest smoke --demo # 离线演示模式:无需 Token,可重复执行
python -m burger_quest smoke # 真实模式:八步只读检查(需 MCD_MCP_TOKEN)
八步 = 连接 → 工具发现(项目依赖的 8 个工具须在官方 35 个工具清单内)→ 服务器时间 →
附近门店 → 菜单 → 餐品详情 → 算价 → 安全自检(仅只读工具)。逐步输出 PASS/FAIL,
退出码 0=全过 / 1=有失败 / 2=配置错误;Token 只从环境变量读,绝不打印。
4.3 作为 WorkBuddy Skill 使用
把本仓库的 skill.md 安装为 WorkBuddy 技能,并在 WorkBuddy 中配置麦当劳 MCP 连接器(mcp-config.example.json 提供模板),即可在对话框中直接说:
「用麦门异世界帮我规划:预算 40,想吃辣,外送到南山科技园」
详细用法见 skill.md 与 MCP_INTEGRATION.md。
4.4 成就卡预览(复古 RPG 界面 · 红黄配色 · 独立项目声明)
分享卡为纯本地 HTML/CSS 实现(无外部 CDN / 字体 / 图片依赖),适配手机与打印;
明确标注 NON-OFFICIAL FAN PROJECT 与 LIVE(真实 MCP 数据)/ DEMO(离线演示数据)模式。
| 卡片 | 数据模式 | 文件 |
|---|---|---|
| 辣味外送成就卡 | DEMO | demo_spicy.html · demo_spicy.md · 预览图 |
| 多人丰盛成就卡 | DEMO | demo_share.html · demo_share.md |
| 低卡轻食成就卡 | DEMO | demo_light.html · demo_light.md |
| 真实 MCP 成就卡 | LIVE SNAPSHOT(历史快照) | live_real.html · live_real.md · 预览图 |
卡片诚实性设计:总价只认 calculate-price 权威结果(未核验显示「未知」);营养信息只在官方
返回真实数据时展示,缺失明确显示「未知」而非装饰数字(见 LIVE 示例卡的属性面板);
每件装备标注商品编码与数据来源,可回溯核查。
📸 LIVE SNAPSHOT 说明:
live_real.*是真实 MCP 调用的历史快照(卡内标注生成时间),
不会自动刷新——卡内金额、优惠与供应状态仅代表该次查询时的返回结果,不代表麦当劳当前价格、
优惠或库存。生成快照卡:quest "…" --card-snapshot --card-html card.html(该标记仅适用于真实模式;
演示数据即使加该标记也只会显示 DEMO 徽章,有专项测试守卫)。
4.5 价格从哪里来、什么时候无法确认(用户必读)
价格来源:真实模式下,所有价格只来自麦当劳中国官方 MCP calculate-price 的实时返回
(金额单位为「分」,如 1590 = ¥15.90);演示模式(demo)使用包内演示数据,卡片标注DEMO · 离线演示数据——演示数据绝不会用来填补真实结果。
价格四段怎么读:
| 展示 | 含义 | 来源字段 |
|---|---|---|
| 原价合计 | 优惠前金额 | 官方 originalPrice 合计 |
| 已核实优惠 | 官方核验真实生效的优惠 | 官方 discount |
| 配送费 | 仅外送,且以官方返回为准 | 官方 deliveryFee |
| 应付总价 | 你最终要支付的金额 | 官方 price(calculate-price 权威结果) |
「单价」的口径:官方按商品行返回行小计——数量 >1 时 ÷ 数量归一;套餐类餐品
(三件套/双全盒/单人餐等)这个单价就是套餐组合价(餐品名自带信息,引擎不按名称推断)。
适用范围:价格只代表「该门店 + 该渠道 + 该次查询」的返回,随时可能随门店、时间、
渠道、规格变化——任务书和分享卡都会标注门店编码、渠道与生成时间;分享出去的卡片
不会自动刷新。外送价格含官方配送费;到店自取不含。
什么时候会无法确认价格(引擎选择诚实失败,绝不编造):
- 菜查到了、但官方没返回某些餐品的价格 → 这些餐品被排除并明示「已从组合中排除(而不是估算价格)」;
- 一个真实价格都拿不到 → 不出方案,明示「价格暂不可验证」(未编造任何价格);
- 算价接口失败 → 不出方案(绝不用单价加总冒充权威总价);
- 0 元或负数的「价格」按无效处理并明示——0 元商品不会免费进组合。
4.6 安全与隐私边界(Web 界面)
本地 Web 界面在 127.0.0.1:8787 开HTTP 服务。浏览器里任意一个网页都能对本机端口发请求——CORS 只阻止读取响应,阻止不了请求发出。所以有两道防线(2026-10-17 安全审计补齐):
| 防线 | 挡什么 | 实现 |
|---|---|---|
| Host 白名单 | DNS rebinding(恶意域名解析到 127.0.0.1,浏览器以为在访问它,实际打到本地服务) | 只接受 127.0.0.1 / localhost / ::1 |
| Origin 校验 | 任意网页跨站 POST,借你的浏览器用你的 Token 发起真实 MCP 查询 | 写操作要求 Origin 与 Host 同源 |
实测(真实 HTTP 请求,非 mock):
GET /api/healthz Host:127.0.0.1:PORT → 200正常
GET /api/healthz Host:evil.example.com:PORT → 403 access-denied
POST /api/quest Origin:https://evil.example → 403 access-denied(未触发任何 MCP 调用)
POST /api/quest Origin:http://127.0.0.1:PORT → 200 进入业务逻辑
其他隐私边界:
- Token 只在服务端:前端只有
token_present布尔标记,从不读取 Token 值;不设Cookie、不用 localStorage 存凭据; - Token 不落任何输出:调用审计、诊断日志(JSONL)、成就卡、进度存档均不含Token 值(有专项断言);
- 诊断日志默认关闭:需显式设
BURGER_QUEST_LOG才落盘,且敏感键自动打码; - 进度存档零个人信息:只存成就 ID/XP/已解锁章节,不写用户需求原文、不写地址手机号;且默认不写文件(需
--progress显式启用); - 绑定地址默认 localhost:
--allow-lan才放行局域网,并会打印风险提示。
5. 使用示例
| 场景 | 输入 | 结果 |
|---|---|---|
| 多条件综合 | 预算 35 元,想吃辣,两个人,希望尽量高蛋白,不想点太多饮料 |
最多 3 个可比较方案,全部经 calculate-price 权威核验;蛋白/少饮偏好逐条说明符合或不完全符合 |
| 辣味外送 | 我想吃辣,预算35,外送到南山科技园,一个人 |
麦辣鸡腿堡 + 麦辣鸡翅 + 双券:原价 ¥33 − 优惠 ¥8 + 配送费 ¥9 = 应付 ¥34,剩 ¥1 |
| 多人丰盛 | 两个人想吃点好的,预算100,堂食 |
麦辣鸡腿堡套餐 + 巨无霸套餐 + 麦乐鸡×2 = ¥86,叠券省 ¥5(丰盛偏好下优先用足预算;同样餐品不加小食的 ¥63 方案也在候选里) |
| 低卡轻食 | 减肥中,想吃低卡的,预算40,到店自取 |
双层吉士堡 + 玉米杯 + 零度可乐 = 应付 ¥25,剩 ¥15,整餐约 540 千卡 |
| 超预算参考 | 预算12块,想吃辣 |
给出最接近的真实价格方案(¥13),明确标注「超出预算 ¥1」 |
| 预算不足 | 预算5块,想吃汉堡 |
如实失败:明确说明没有组合落在预算内,并给调整建议,不编造价格 |
| 多家门店 | quest "想吃辣,预算35"(位置附近有多家店) |
列出全部候选门店及编码并暂停,用 --store-code 指定后重跑 |
套餐规划引擎的铁律(2026-10-14 升级):
- 餐品只来自真实查询结果(或明确标记的演示数据);价格缺失的餐品不入组合;
- 最终总价只认 calculate-price 权威返回,绝不用菜单展示价求和冒充;
- 优惠券只从真实券池选择,不默认可叠加——只有权威核验确认生效的优惠才计入;
- 营养偏好只用真实营养数据:数据缺失时降低可信度(标注 + 降权)或排除候选,绝不猜测;
- 每个方案附「符合 / 不完全符合」的简洁说明;最多 3 个可比较方案,按推荐序排列;
- 金额全程整数分;RPG 文案层只做叙事包装,不改变商品数量、总价与剩余预算。
中文需求解析(2026-10-14 升级,规则式实现,无大模型依赖):
- 本次新增支持:模糊预算(「大概二三十元」= 20~30 按上限 ¥30 处理并注明)、否定口味(「不吃辣」= 硬避免,不被「想吃脆的」覆盖;「想吃辣但不吃辣」按不吃处理并提示冲突)、多预算取最严、营养数字不再污染预算/地点(修复「热量控制在400千卡以内」被误读为 ¥400)、「偏清淡」、门店名作地点(「麦当劳深圳同方信息港餐厅」)、「X附近」、「少糖」(识别但明示无糖数据、无法按此筛选——不假装支持);
- 仍不支持及处理:任意自由文本的意图推断(如「随便」不设预算)、无糖/少糖的筛选验证(数据无糖字段,明示)、跨句指代(「还是老样子」)、口语化极模糊表达(「看心情」)——凡未能可靠解析的条件都会在【提示】中明示,不假装理解成功;
- 解析结果可用
--json的constraints_debug字段查看(结构化、默认不含原始输入文本)。
完整可复现演示(含验证约束说明)见 examples/planner_demos.md 与 examples/demo.md。
6. 测试
python -m pytest tests/ -v
568 个测试覆盖:自然语言解析(预算:明确/上限/模糊区间二三十元/多值取严/营养数字不干扰;人数;口味:否定口味不吃辣(硬约束不被其他偏好覆盖)/冲突裁决/清淡;营养:低卡/高蛋白/少糖识别但明示不可执行/非医疗声明;用餐方式;位置:街道商场园区/门店名/X附近;心情风格;sources 溯源 explicit/inferred/default;调试表示默认不回显原文)、组合规划与不编造铁律(价格缺失出局 / 算价失败无方案 / 预算筛除 / 优惠不默认叠加 / 营养零覆盖排除部分覆盖降权)、边界案例(预算刚好相等 / 超出一分钱 / 券提交无效果 / 无可行解可解释失败)、最多三个可比较方案与排序、RPG 不变性(文案不改数量/总价/剩余预算)、确定性、真实 MCP 客户端(同步门面 / 401·429 状态码还原 / 超时 / SDK 缺失 / 业务错误 / 协议级 isError / 非法返回结构,用假会话不访问网络)、MCP 效率与可靠性(调用次数上限 / 分工具超时 / 瞬时错误退避重试 / 401 不重试 / 429 降速 / 任务内去重 / 短 TTL 缓存与过期 / 算价与优惠券绝不缓存 / 失败不进缓存 / 部分工具失败隔离)、防御式字段适配(联调实测格式:文本信封提取、菜单骨架、算价分单位、营养文本解析)、门店解析流程(多候选不默默选择 / 无位置要求补充 / 外送 beCode 链路 / 无效编码明确报错)、CLI 全流程(demo / quest / stores / tools / smoke / 帮助与退出码)、只读冒烟测试(全通过 / 某步失败 / 无门店不编造 / 仅只读红线 / 连接失败 / 缺 Token)、四个离线演示的场景固化、成就卡安全与结构(恶意 HTML/脚本/引号/特殊字符转义、LIVE/DEMO 标签、装备溯源、价格四段、未核验与营养缺失显未知、无外部资源、移动端与打印适配)、分享卡转义与必备声明、Skill 文档一致性(skill.md 提到的工具名均真实存在/必备章节齐全/解析规则与引擎一致/数值示例与引擎输出一致)、券双字段协议契约(couponId+couponCode 成对提交 / 600010 忠实还原 / 演示券均带码 / 三种券形态统一)、10 类失败路径矩阵(非法参数 / 缺字段 / 业务错误 / 协议级 isError / 部分工具失败 / 价格缺失 / 响应结构变化 / 超时 / 401 / 429,由元测试断言矩阵齐全且用例真实存在,防退化)、MCP 全链路体检(403 映射 / 连接超时与读取超时分离 / 无信封响应强制失败+白名单宽松 / 显式传输层超时 / doctor L1 探针分类且不携带 Token / 五层状态汇总 / 真实模式不回退 Demo)、MCP 工具契约审计(官方 list_tools Schema 快照 verbatim 落盘 tests/fixtures/;端到端传参对照官方 Schema:必填齐备/无官方外参数/枚举合法/类型正确;beCode 传递规则到店禁传/外送必传;优惠券 per-item 挂载与容量守卫;三态台账守卫防新工具绕过审计)。、安全与隐私审计(本地 Web 服务 Host 白名单挡 DNS rebinding + Origin 校验挡跨站代打真实查询/ 默认只监听 localhost;前端无内联脚本与 innerHTML、零 Token 引用;Token 不进审计/日志/卡片/进度存档;进度存档零个人信息且 opt-in;全仓无硬编码 Token 与手机号)。
MCP 调用效率(2026-10-14 实测,优化前后对比)
| 指标 | 优化前 | 优化后 |
|---|---|---|
| 演示模式单任务调用 | 24 次 | 11-15 次(最坏 15,-40%) |
| 真实模式单任务调用 | 17 次 | 7 次(-59%) |
| 真实模式任务耗时 | 3.5 s | 1.8 s(-43%) |
| query-meal-detail | 10 次/任务 | 0 次(名称取自行算价 productList,缺名称才回退) |
| 每组合券试探 | ≤8 次算价 | ≤2 次(最优单券→最优双券,全失败才回退无券) |
优化手段(均不牺牲价格真实性):① 一次 calculate-price 同时完成「名称 + 单价」发现;② 任务内相同输入只读查询去重 + 短 TTL 缓存(时间 30s / 门店 2min / 菜单详情 5min / 营养 10min;优惠券与算价绝不缓存);③ 分工具超时(默认 30s / 算价 45s)+ 有限退避重试(网络与 5xx 最多 3 次,429 最多 2 次并降速,401 不重试直接提示)。每次任务的调用明细(序号/耗时/用途/成功状态/尝试次数)可在输出末尾的【MCP 调用审计】查看,也可用 tools/measure_calls.py 复测。
说明:测试中的「真实模式」用假会话验证引擎链路,不访问麦当劳服务器;真实服务器的联调实测记录见 MCP_INTEGRATION.md。
7. 已知限制(透明声明)
已于 2026-10-13 用官方 Token 实测验证(详见 MCP_INTEGRATION.md §3.5):
- 网络链路、
mcpSDK 1.27.2 双版本兼容、401/429 状态码还原; - 7 个工具的真实参数名与返回结构(门店查询 / 菜单骨架 / 餐品详情 / 价格发现 / 算价验价 / 营养文本);
- 多候选门店列出暂停、外送无地址时的明确说明。
仍未验证(需要特定账户状态):
- 优惠券字段与金额单位(联调账户当时无可用券;
items[].couponId挂载方式按官方 Schema 实现); - 外送全链路(需要账户已保存配送地址);
query-meal-detail的特制选项(modification)与套餐换选项——MVP 不展示,接口已预留。
8. 架构(分层)
flowchart TB
User["用户一句话<br/>预算35,想吃辣,两个人,尽量高蛋白"]
subgraph Parser["解析层(纯规则,无大模型依赖)"]
NL["nlparser.py<br/>预算/人数/口味/避免口味/营养偏好<br/>用餐方式/位置/门店名/心情/丰盛"]
end
User --> NL
NL -->|"Constraints<br/>(explicit / inferred / default 分级)"| SVC
subgraph MCPLayer["MCP 客户端层(同一接口两种实现)"]
BASE["mcp_base.py<br/>MCPClient 接口 + 防御式适配<br/>文本信封提取 / 菜单骨架 / 分单位金额"]
HTTP["mcp_http.py<br/>真实客户端:官方 mcp Python SDK<br/>Streamable HTTP + 同步门面<br/>超时/重试/去重缓存/调用审计"]
MOCK["mcp_mock.py<br/>演示客户端:包内演示数据(离线)"]
BASE --- HTTP
BASE --- MOCK
end
SVC["service.py 编排层<br/>门店解析 → 菜单骨架 → 价格发现<br/>→ 优惠/营养 → 组合验价"]
subgraph Planner["规划层"]
PL["planner.py<br/>候选模板 + 硬避免(不吃辣/不要饮料)<br/>优惠试探(不默认可叠加)<br/>营养覆盖度门控 + Top3 可比较方案"]
end
subgraph Output["输出层(只包装事实,不改数值)"]
RPG["rpg.py<br/>任务名/故事/装备绰号"]
CARD["cards.py<br/>HTML / Markdown 成就卡<br/>LIVE/DEMO 徽章 · 溯源编码 · 未知不填空"]
end
HTTP -->|"官方 MCP Server<br/>https://mcp.mcd.cn"| SVC
MOCK --> SVC
SVC --> PL
PL --> RPG --> CARD
CARD --> Final["可分享成就卡<br/>价格四段 + 属性面板 + 数据来源"]
style HTTP fill:#fff3cd,stroke:#da291c
style MOCK fill:#f8f9fa,stroke:#6c757d
style Final fill:#d4edda,stroke:#1f9d55
分层铁律:业务逻辑(planner)、工具调用(mcp_*)、游戏文案(rpg)、展示层(cards)严格分离——RPG 文案只做叙事包装,不能改变商品数量、总价与剩余预算(有专项测试断言)。
从用户输入到权威价格核验的数据流
sequenceDiagram
actor U as 用户
participant NL as nlparser
participant SV as service
participant MCP as 麦当劳官方 MCP
participant PL as planner
participant CD as cards
U->>NL: "预算35,想吃辣,两个人,尽量高蛋白"
NL->>SV: Constraints(显式限制/软偏好/默认值分级)
SV->>MCP: now-time-info(失败不阻塞)
SV->>MCP: query-nearby-stores(位置→门店;多家列出不默选)
MCP-->>SV: 门店编码 storeCode
SV->>MCP: query-meals(菜单骨架:只有编码/分类/标签)
MCP-->>SV: categories[].meals[].code
SV->>MCP: calculate-price(短名单各1件=价格发现)
MCP-->>SV: productList:productName + subtotal(分)
Note over SV,MCP: 名称与单价来自同一次权威返回;<br/>productList 缺名称才回退 query-meal-detail
SV->>MCP: query-store-coupons / query-my-coupons / list-nutrition-foods
MCP-->>SV: 真实券池 + 营养数据
loop 每个候选组合(最多3个方案)
SV->>MCP: calculate-price(组合验价,含券试探)
MCP-->>PL: 权威应付总价(分)
end
PL->>PL: 预算过滤 + 评分排序 + 符合/不完全符合说明
PL->>CD: PlanOption[]
CD-->>U: 成就卡(LIVE/DEMO 徽章 + 价格四段 + 溯源)
真实数据流要点(2026-10-14 联调实测):官方 query-meals 只返回餐品编码与标签,没有名称和价格——名称与单价来自一次 calculate-price 探测的 productList(金额单位为分,如 2250=¥22.50),query-meal-detail 仅在 productList 缺名称时回退调用;组合验价同样只认 calculate-price 返回,绝不用菜单展示价求和冒充结算结果。
9. 安全与合规
- Token 不进仓库:只从环境变量
MCD_MCP_TOKEN读取;mcp-config.example.json仅含占位符;.gitignore显式忽略.env/mcp-config.json; - 审计链路:每次任务输出
data_sources与mcp_calls,可追溯每个数字的来源; - 无实际影响操作:MVP 不接入下单;涉及真实资金/库存的工具(
create-order等)在TOOLS_RESERVED中显式预留; - 非官方声明:所有输出(终端 + 卡片)均带非官方项目声明。
10. 目录结构
burger-quest/
├── README.md 本文件
├── CONTEST_DECLARATION.md 参赛声明(官方文件,内容未改动)
├── MCP_INTEGRATION.md MCP 集成说明(工具/调用链/业务价值/SDK 兼容)
├── skill.md WorkBuddy Skill 入口
├── workbuddy.md WorkBuddy 开发上下文(专项奖励材料)
├── mcp-config.example.json MCP 配置模板(仅占位符)
├── requirements.txt
├── pyproject.toml
├── burger_quest/ 引擎源码
│ ├── models.py 数据模型(金额用「分」,杜绝浮点误差)
│ ├── nlparser.py 自然语言解析
│ ├── mcp_base.py MCP 接口 + 防御式适配
│ ├── mcp_http.py 真实 MCP 客户端(同步门面 + 后台事件循环)
│ ├── mcp_mock.py 演示 MCP 客户端
│ ├── planner.py 套餐规划引擎
│ ├── rpg.py RPG 文案层
│ ├── cards.py 分享卡渲染
│ ├── service.py 编排层(门店解析/错误处理)
│ ├── smoke.py 只读 MCP 冒烟测试
│ ├── cli.py 命令行入口
│ └── data/demo_data.json 演示数据(随包分发,明确标注非实时)
├── tests/ 568 个测试(含 official_tools_schema.json 官方 Schema 快照)
└── examples/ 可复现演示与示例卡片
11. 贡献
git clone https://github.com/yaohanbo1-hue/burger-quest.git
cd burger-quest
pip install -e ".[dev]" # 运行时 + 测试依赖
python -m pytest tests/ -q # 568 个测试
python -m burger_quest smoke --demo # 只读冒烟(离线)
贡献约定:
- 铁律不可破:不编造餐品/价格/优惠/库存;新增能力必须带测试(含失败路径);
- 文档与代码同步:改了工具名/调用链/解析规则,
tests/test_skill_docs.py会校验skill.md与代码的一致性; - 安全底线:不提交任何真实 Token 或配置;HTML/Markdown 输出必须转义用户与 MCP 文本;CI 的 secret scan 会拦截;
- 演示数据:
burger_quest/data/demo_data.json为演示数据,结构须与真实 MCP 返回保持兼容,不得冒充实时数据; - 提交信息用中文描述「改了什么 + 为什么」,大改动附实测数据。
12. 参赛信息
- 比赛:麦当劳程序员创意开发大赛(https://tch.cloud.tencent.com/contest/48)
- 官方 MCP 使用指南:https://github.com/M-China/mcd-mcp-server
- 本作品由 WorkBuddy 辅助开发(见 workbuddy.md)
Reviews (0)
Sign in to leave a review.
Leave a reviewNo results found