mcd-experience-atlas

mcp
Guvenlik Denetimi
Uyari
Health Gecti
  • License — License: MIT
  • Description — Repository has a description
  • Active repo — Last push 0 days ago
  • Community trust — 16 GitHub stars
Code Uyari
  • network request — Outbound network request in src/build_atlas.py
Permissions Gecti
  • Permissions — No dangerous permissions requested

Bu listing icin henuz AI raporu yok.

SUMMARY

麦当劳体验活动地图 —— 生日派对/亲子读书会/品鉴会在全国281城3664家门店哪里能办,一张地图查完。基于官方MCP只读采集与可视化。

README.md

麦当劳体验活动地图 · mcd-experience-atlas

「我在成都,能办麦当劳生日派对吗?哪家店?多少钱?这周六还有位吗?」——一次查询就有答案。
基于麦当劳中国官方 MCP 的只读采集与可视化:生日派对、主题派对、亲子读书会、品鉴会、体验营在全国哪些城市、哪些门店、什么时段可以预约。

🗺️ 在线体验(无需安装,打开即用):https://johnny37.github.io/mcd-experience-atlas/

在线地图
MCP
tools
cities
stores
tests
license

麦当劳体验活动地图总览:全国 281 城可预约活动分布

全国体验活动分布总览 —— 圆点大小 = 可体验的活动款数,右侧为数据洞察(含 2 款全国零城市可约的下架活动、体验丰富度中位数 18 款等独家指标)


一、项目介绍

一句话定位

「我在成都,能办麦当劳生日派对吗?哪家店?多少钱?这周六还有位吗?」——一次查询就有答案。

麦当劳的体验活动(生日派对 / 主题派对 / 品鉴会 / 亲子读书会 / 体验营)分散在 App 的深层路径里,
用户需要逐级确认「城市有没有 → 哪家门店能办 → 哪天可约 → 几点有场 → 还剩多少位」。
本项目把这条链路的数据一次拉通,做成一张可以点开的全国地图。

核心数据(2026-10-13 实测快照)

指标 数值
可预约城市 281 个(含行政区划码 + 经纬度)
可办活动门店 3664 家
场次样本 157 个(覆盖 79 个城市)
体验品类 SPU 26 款(商城 SPU 合计 50 款)
MCP 工具(实测) 35 个
餐品营养表 160 项
当月营销活动 13 天 / 27 个事件
门店数 TOP5 城市 北京 365 · 上海 219 · 天津 136 · 佛山 118 · 成都 97

它解决什么问题

官方 App 里,上述五个问题需要逐级点开、逐个城市试才能回答,且没有任何横向对比视角。
本项目把这些数据一次拉通后,可以直接回答:

  • 我所在的城市能办哪些麦当劳体验活动?
  • 哪家门店能办?这周还有没有场次?多少钱?几个人起订?
  • 全国范围内,哪些活动其实「一个城市都约不了」?
  • 同样叫「生日派对」,不同城市能办到的款式差多少?

最后一条是本项目独有的指标——体验丰富度(按可体验 SPU 款数计):
全国中位数 18 款,最少 8 款,最多 23 款(福州 / 龙岩 / 泉州并列第一)。


二、安装方法

环境要求

项 要求
Python 3.8 及以上(实测 3.13)
第三方依赖 无。全部使用标准库实现
Node / 构建工具 不需要
操作系统 Windows / macOS / Linux 均可
MCP Token 仅采集时需要;查看地图和运行测试不需要

安装步骤

# 1. 获取代码
git clone https://github.com/johnny37/mcd-experience-atlas.git
cd mcd-experience-atlas

# 2. (可选)验证安装 —— 无需 Token,纯本地运行
python tests/test_toon.py               # 19 项
python tests/test_data_pack.py          # 20 项
python tests/test_booking_gates.py      # 35 项
python tests/test_contest_compliance.py # 24 项
# → 共 98 项,全部通过即说明环境就绪

# 3. 查看地图(无需 Token)
python -m http.server 8000
#    浏览器打开 http://localhost:8000/web/

在线预览(GitHub Pages)

仓库根目录的 index.html 是数据完全内联的单文件版本,
因此可以直接用 GitHub Pages 托管,访客打开链接即可看到地图,无需 clone:

Settings → Pages → Build and deployment → Source: Deploy from a branch
           → Branch: main / (root) → Save

本仓库已启用 GitHub Pages,在线地址:https://johnny37.github.io/mcd-experience-atlas/。
本仓库的 dist/index.html 与根目录 index.html 内容相同,均可单独分享。

配置 MCP Token(仅采集新数据时需要)

Token 在 https://open.mcd.cn/mcp 用手机号登录后申请。本项目按以下优先级读取,不接受也不生成任何本地凭证文件:

  1. 环境变量 MCD_MCP_TOKEN;
  2. WorkBuddy 的 ~/.workbuddy/mcp.json 中 mcd-mcp 连接器已配置的凭证
    (在 WorkBuddy 中接入过自定义连接器时自动生效,零额外配置)。
# 方式 A:环境变量
export MCD_MCP_TOKEN="你的 Token"

# 方式 B:在 WorkBuddy 中接入连接器(推荐)
#   左侧边栏 → 连接器 → 自定义连接器 → 配置 MCP,填入:
#   { "mcpServers": { "mcd-mcp": {
#       "type": "streamablehttp",
#       "url": "https://mcp.mcd.cn",
#       "headers": { "Authorization": "Bearer YOUR_MCP_TOKEN" } } } }
#   之后直接运行采集脚本即可,无需再设置环境变量。

配置模板见 mcp-config.example.json(仅含 ${MCD_MCP_TOKEN} 占位符)。

作为 Agent Skill 安装

本仓库同时是一个可被任意 MCP 客户端加载的 Agent Skill。将仓库路径加入客户端的技能目录即可,
SKILL.md 已按 WorkBuddy 标准格式声明了所需的 9 个只读工具与使用约束。


三、使用示例

示例 1:查看地图(最常用,无需 Token)

三种方式任选:

# A. 本地起服务(数据走 data/atlas.json)
python -m http.server 8000
#    打开 http://localhost:8000/web/

# B. 直接双击根目录的单文件版(数据已内联,无需服务器、无需 Token)
#    index.html

# C. 在线访问(零安装):
#    https://johnny37.github.io/mcd-experience-atlas/

页面上可以做的事:

  • 切换视图:「体验丰富度」看各城市能办多少款活动;「可办门店数」看门店密度;
  • 筛选品类:下拉选择「生日类派对 / 主题类派对 / 品鉴会 / 读书会」,
    地图即时高亮该品类可约的城市,不可约的缩为灰点;
  • 点选城市:右侧显示该城全部可办门店、7 天可约日期与每日场次数、场次时段与余位;
  • 搜索:输入「东单」可直接定位到麦当劳北京东单北大街餐厅;
  • 离线分享:直接双击根目录 index.html 或 dist/index.html,数据已内联,不需要服务器。

示例 2:采集最新数据(需要 Token)

python src/collect.py                    # 全量:281 城门店 + 79 城场次抽样
python src/collect.py --deep             # 深采:每个城市首店都取日期与场次
python src/collect.py --resume           # 中断后续采,跳过已完成的城市
python src/build_atlas.py                # 打包成 web/ 使用的 data/atlas.json

实际输出:

[0/7] tools/list → 35 个工具
[1/7] mall-points-products → 50 个 SPU
[2/7] 体验品类城市覆盖 {'读书会': 281, '生日类派对': 281, '主题类派对': 279, '品鉴会': 36} → 去重后 281 城
[3/7] 门店采集完成 → 3664 家
[4/7] 场次抽样 → 79 城 / 157 个场次
[5/7] list-nutrition-foods → 160 项(TOON 已解析)
[6/7] query-lottery-info → 麦麦积分抽奖,10 项奖品,单次 24 积分
[7/7] campaign-calendar → 13 天 / 27 个事件

示例 3:作为 Agent Skill 对话使用

加载本仓库为 Skill 后,可以直接这样问:

用户:我在成都,想给孩子办个麦当劳生日派对,这周六还有位吗?多少钱?

Skill 会依次调用 mall-points-products → query-party-city → query-party-store
→ query-party-store-date → query-party-store-session,回答:

成都可以办 6 款生日派对(开心鸭尊享版、迪士尼疯狂动物城主题尊享版等),
全市有 97 家门店可办,你能体验的活动共 20 款,高于全国中位数(18 款)。

以成都高新区大魔方餐厅的采样场次为例,10-14 至 10-22 连续 9 天可约,
这周六(10-17)有 2 个场次:10:30–12:00 和 14:00–15:30,
5~12 人,当前各余 12 位。注意这是「亲子读书会」品类的场次与价格(¥45.00),
生日派对的场次时间与价格需按具体 SPU 再查一次。实际下单请在麦当劳 App 或门店完成。

Skill 内置的硬约束(见 SKILL.md):只读,禁止调用任何写操作工具;
金额单位为分需自行换算;涉日期必须实时调用 now-time-info;不得编造未返回的门店。

示例 4:运行测试

python tests/test_toon.py
python tests/test_data_pack.py

四、地图功能

功能 说明
体验丰富度视图 圆点大小 = 该城市可体验的活动款数(按 SPU 计)。官方渠道没有这个指标
可办门店数视图 圆点大小 = 该城市可办活动的门店数
品类筛选 下拉或点击覆盖条形图切换品类,地图即时高亮;不可约城市缩为灰点
7 天场次日历 选中城市后显示一周内每日可约场次数量,零场次的日子标灰
门店搜索与筛选 支持按城市名或门店名全局搜索;可按「仅可预约」过滤门店
数据洞察面板 自动算出「全国零城市可约的活动」「覆盖最窄的在售活动」「门店集中度」等结论

五、预约功能(带确认闸,默认关闭)

地图与查询全部只读;src/book.py 额外提供了可真实下单的能力,但默认关闭,
只在满足下列全部条件时才创建订单:

  1. 用户精确说出「确认创建订单」——「试试看」「可以就下单」等模糊措辞一律视为未确认;
  2. 用户核对过完整回显(活动 / 门店 / 日期 / 场次 / 人数 / 单价 / 预估总额);
  3. 用户提供的订单指纹与当前参数算出的一致(防止「确认的是 A、下单的是 B」);
  4. 前置校验通过:人数在 [partyMin, partyMax] 内、余位 > 0、shopId == 5、
    partyType 与商品规则一致、满足提前预订天数。
# 第一步:只读,任何时候都安全,可反复运行
python src/book.py --spu-id 8516 --city 南宁市 --store 3570247 \
    --date 2026-10-17 --session 36915550 --count 6

# 第二步:写操作,真实创建订单
python src/book.py --spu-id 8516 --city 南宁市 --store 3570247 \
    --date 2026-10-17 --session 36915550 --count 6 \
    --confirm 确认创建订单 --fingerprint <第一步给出的指纹>

实测回显效果(南宁水晶城餐厅,生日派对):

活动名称   :麦当劳一起开心鸭尊享版生日派对
派对类型   :包场          人数规则:5-12 人(年龄 4-12)
门店       :麦当劳南宁水晶城餐厅(青秀区金湖路61号)
预约日期   :2026-10-17    场次:12:00 – 13:30   余位 12
参与人数   :6 人          单价 ¥138.00/位      预估总额 ¥828.00
订单指纹   :74E14B2F22B1

三条不可妥协的安全约束:

  • 绝不重试 party-order-create——若订单已创建但响应未读回,重试会产生重复订单,
    这是金钱相关的不可逆操作。mcp_client.call() 对写操作工具强制 retries=0。
  • 默认黑名单——mcp_client.WRITE_TOOLS 默认拦截全部 7 个写操作工具,
    须显式 allow_write=True 才放行,且只有 book.py 会这么做。
  • 审计留痕——每次下单尝试写入 data/booking_audit.log,便于事后核对。

这些约束由 tests/test_booking_gates.py(35 项)锁定,全部离线运行、不发真实请求。


六、地图合规说明

默认视图是纯 SVG 散点图:只绘制城市坐标点与经纬网格,不描绘任何行政边界、不加载任何在线底图瓦片,
因此不涉及领土表示,可安全托管到 GitHub Pages 等任意静态空间。

如果你需要带底图的视图,请自行接入腾讯地图 / 高德 / 百度 / 天地图这四家具备资质的服务商,
并遵守《中华人民共和国测绘法》及相关地图合规要求。请注意:

  • 禁止使用 Google Maps、Apple Maps、Bing 海外版、OpenStreetMap 及 Mapbox 等未取得境内测绘资质的来源;
  • 前端代码中不要硬编码任何地图 Key,应通过自己的后端代理或域名白控;
  • 本项目 data/ 中的经纬度来源于麦当劳 MCP 返回值,坐标系以官方为准,
    接入不同底图前请自行核对是否存在 GCJ-02 / BD-09 偏移。

七、技术要点

这个项目的技术含量不在前端,而在摸清一条没有文档的链路。

1. 链路入口是积分商城,不是「派对工具」

query-party-city 这类工具的入参 spuId 来自 mall-points-products——
也就是说,体验活动预约的入口是积分商城选商品,而不是某个独立的派对查询工具。
官方门户文档完全没有记载 query-party-* 系列,只读门户文档的开发者不会发现这条链路。

完整链路:

mall-points-products → query-party-city → query-party-store
  → query-party-store-date → query-party-store-session
  →(party-order-create,写操作,本项目不调用)

2. 接口返回不是纯 JSON

所有 tools/call 返回的是「Markdown 说明 + 内嵌一行原始 JSON」混合体。
不能取「第一行看起来像 JSON 的内容」——说明文档里也有 JSON 示例。
本项目从 {"success" 起用 JSONDecoder.raw_decode 找边界(见 src/mcp_client.py)。

3. 营养表是自研的 TOON 格式

list-nutrition-foods 返回的不是 JSON 数组,而是:

[160]{productName,nutritionDescription,energyKj,energyKcal,protein,fat,carbohydrate,sodium,calcium}:
  猪柳麦满分,null,1288,308,16,16,24,781,213
  巨无霸,null,2145,513,27,28,44,1010,190

坑在于:在原始 HTTP 响应文本中,行分隔符是两字符序列 \n(反斜杠 + n),不是真实换行,
直接 split('\n') 会得到 0 行。本项目 src/toon.py 对两种形态都做了归一化,并有回归测试锁定。

4. 其他实测确认的接口特性

详见 MCP_INTEGRATION.md 第 5 节的 10 条清单,包括:
query-nearby-stores 单次只回 5 家且坐标返回 0.0、available-coupons 不返回 couponId、
鉴权失败返回 403 + 业务错误信封、协议仅支持 ≤2025-06-18 等。


八、目录结构

mcd-experience-atlas/
├── README.md                 项目介绍 / 安装 / 使用示例 / 目标用户
├── MCP_INTEGRATION.md        实际调用的 Server/Tool/流程/业务价值(参赛必需)
├── CONTEST_DECLARATION.md    参赛声明(官方原文,未做任何改动)
├── mcp-config.example.json   脱敏配置示例,仅含 ${MCD_MCP_TOKEN} 占位符
├── workbuddy.md              使用 WorkBuddy 开发的对话上下文
├── SKILL.md                  Agent 技能描述(可被任意 MCP 客户端加载)
├── LICENSE                   MIT
├── index.html               单文件地图页(数据内联,供 GitHub Pages 根目录托管)
├── data/
│   ├── atlas.json            前端使用的打包数据(build_atlas.py 生成)
│   ├── cities.json           281 城 + 经纬度 + 品类覆盖矩阵
│   ├── stores.json           各城市可办门店全量
│   ├── sessions.json         场次抽样(79 城 / 157 场次)
│   ├── experiences.json      50 个商城 SPU
│   ├── nutrition.json        160 项营养表(TOON 已解析)
│   ├── lottery.json          积分抽奖活动与奖池
│   ├── campaign_calendar.json 当月营销活动日历
│   ├── live_verification.json 通过 WorkBuddy 连接器的全链路实时验证记录
│   ├── tools_snapshot.json   tools/list 实测元数据
│   └── tools_reference.md    35 个工具的完整描述与入参 Schema
├── src/
│   ├── mcp_client.py         MCP 客户端(混合响应解析、429 退避、写操作黑名单)
│   ├── toon.py               TOON 解析器(含字面量 \n 归一化)
│   ├── collect.py            采集脚本(支持 --deep / --resume)
│   ├── build_atlas.py        数据打包 + 单文件版本生成(同时输出根 index.html)
│   └── book.py               体验活动预约:带确认闸的下单流程(默认只读)
├── web/
│   ├── index.html            图谱页面(零依赖)
│   └── atlas.js              投影 + 视图切换 + 品类筛选 + 搜索交互
├── dist/
│   └── index.html            单文件版本(数据内联,可离线双击打开)
├── docs/
│   └── map-overview.png      README 首屏截图(在线演示实拍)
└── tests/
    ├── test_toon.py              19 项:TOON 解析与混合响应提取
    ├── test_data_pack.py         20 项:数据包结构与自洽性
    └── test_contest_compliance.py 24 项:参赛要求逐项合规校验

九、测试

python tests/test_toon.py               # 19 项:TOON 解析与混合响应提取
python tests/test_data_pack.py          # 20 项:数据包结构与自洽性
python tests/test_booking_gates.py      # 35 项:下单确认闸的安全属性
python tests/test_contest_compliance.py # 24 项:参赛要求逐项合规校验
# → 共 98 项,全部通过

三个测试各自锁定一类风险:

  • test_toon.py——TOON 的字面量 \n 陷阱、混合响应中「不能误取说明文档里的 JSON 示例」;
  • test_booking_gates.py——锁住「默认只读 + 显式确认 + 写操作不重试」三条安全属性:
    只读客户端必须拒绝全部 7 个写操作工具、模糊确认串不构成确认、
    订单指纹随任一要素变化、越界人数与零余位被拦截。全部离线运行,不发真实请求。
    test_data_pack.py——data/atlas.json 的计数一致性、城市字段完整性、
    覆盖矩阵的 cityCodes 可解析、门店索引无孤立键、场次价格单位为分;
  • test_contest_compliance.py——按官方参赛要求逐项校验:四个必需文件名未改动、
    README 四个必备章节齐全、CONTEST_DECLARATION.md 与官方原文 MD5 一致(内容未改动)、
    mcp-config.example.json 仅含环境变量占位符、仓库内无任何真实凭证。

最后一个测试存在的意义:参赛要求里有「文件名不可改动」「内容不可修改」这类硬约束,
一旦日后重构目录或误改文档,它会立即失败,避免到报名截止前才发现问题。


十、采集的健壮性

长任务中断是真实会发生的事(实测遇到过代理 502)。采集脚本为此做了三件事:

  1. 增量落盘:每 25 个城市写一次 stores.json,中途失败不丢已采数据;
  2. 断点续采:--resume 复用上次已完成的城市,不从头再来;
  3. 网络错误与业务错误分离:连接层故障(URLError)上抛重试,
    服务端错误码(401/429)按语义处理,两者不会混为一谈。

十一、目标用户

  • 家长:想给孩子办一场麦当劳生日派对,先确认自己城市有没有、哪家店、什么价位;
  • 行政 / HR:筹备团建或品鉴会,需要横向对比可选门店与时段;
  • 亲子社群 / 本地生活内容创作者:需要「哪里能办」的确定性答案;
  • 麦当劳 MCP 开发者:需要一条完整的体验活动链路参考实现与踩坑记录。

十二、参赛文件清单

按《麦当劳程序员创意开发大赛》要求逐项对照:

# 文件 官方要求 本项目状态
1 README.md 必须,文件名不可改动;含项目介绍、安装方法、使用示例、目标用户 ✅ 四项分别为第一、二、三、十一节
2 CONTEST_DECLARATION.md 必须,文件名及内容均不可改动 ✅ 官方原文件,MD5 与官方仓库一致
3 MCP_INTEGRATION.md 必须,文件名不可改动;说明 MCP Server、Tool、调用流程和业务价值 ✅ 见该文件第 1~6 节
4 源代码 必须,形式不限 ✅ src/ 4 个 Python 模块 + web/ 前端 + tests/
5 workbuddy.md 参加 WorkBuddy 专项奖励必须 ✅ 开发过程、决策与安全自查
— mcp-config.example.json 活动规则另列,仅允许环境变量占位符 ✅ 仅含 ${MCD_MCP_TOKEN}
— 项目创建时间 2025-12-25 ~ 2026-10-25 ✅
— 真实使用麦当劳 MCP 必须 ✅ 9 个只读工具,35 个工具清单已实测
— 真实使用腾讯 WorkBuddy 拿专项奖励必须 ✅ 通过 WorkBuddy 连接器开发与验证

十三、免责声明

本项目为独立参赛作品,由参赛者独立开发,非麦当劳官方产品,也未获得麦当劳品牌背书。
项目展示的所有数据来源于麦当劳中国 MCP 的只读接口返回,仅供参考;
活动规则、价格、余位及供应状态以麦当劳官方渠道的实时结果为准。
本项目不执行任何下单、领券、抽奖或积分消费操作。
对「麦当劳」「麦麦省」「麦乐送」等名称的使用仅限描述性指代,
不构成对麦当劳及其关联方商标的任何授权,亦不暗示官方背书。

License

MIT

Yorumlar (0)

Sonuc bulunamadi