1024 想请全组吃麦当劳,结果我先写了个拼单工具

起因:每次点下午茶,都有一个人在当财务
组里点下午茶基本是这样:群里接龙几十条,总有人被漏掉;门店券怎么用才最便宜,没人算得清;付完钱还得挨个说"你转我 24.9"。1024 程序员节快到了,大概又要来一遍。
刚好麦当劳中国开放了 MCP,也在办程序员节的开发比赛,我就趁机做了个小工具「麦麦拼单」(mcd-team-feast)。先说一句:我不是科班出身,这个项目基本是和 AI 一起 vibe 出来的,下面写的也都是边做边踩到的坑,大佬看了轻拍。
它做的事情很简单:开个团 → 链接丢群里,大家各点各的 → 它自己试券、挑最便宜的方案 → 每人该转多少算到分 → 出一张 git log 风格的账单卡。
接麦当劳 MCP:只读为主,金额单位是"分"
MCP 客户端是自己写的,零依赖,就是按 Streamable HTTP 走一遍 initialize → tools/list → tools/call,带上 Session Id。Token 只从本机环境变量读,不进仓库,网页版完全不需要。
真正用到的基本是只读工具:query-nearby-stores 找门店,query-meals 拉菜单,query-store-coupons 拿可用券,calculate-price 计价,另外 list-nutrition-foods 用来算热量。
几个踩到的点:
- 金额单位是分 。
calculate-price返回的原价、优惠、实付都是整数分。这反而是好事,我整个项目内部都用分算,最后才转成元显示,浮点误差直接绕过去。 - 券是挂在商品行上的 。不是"整单传一个券 ID",而是给某一份商品带上
couponId/couponCode。所以每张候选券都要拆出一份对应商品再去计价。 - 活动价别重复扣 。最早的版本里,我把"菜单价和实付的差额"和券的优惠又加了一遍,结果优惠被算了两次,账单卡上省得特别夸张。后来改成以
calculate-price的实付为准:各人菜单价之和减实付,就是能分摊的全部优惠,门店活动价单独列一行。 - 菜单上有些价格要另外买卡才享受(随单购早餐卡之类),拼单场景一律按原价算。
券组合搜索 + 按分分账
券的数量不多,所以没上什么高级算法,就是老实枚举:最多 3 张券、整单券最多 1 张,每种组合都丢给官方 calculate-price 算一遍,取实付最低的。价格以官方为准,我自己不猜优惠规则,这样就不会出现"工具说 30,结账 35"的尴尬。
分账规则是按每人原价占比分摊优惠:每人的优惠用 Math.floor 取整到分,剩下的尾差由发起人兜底,保证每人应付加起来 = 实付,一分不差。这条守恒有单独的测试盯着。
真实跑过一次(只计价,没下单):上海一家门店 5 人下午茶,10 件咖啡甜品小食,原价 ¥159.90 ,门店活动价优惠 ¥19.20,实付 ¥140.70 ,人均 ¥28.14。
账单卡:git log、PR、小票三选一
算完总得有个能发群里的东西。我做了三种主题:git log 提交记录风、GitHub PR 风、普通小票风。卡片是纯函数生成的 SVG,CLI 和网页共用一套,字体做了子集化内嵌进去,导出 PNG 在不同设备上长得一样。中文和等宽字体混排对齐这块折腾了挺久。

| PR 风 | 小票风 |
|---|---|
![]() |
![]() |
卡片底部还有一句梗签名 ,会按时段、人数、省钱比例、热量自动挑一句程序员梗,不满意可以换一句或者自己写。另外 feast blame 会评出今天的"钱包担当"和"热量担当",纯属娱乐。
实时拼单:开团 → 同步 → 锁单
一开始网页版是"链接模式":团的数据全编码在 URL 里,不经过服务器,但每个人改完都得把新链接发回群里,用起来有点累。
后来加了实时房间:团长开团拿到链接和二维码,成员填个昵称就能进来,谁在线、谁选好了、谁还在选,所有人实时看得到,团长还能设截止倒计时。到点了团长一键锁单,大家变成只读,看到最终分账;要改可以再解锁。

实现很朴素:服务端推送用 SSE,客户端操作用普通 POST,服务端是一个零依赖的 Node 文件。房间只存团名、昵称和点了什么,24 小时自动删除;断线会自动重连,同步服务连不上时自动退回链接模式,界面上会标明当前是哪种模式。想自己部署的话,仓库里有通用的部署模板。
做成 MCP Server,但故意不给下单工具
既然是 MCP 比赛,顺手把它自己也做成了一个 MCP Server(stdio),配一个 SKILL.md,在 Agent 里说一句"帮我们组拼一单麦当劳"就能开团、点餐、结算、出卡。
但这里我故意没暴露任何下单工具 。AI 帮你算账没问题,替你花钱还是算了。真要下单只能在终端手动执行 feast order --confirm <确认码>,确认码由门店、餐品和券组合哈希出来,团内容一变就失效,最后也只返回支付链接,付不付钱你自己决定。
几点体会
- 算钱的事交给官方接口 。自己模拟优惠规则一定会翻车,能调
calculate-price就别猜。 - 金额全程用整数分,再写一条"总和守恒"的测试,能省很多心。
- 和 AI 一起写代码,测试比代码更重要。AI 改起东西来很勇,那个优惠算两遍的 bug 就是这么来的,后来全靠测试兜住。现在一共 30 个用例,全程不碰真实接口。
- 能分享的结果比功能列表好传播。大家对算法没什么兴趣,但会把一张好玩的账单卡转到群里。
链接
这是「麦当劳程序员节创意开发大赛」的参赛作品,非官方产品。网页版用的是演示菜单(虚构价格),不用登录、不用 Token 就能完整走一遍流程;命令行也可以一行跑 mock:
bash
npx github:Jasonin2099/mcd-team-feast demo --mock --theme all
如果这周你们组也要点下午茶,可以拿去试试。觉得还行的话,顺手点个 Star 我会很开心,有问题或者新主题的点子也欢迎提 issue。

