$ cd ../

Automating Our Family Meals with an Albert Heijn MCP Server

How I connected Claude and an MCP server to Albert Heijn's API to turn weekly Bonus discounts into a realistic, kid-friendly meal plan — and push the grocery basket straight into the app.

The Monday morning friction

Every Monday morning starts with the same mental hurdle in our house: what are we eating this week, what’s on sale at Albert Heijn, and who is going to spend twenty minutes tapping twenty ingredients into the shopping app?

With two full-time schedules and two toddlers (ages 3 and 5), meal planning quickly becomes an annoying multi-variable optimization problem:

  • Time-capped: Weeknight dinners must be ready in 30 minutes or less.
  • Nutritious: Real protein (quality meat, poultry, fresh fish) and plenty of vegetables.
  • Flavorful: We love Belgian comfort food and punchy Asian or Latin profiles — nobody in this house wants bland, unseasoned boiled chicken and broccoli.
  • Toddler-compatible: The kids need to actually eat it. Cooking two separate meals every night is exhausting, but eating totally bland food as adults is depressing.
  • Budget-conscious: We want to take advantage of active Albert Heijn Bonus discounts instead of buying random full-price items.

Instead of sitting on the couch Sunday evening flipping through store flyers and manually searching for each item in the Albert Heijn app, I automated the whole loop using the Model Context Protocol (MCP) and a local agent skill.

The backbone is an MCP server written in Go. A big shout-out to Serzhan for building the original mrserzhan/ah-mcp. On our fork, we added support for Belgian Albert Heijn accounts, fixed token refreshes, and layered on some necessary security hardening (loopback-by-default binds, constant-time token auth, Origin header validation against DNS rebinding, and systemd sandboxing).

Here is the setup, what broke along the way, and how it works now.


The architecture

The Model Context Protocol (MCP) gives the agent actual tools instead of relying on copy-paste. Instead of asking a model for recipe ideas and then manually transcribing them into the grocery app, the agent talks directly to the local ah-mcp service.

                  Agent (AI Skill)
                   ├── ah_get_bonus_offers ──► ah-mcp (Go server)
                   ├── TheMealDB API       ──► marinade & sauce ideas
                   └── local markdown      ──► preferences & history


                   Plain text proposal in chat

                               ▼ (manual "looks good")
                   ah_add_to_shopping_list ──► AH basket / app

The stack has four pieces:

  1. ah-mcp (Go server): Wraps the Albert Heijn mobile backend. Running on our hardened fork (upstream mrserzhan/ah-mcp), it exposes tools like ah_get_bonus_offers, ah_get_shopping_list, ah_search_products, and ah_add_to_shopping_list.
  2. Local skill instructions: Enforces family dietary constraints, preparation time limits, and pantry rules.
  3. TheMealDB API: An on-demand culinary reference used to fetch sauce ratios, marinades, and preparation techniques when the supermarket deal is just raw protein.
  4. Local markdown memory: Stores persistent user preferences (user-preferences.md) and a running log of past meals (menu-history.md) so the agent doesn’t repeat dishes or forget dietary rules between sessions.

Scouting deals without hallucinations

If you ask a generic LLM what’s on sale at Albert Heijn, it will cheerfully hallucinate products that don’t exist, claim items are on sale that haven’t been discounted in six months, or suggest ingredients the store doesn’t stock.

By giving the agent direct tool access to the store’s promotional feed, it queries live data:

// Tool call to ah-mcp
ah_get_bonus_offers({
  "limit": "50"
})

The server returns exact promo mechanics and item IDs directly from the AH API:

[
  {
    "id": 566443,
    "title": "AH Varkenschipolata 5 stuks",
    "original_price": 4.78,
    "bonus_price": 2.39,
    "bonus_mechanism": "1 + 1 gratis"
  },
  {
    "bonus_segment_id": "811127",
    "title": "Kipfiletlapjes",
    "bonus_mechanism": "2e HALVE PRIJS"
  },
  {
    "id": 4183,
    "title": "AH Bloemkool",
    "bonus_price": 0.99
  }
]

Because the agent reads the live backend, every ingredient in the plan corresponds to an actual in-stock product with a known price.


The flavor problem (and TheMealDB)

Supermarket promotions are repetitive: chicken breast, minced meat, cauliflower, carrots, cod. If you leave meal generation to default LLM weights, you get college-dorm classics: pan-fried chicken breast with boiled carrots and rice.

To elevate basic ingredients without overcomplicating weeknight prep, I plugged in TheMealDB open API as a reference.

When the agent spots discounted chicken or fish, it pulls tested marinades and sauce reductions:

  • Querying citrus-soy marinades and high-heat wok techniques for chicken cutlets.
  • Looking up classic butter-lemon and herb reductions (beurre citronné) for fresh cod or haddock.

The agent incorporates those techniques into the recipe steps, turning a 20-minute weeknight dinner into something that tastes intentional.


Family constraints & toddler physics

This is where standard recipe generators fall flat: the reality of feeding young children.

Toddlers won’t eat spicy food, bitter greens, or complicated mixed textures. But cooking two separate meals every evening is exhausting, and cooking unseasoned mush for adults is depressing.

The trick is modular seasoning: cooking a single base meal that toddlers love, while designing an adult accent that gets added separately at the table.

Our weekly formula consists of:

  1. Dinner 1 (Quick, under 30 min, Meat/Poultry): e.g. Asian chicken strips with sweet wok corn. Sweet corn is naturally crunchy and sweet (toddler favorite), cooked with mild ginger and soy. Fresh chili flakes and sriracha stay on the table in a ramekin for the parents.
  2. Dinner 2 (Quick, under 30 min, Fish/Seafood): e.g. Fresh Atlantic cod over Flemish carrot-potato stoemp (mash). The sweet, buttery carrot mash and tender cod are easy for 3- and 5-year-olds. For the adults, a quick 2-minute raw chimichurri (flat parsley, garlic, olive oil, lemon, chili) is spooned over our plates.
  3. Dinner 3 (Extensive, 45–60 min, Meat or Fish): A relaxed weekend roast or oven stew. For example, braised chipolata sausages with roasted cauliflower and baby potatoes. The kids dip sausages and crispy potatoes; parents spoon a rich Dijon mustard and thyme pan-reduction over theirs.
  4. 1 Healthy Lunch (under 15 min): Nutrient-dense mini whole-wheat wraps with smoked chicken, avocado, and radish crunch.

One pan, one prep session, zero whining, and both parents get a proper meal.


The pantry trap: never assume fresh herbs

Another common trap with automated meal planners is assuming your home kitchen is staffed like a restaurant walk-in.

Early on, the agent would casually instruct: “finish with 2 tablespoons of freshly chopped flat-leaf parsley and fresh tarragon”. At 6:00 PM on a Tuesday, nobody wants to discover they don’t have fresh tarragon.

I split the pantry into two strict categories:

CategoryAgent assumptionSystem behavior
Fresh herbs & aromatics (flat parsley, coriander, rosemary, thyme, fresh ginger, garlic, limes, chili)NEVER assume in stockAlways add to the suggested shopping list and calculate into the basket total.
Dry staples & condiments (olive oil, butter, salt, black pepper, nutmeg, soy sauce, mustard, vinegar)Assumed presentSkipped from the grocery list unless explicitly missing.

If a recipe calls for fresh rosemary or flat-leaf parsley for a sauce, it lands on the shopping list automatically. No mid-cooking surprises.


Human-in-the-loop: why modals failed

When automating shopping carts with an LLM, there is a temptation to add interactive approval dialogs.

In my first prototype, an interactive question prompt popped up before the agent had finished rendering the proposed menu. The popup completely obscured the recipes, asking: “Do you want to add these 14 items to your cart?” How can you approve a cart before you’ve even read what you’re cooking?

I scrapped the modal flow completely in favor of Render First, Confirm Manually:

  1. Full preview in chat: The agent renders the complete plan first: the weekly schedule table, step-by-step recipe cards, toddler serving notes, and the itemized grocery list with prices.
  2. No blocking UI dialogs: The message simply ends with: “Does this menu look good to you?”
  3. Natural review: I can read through the dinners, swap an ingredient if needed, or simply reply “Looks great”.
  4. Only upon receiving that manual confirmation does the agent call ah_add_to_shopping_list.

Real prices, real savings

Because the agent looks up actual product SKUs via the AH API, it calculates real basket costs rather than rough estimates:

AH Kabeljauwfilet (2x ca. 250g)     € 15,50
Plukon Kipfiletlapjes (600g)        €  7,79  [BONUS 2e halve prijs]
AH Varkenschipolata 5st (2x)        €  2,39  [BONUS 1+1 gratis]
AH Scharrel kipfiletreepjes         €  3,49
AH Aardappelen kruimig (800g)       €  1,99
AH Winterpeen (1 kg)                €  0,99  [BONUS]
AH Mini krieltjes (600g)            €  0,99  [BONUS]
AH Bloemkool (1 st)                 €  0,99  [BONUS]
AH Verse maïskolven (2x)            €  1,98  [BONUS]
AH Radijs 200g (2x)                 €  0,99  [BONUS 2 voor €0.99]
AH Verse rozemarijn                 €  0,99  [BONUS]
AH Platte peterselie                €  0,99  [BONUS]
AH Avocado eetrijp (2 stuks)        €  3,99
Lassie Toverrijst (400g)            €  0,99  [BONUS]
AH Tortilla volkoren wraps mini     €  1,49
-------------------------------------------
Total Estimated Basket:             € 45,55
Active Weekly Bonus Savings:     ca. € 14,40

Four complete dinners and lunches for a family of four for under € 46, while capturing over € 14 in active promotions.


What lands on the fridge

Once confirmed in chat, two things happen automatically:

  1. One-click cart sync: Every item is injected straight into our Albert Heijn basket via ah_add_to_shopping_list. When we walk into the store (or order home delivery), everything is already organized by aisle.
  2. Print-ready menu sheet: A clean, 2-page HTML template is rendered and converted into a PDF with cooking instructions, exact timings, and toddler separation notes:

Albert Heijn Automated Family Menu

We print it out, stick it on the fridge with a magnet, and that’s the week sorted. Whichever parent gets home first can start cooking immediately without checking recipes on a phone or wondering what ingredients were meant for which meal.

Building on top of MCP bridges the gap between generic chat models and actual daily utility. Instead of brainstorming recipes in a vacuum, the agent knows our kids’ ages, respects our pantry rules, checks live supermarket discounts, and handles the boring data entry in the shopping app for us.

$ next
Tunnelling a Portainer Edge agent through AWS PrivateLink (and a few traps I hit on the way)
./next-post