麦当劳的开发者大赛:麦当劳 MCP 实测——一个藏在 inputSchema 里的参数

0 阅读3分钟

麦当劳 MCP 实测:一个藏在 inputSchema 里的参数

麦当劳放出了官方 MCP Server(mcp.mcd.cn,35 个 tool),我拿它做了个小工具, 顺手记录了 6 条官方文档没写、但会直接导致调用失败的契约。

先说最有意思的那条 —— 它解释了一个现象。

一、roundList 只存在于 inputSchema 里

calculate-price 的正文 description 完全没有提到 roundList 这个参数。 它只出现在 inputSchema.properties.items.items.properties.roundList, 描述只有三个字:「套餐子项」。

{
  "items": {
    "items": {
      "properties": {
        "roundList": {
          "description": "套餐子项",
          "type": "array"
        }
      }
    }
  }
}

而 roundList 正是套餐内商品换购的唯一入口。

也就是说:麦当劳开放了「换掉套餐里的可乐」这个能力, 但因为参数只躺在 schema 里、正文没写,绝大多数人根本没发现它。

——这大概就是为什么当时榜单上 100 个项目里,没有一个做套餐换购。

二、单位方向相反(最坑的一个)

# query-meals 返回的价格:元字符串,schema 里没写单位
{"currentPrice": "33.5"}     # 字符串!单位是元

# calculate-price 接收的价格:整数,schema 明写"单位分"
{"price": 3450}              # 整数,单位是分

一个进一个出,一个元一个分,一个字符串一个整数。 schema 里只有后者标了单位。我第一次调用时直接 int("33.5") 崩了。

我的做法:在 adapter 层统一转成整数分,全项目只在这一层接触原始字段。

def parse_money(raw: str) -> int:
    """元字符串 → 整数分。"""
    return round(Decimal(raw) * 100)

三、前导说明格式不统一

MCP 返回的 JSON 前面会垫一段说明文字,但不是每个 tool 都有:

  • query-meals → 带 Original Response 前导
  • query-nearby-stores → 不带

所以解析前必须先剥前导,且不能硬编码那行标题:

def strip_preamble(text: str) -> str:
    """剥掉 MCP 响应前导说明,返回纯 JSON。"""
    start = text.find("{")
    if start < 0:
        raise ValueError("响应中找不到 JSON")
    return text[start:]

四、query-my-coupons 返回的是 Markdown,不是 JSON

这个 tool 不吐 JSON。想结构化处理只能自己解析 Markdown 表格。 我直接绕开了:这个数据对我的场景不是必需的。

教训:不要假设同一种传输格式。

五、条件价:canWithOrder

query-meals 返回的 currentPrice 不总是你能真正付的价格。

{"discountType": "随单购麦金卡优惠", "canWithOrder": True}

这类餐品标的是「买卡才成立」的价格。不买卡,按划线原价付款。 我实测到最大差额:¥49.90 vs ¥114.50,差 ¥64.60。

判断逻辑:

def is_conditional(item) -> bool:
    """是否为「随单购」条件价。"""
    return "随单购" in (item.discount_type or "") or item.can_with_order

六、双人餐位的 minQuantity = 2

双人套餐的座位 minQuantity 是 2, 构造 roundList 时 quantity 必须取 max(1, min_quantity), 否则核价出来的金额不对(少算一份)。

quantity = max(1, round_.min_quantity)

最终做出的东西

一个零依赖的套餐换购分析工具:只用 Python 标准库,不需要 MCP Token, python demo.py 直接跑 —— 因为我把真实响应录进了 fixtures/。

实测门店 40 个套餐的结论:

  • 33 个(82%)存在「与默认同价的替换选项」,合计 129 个
  • 20 个(50%)默认搭配比可达最低价更贵,最高贵 ¥4.00
  • 单个套餐最多 8 个免费替换(全明星双人分享餐八件套,54 个候选选项)
  • 18 个餐品是「随单购麦金卡」条件价,最大差 ¥64.60

所有金额都由 calculate-price 真实核价回填,不是估算。

换购说到底就一句话:这些都是同一份钱能买到的东西,只是菜单不会告诉你。

项目(MIT,含 52 项单测 + CI): github.com/huwell88/mc…

我参加了麦当劳的开发者大赛,如果这篇对你有用,欢迎点个 Star 支持一下 🙏


补充:我还把 roundList 的调用契约写成了文档(MCP_INTEGRATION.md), 以及一份完整的数据溯源(fixtures/README.md,含采集门店与时间), 接入 McD MCP 的话可以直接参考。