mcd-order
Skill19 starsChanged 3 months agoMITView on GitHub
Find the cheapest way for a group to order McDonald's China (麦当劳): treat combos as cheap component containers, redistribute their parts across people, and use coupons (优惠券) and points (积分) to cover everyone's exact items for the least cash. Use when several people order McDonald's together and want the cheapest split / 拼单最省 / "cheapest way to order X, Y, Z". Runs on the mcd-mcp MCP server.
In this repository18 other files it gives its agents.
- base-breakout-scanSkill
- commit-contextSkill
- conviction-funnelSkill
- distill-memorySkill
- distill-moduleSkill
- fable-mindSkill
- hackernewsSkill
- map-moduleSkill
- mean-reversion-scanSkill
- momentum-scanSkill
- premarket-briefSkill
- regime-scanSkill
- review-iterateSkill
- snapback-scanSkill
- sparse-checkoutSkill
- statusline-vocabSkill
- wallstreetbetsSkill
- yfinanceSkill
The file
We split this skill into the rules it gives an agent
6 rules. About 0 are common, 4 are distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈4
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 41
Run the whole pipeline (Steps 1–7) silently: don't narrate each batch, don't post intermediate findings, don't walk through the …
- Distinctive: line 124
Don't fabricate menu, price, coupon, or points data to work around a missing server — the whole point is real numbers.
- Distinctive: line 176
When you do, present the candidate SKUs (or the size/spec resolutions, each priced) as an AskUserQuestion menu so the user taps the answer …
- Very specific: line 260
If the user has no non-expiring points, or you genuinely can't estimate any redemption value, omit minCashSavedPer100Points entirely — the …
- Very specific: line 271
Read each line's product-level subtotal as that option's real unit price and feed those into the solver (Step 6) instead of the sticker …
- Distinctive: line 352
Don't invent data: if a tool fails, report it and retry/ask — never fill in plausible prices or coupons.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
McDonald's Order Optimizer
The insight this skill automates: a McDonald's combo (套餐) is priced below the sum of its parts, so a combo is really a cheap container of components. When several people order together, the naive "one combo each + extra singles" often pays for items nobody wanted. If you instead treat the group's order as one multiset of items to cover and let combos' components be redistributed across people (using each combo's free in-combo swaps), you can usually cover the same food for less — and coupons + points push it lower still.
Finding that cheapest combination by hand is slow combinatorics. A bundled
solver (scripts/optimize.py, stdlib only) does it deterministically; your job
is to gather the real menu/coupon/points data from the mcd-mcp MCP server,
shape it into the solver's input, verify the top candidates' true prices, and
present the plan. This skill is an advisor by default — it does NOT place the
order unless the user explicitly asks afterward.
A note on language: this SKILL.md and references/modeling.md are instructions
for you (the model) and are written in English. Chinese appears only where it's
functional — mcd-mcp's own domain terms (glossed in English) and the menu/coupon
data, which comes back in Chinese. Anything you show the END USER (the install
prompt below, the final plan in Step 8) should match the language they're writing
in — Chinese for a Chinese speaker, English for an English one — not hardcoded.
<SKILL_DIR> below is the directory containing this SKILL.md — substitute its
absolute path when running.
Execution style — heads-down by default, present once
Run the whole pipeline (Steps 1–7) silently: don't narrate each batch, don't post intermediate findings, don't walk through the combinatorics in prose as you go. The user sees exactly one thing — the final plan in Step 8. The dominant wall-clock cost on this task is the work between tool calls — your own reasoning plus the text you generate — not the remote MCP calls, which take only seconds per batch. So cut both: don't just go quiet, also think less (lean on the solver, per the first bullet).
- Lean on the solver; don't re-derive the optimum by hand.
optimize.pydoes the combinatorics — your job is to feed it real prices, not to hand-prove the answer in prose first (that doubles the work). - Probe every candidate in the single Step 5b batch. Price all the
containers you might use (every four-piece / 随心选 / three-piece code), not just
the obvious ones — you already hold every
productCodefromquery-meals. Missing one (e.g. a burger's four-piece) forces a secondcalculate-priceround-trip mid-solve. Step 5b already mandates one batch; the point here is completeness within it. - Speak mid-run only when blocked — an ambiguous SKU, a tool error, the server missing. Otherwise stay quiet until the plan.
This is the default. If the user explicitly asks you to show your work / explain the reasoning, narrate freely instead.
Asking the user — offer a menu, not a blank prompt
Whenever you need a decision from the user — the up-front ones (scenario, store) just as much as the mid-run blockers above — prefer the AskUserQuestion tool with concrete, selectable options over a typed question. The user taps a choice and hits Enter instead of spelling out a store name, a drink size, or a yes/no — it's faster, can't be mistyped, and shows them the choices that actually apply (real menu names, real nearby stores) rather than making them recall them. Free text stays the fallback only for input that can't be enumerated — a brand-new delivery address, or a city to search when nothing's on file yet. You rarely need a separate typed prompt for that: every AskUserQuestion already carries an "其他/Other" choice, so list the enumerable options and let "其他" absorb the open case inside the same prompt.
Build the options from live data wherever a list already exists, and label them in the user's language (the menu/store names arrive in Chinese — leave those as-is):
- Scenario → the four service types: 到店自取 / 得来速 / 麦乐送 / 团餐.
- Store → what
query-nearby-storesreturns. Try favorites first (searchType 1): if they have any, they pick one with zero typing. A question caps at 4 options, so when more stores come back, offer the nearest/favorite few and let the built-in "其他" choice cover the rest — i.e. search by a typed city/keyword, the one part that's unavoidably free text. - Delivery address → the saved addresses from
delivery-query-addresses; only ask them to type one when none exists. - Ambiguous SKU ("可乐" → which size? zero-sugar?) → the matching menu SKUs.
- Size/spec conflict (wants a large coke, the combo includes medium) → the resolutions, each with its price (e.g. 加钱升大杯 / 保留中杯 / 单点大杯).
- Claim coupons first? → only if you'd surface it at all (it's optional, not a
default step — don't inject a prompt just to have one): a yes/no on auto-claiming
everything from 麦麦省 (
auto-bind-coupons) before optimizing. - Place the order (Step 8) → the final go/no-go, and the pick between close plans.
This serves "present once", it doesn't fight it: menus appear only at the real decision points above, never as chatter. AskUserQuestion can hold several questions in one prompt — batch the independent ones (e.g. the ambiguous-size question for two different people) so the user clears them in a single pass instead of one prompt each.
Step 0 — Require the mcd-mcp server (install gate)
Everything here depends on the mcd-mcp server (tools named mcp__mcd-mcp__*,
e.g. mcp__mcd-mcp__now-time-info). Before doing anything else, confirm it's
connected. If those tools are unavailable / a call errors with "no such tool" or
a connection failure, stop and help the user install it instead of trying to
proceed without data. Tell them (in their conversation language) the gist below:
This skill needs the McDonald's China MCP server (
mcd-mcp), which isn't connected yet. Setup (see https://github.com/M-China/mcd-mcp-server):
- Get a token: visit https://open.mcd.cn/mcp, sign in with phone verification, open the console, and activate your MCP Token.
- Register the server with Claude Code, e.g.:
(or add the same block underclaude mcp add-json mcd-mcp '{"type":"streamablehttp","url":"https://mcp.mcd.cn","headers":{"Authorization":"Bearer YOUR_MCP_TOKEN"}}'mcpServersin your MCP config). It's a remote/hosted server — nothing to download. ReplaceYOUR_MCP_TOKEN.- Restart Claude Code so the
mcd-mcptools load, then ask again.
Don't fabricate menu, price, coupon, or points data to work around a missing server — the whole point is real numbers.
What you'll do (pipeline)
0. Confirm mcd-mcp is connected (else: install gate above)
1. Gather the order: who's eating + each person's exact items, the scenario, store/address
2. Lock scenario + store -> orderType / beType / storeCode / beCode
3. Pull the menu -> query-meals (buyable products, sticker prices, productCodes)
4. Pull coupons -> query-store-coupons (usable here) + query-my-coupons
5. Pull points -> query-my-account (balance + expiring) + mall-points-products
⮑ 3/4/5 only depend on the locked store — issue them as ONE parallel batch.
mcd-mcp is remote; the number of sequential round-trips, not of calls, costs
wall-clock.
5b. Detail + price-probe BEFORE solving (ONE batch) -> query-meal-detail +
calculate-price on the candidate container combos & key singles; both need only
the query-meals codes, not each other. Use the REAL probed prices, not the
sticker prices query-meals returned — auto-promos can be ~50%+ off and change
WHICH combo wins, not just the total (see Step 3 for the sticker-price trap).
6. Build the solver input JSON (using the 5b real prices) -> run scripts/optimize.py
7. Verify real prices -> calculate-price on the winning basket(s); re-rank by true cash
8. Present the plan (advisor only; offer to place it on explicit request)The exact JSON shapes for steps 6–7 and the field-by-field mapping from each
mcd-mcp tool live in references/modeling.md — read it before building the
input; don't reconstruct the schema from memory.
Step 1 — Gather the order
Get, in the user's own words:
- People and their exact items — e.g. "person A: Big Mac + medium fries; person B: McSpicy + medium Coke". You translate this colloquial wishlist into canonical menu SKUs later (against the live menu) — the user never has to speak in product codes.
- Scenario — one of: dine-in (到店自取, beType 1) / drive-through (得来速, beType 5) / McDelivery (麦乐送, beType 2) / corporate group-meal (团餐, beType 6). Prices and coupons differ by scenario, so this must be fixed up front. Offer these four as an AskUserQuestion menu, not a typed question (see "Asking the user — offer a menu" above).
- Store or delivery address, and any budget / dietary constraints.
Two decisions are baked in (don't re-litigate them unless asked):
- Strict SKU — each person gets exactly their specified item; no cross-person substitution. A combo's free in-combo swap (e.g. default cola → sprite at no charge) counts as legal, because the recipient still ends up with their exact SKU. Only ask the user when their phrasing is ambiguous (just "cola" → which size? zero-sugar?) or when a size/spec conflicts (wants a large coke but the combo only includes a medium) — surface it, don't guess. When you do, present the candidate SKUs (or the size/spec resolutions, each priced) as an AskUserQuestion menu so the user taps the answer instead of typing it.
- Waste allowed + reported — the optimizer may buy a combo and leave a component unused if that's still cheapest; whatever's unused is reported as a leftover list so the user decides.
Step 2 — Lock scenario and store
- dine-in / drive-through →
query-nearby-stores(searchType 2 with city+keyword to search, or searchType 1 for favorites). beType 1 returns stores with nobeCode; beType 5 returns stores withbeCode— keep it. - McDelivery / group-meal →
delivery-query-addresses(ordelivery-create-addressto add one), thendelivery-query-storesto get the deliverable store +beCode.
Whichever path you take, present the returned stores / addresses as an AskUserQuestion menu (favorites & saved addresses first) rather than asking for a typed identifier — see "Asking the user — offer a menu" above for the 4-option cap and the "其他"-to-search fallback.
Record orderType (1 for dine-in/drive-through, 2 for delivery/group-meal),
beType, storeCode, and beCode (when present). Every later call needs them,
and a SKU's productCode can differ across scenarios — so re-pull the menu if
the scenario changes.
Step 3 — Pull the menu and combo composition
query-meals(with the locked orderType/beType/storeCode/beCode) → the list of buyable products with prices andproductCodes. These become the solver'ssingleandcombooptions.- For each combo the group might use,
query-meal-detail→ its default components, one per round (it returns the default fill, not the full swap menu — seereferences/modeling.md§6). This is still what lets the solver redistribute components and model free swaps (interpretation A). Without it a combo is an opaque price and the core insight can't fire. Fetch it in the Step 5b batch (it needs thequery-mealscodes), not in this menu pull.
Normalize both demand-side items and combo components to one SKU namespace
(prefer productCode; fall back to name|size when a combo's component code
doesn't line up with its standalone code) — see references/modeling.md.
query-meals prices are STICKER prices — do not optimize on them. Large auto-
promotions (套餐立减 on 四件套 / 随心选, 限定特惠, McCafé 立减) plus an order-level
member discount are applied only by calculate-price, and routinely knock 30–50%+
off — e.g. a ¥57 四件套 ringing up at ¥28, a ¥21 冰奶铁 at ¥9.9 (both just over 50%).
A discount that
large changes which container is cheapest, not just the final total, so solving
on sticker prices can pick a "cheap on paper" combo a hidden promo has already
beaten. Before you solve, price-probe every plausible container combo and key
single with calculate-price (Step 5b), batched, and feed those real numbers in.
Step 4 — Pull coupons
query-store-coupons(with the locked store + orderType/beType) → coupons actually usable for this order. Use these to create coupon-discounted option variants and the coupon resource pool.query-my-coupons→ what the user already holds.available-coupons/auto-bind-couponscan surface/claim coupons from the 麦麦省 coupon center first, if the user wants to grab everything claimable before optimizing.
Model order-level threshold discounts (满减) / stacking conservatively in the
local solver — the authoritative coupon math comes from calculate-price in
step 7.
Step 5 — Pull points (Plan A)
query-my-account→ points balance and how much is expiring soon.mall-points-products(+mall-product-detailas needed) → point-redeemable items/coupons. Turn the ones relevant to this order into options withcashPrice: 0andpointsCost: <cost>.
Points policy is Plan A: minimize cash, treat expiring points as free to spend, and charge non-expiring points their opportunity cost so they're only spent here when that beats holding them for a better redemption.
Derive that opportunity cost from the mall — don't guess a number. Scan
mall-points-products and, for each redeemable, estimate its cash value per
point = (what you'd otherwise pay for it, in fen) ÷ (its points cost). Gift
cards give an exact rate (face value ÷ points); product coupons use the granted
item's menu price. Take the best rate as r* (fen per point) and pass
pointsPolicy.minCashSavedPer100Points = round(r* × 100). The solver then spends
a non-expiring point on this order only when doing so saves more cash than r* —
i.e., only when this order is a better use of the point than the best redemption.
If the user has no non-expiring points, or you genuinely can't estimate any
redemption value, omit minCashSavedPer100Points entirely — the solver then
spends only the free expiring points and never burns the stash on a guess.
Step 5b — Price-probe the candidates (before solving)
query-meals prices are sticker prices (Step 3). Before you build the solver input,
fire one batch that fetches query-meal-detail for every plausible container
combo and calculate-price for those combos plus the key singles — both depend
only on the query-meals codes, not on each other, so they share one round-trip.
Read each line's product-level subtotal as that option's real unit price and
feed those into the solver (Step 6) instead of the sticker prices. Do not treat
a per-call calculate-price total as a unit price: order-level discounts (会员立减,
满减) ride on the whole basket, so if you price candidates in separate calls each
one carries that order-level cut and summing them double-counts it. Unit price =
line subtotal; order-level effects are confirmed only on the full basket at Step 7.
This is what stops the solver from picking a combo a hidden promo has already beaten, and folds detail-fetch + pricing into a single round-trip.
Step 6 — Run the solver
Build the input JSON exactly as specified in references/modeling.md (all cash
in fen). Set each option's cashPrice to its Step 5b probed price, not the
sticker price from query-meals — otherwise the solver mis-ranks containers a
hidden promo has re-priced. Then:
python3 <SKILL_DIR>/scripts/optimize.py --input /tmp/mcd_problem.json --pretty
# or pipe the JSON in on stdinIt returns plans (top-K cheapest baskets), a priced baseline, and savings
vs the naive baseline and vs all-singles. If meta.uncoverable is non-empty, a
demanded SKU has no buyable option — tell the user (likely a normalization miss
or an item that store doesn't sell). If meta.optimal is false the search hit
its node cap and the result is a good-but-unproven solution; say so.
Step 7 — Verify true prices with calculate-price
Local cash is an estimate; real coupon stacking and threshold discounts (满减) are
only correct from the server. For each of the top-K plans, call calculate-price
with that plan's non-points items (productCodes, quantities, and any
couponId/couponCode) at the locked store/orderType. Re-rank by the returned
real total. Prefer the plan whose verified price is lowest; if verification
reorders them, trust the verified numbers, not the local estimate.
Step 8 — Present the plan
Render the result in the user's conversation language — don't hardcode a language. (Product, combo, and coupon names come straight from the menu data, so they'll appear in Chinese regardless; that's fine.) Lead with the win, then the details, roughly this structure:
[Cheapest plan] (scenario · store)
Lowest: ¥XX.X · saves ¥X.X (YY%) vs "one combo each" · ¥X.X (ZZ%) vs all à la carte
[What to buy]
- <combo> ×1 (drink swapped to cola) ¥30.0
- <burger> ×1 (coupon C1 applied) ¥15.0
Coupons used: C1 ×1 Points used: 0 (800 expiring & unused — see below)
[Who gets what]
- person A: <burger> + <fries> (from <combo>)
- person B: <burger> (à la carte, couponed) + <cola> (combo swap)
[Leftover / waste]: noneConvert all fen → 元 (÷100) for display. Always show the savings vs the naive
plan — that's the user's whole insight, quantified. One guard: if
baseline.covered is false, the baseline you built doesn't actually feed
everyone, so savings.vsBaseline is comparing against an under-spec'd order and
overstates the win — fix the baseline (or omit that comparison) rather than
quoting it. List any leftover items honestly, and note expiring points
that went unused (so they can decide to spend them). If plans has more than one
close option, mention the runner-up so the user can trade (e.g. "¥1 more but no
coupon needed").
End with the go/no-go as an AskUserQuestion in the user's language — options like
"按这个方案下单" / "先不下单", plus one option per close runner-up so they can pick the
plan they prefer right there. Only call create-order if they explicitly choose to
place it — it spends real money. Default is advice only; "先不下单" just ends the turn.
Notes
- Units: the solver speaks fen (integer) end to end, matching
calculate-price; you do the ÷100 for the user. - Scenario changes the data: switching dine-in ↔ delivery changes prices,
coupons, and sometimes
productCode— re-pull menu/coupons and re-run, don't reuse. - Don't invent data: if a tool fails, report it and retry/ask — never fill in plausible prices or coupons. A wrong number defeats the purpose.
- Round-trips dominate wall-clock: mcd-mcp is a remote server, so cost scales
with the number of sequential batches, not the number of calls. Put every call
with no unmet dependency in one parallel batch — all store-scoped reads
(menu/coupons/account/points) together, then
query-meal-detail+calculate-priceprobes for the candidates together (both need only the menu codes, not each other). A clean run is ~4 batches. The classic time sink is optimizing on sticker prices, then re-doing the whole plan aftercalculate-pricereveals a promo — price-probe first (Step 5b) and that vanishes. - Re-runs are cheap: tweaking the group's items, scenario, or the points
threshold just means rebuilding the JSON and re-running
optimize.py. - For the full input/output schema and the mcd-mcp field mapping, read
references/modeling.md.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

