28.07.2026 04.09.2026 offerhopper.ai team 20

Build a Personal AI Grocery Assistant with Claude, ChatGPT or OpenClaw

Build a Personal AI Grocery Assistant with Claude, ChatGPT or OpenClaw
Building a personal AI grocery assistant with offerhopper.ai via MCP — AI-generated (offerhopper.ai)

Someone out there has offerhopper.ai wired into their morning. Every Monday around eight, before anyone has decided to go shopping, a query arrives from an agent running on a schedule, asking whether anything is worth a trip today.


A finished setup looks like this. One Saturday this month the assistant priced a week of dinners for a household near Trier, built the menu around the pork mince and the salmon that were actually discounted that week, and ran the final list for a real route: three stops, 40.67 EUR all in. Asked whether that beat just going to one shop, it forced a single-store price on the identical basket, came back with 53.18 EUR, and reported the trip as worth 12.51 EUR that week. When a coeliac guest turned up midweek, it re-solved the back half of the menu into tacos and a tuna salad on corn tortillas without touching the days that already worked. The rest of this page is how to build that.

I built offerhopper.ai as a web application — read the story of why offerhopper.ai exists to solve supermarket decision fatigue. You open it when you've already decided to shop, type what you need, and it works out where to go and whether the drive pays for itself. That was the first thing to exist, and for a website it holds up fine.

The MCP server was built because AI agents making up numbers about trips and prices is a real problem. A language model asked "is it worth driving to Aldi today?" will happily invent a plausible-sounding answer with no route, no live prices, and no actual arithmetic behind it. offerhopper.ai does the arithmetic. Exposing it over the Model Context Protocol (MCP) means an agent gets a real multi-store cost calculation instead of a guess dressed up as one. And once it's the agent making the call rather than you, the tool stops waiting to be remembered: the agent already knows things about your household that the website never will.

This guide is about turning that into a personal assistant inside Claude, ChatGPT, or self-hosted agents like OpenClaw. It covers the setup, not the wire format: connection details, the full parameter list and the response payload live in the MCP Developer Guide. Every worked example below is real output from one German household, including the runs that went wrong. If you'd rather not do any of it by hand, skip to the agentic setup flow and hand this page to your agent.

One thing to be clear about before you start. offerhopper.ai answers "where should I go to buy this list, and is the trip worth it". It does not answer "what's on offer near me". You bring the list; it does the routing and the arithmetic.


Quickstart

Connect the server, hand your assistant a little context, run one query.

1. Connect the server

Parameter Value
MCP Server URL https://mcp.offerhopper.ai/mcp
Authentication None (public endpoint)
Tool name plan_optimal_shopping_route
Supported assistants Claude (Web, Mobile, Desktop), ChatGPT (Developer Mode), OpenClaw

On Claude web and mobile, open Settings, then Connectors, then Add Custom Connector, and enter the URL with no authentication. Add it at account level rather than inside a project, or scheduled tasks won't see it later. On ChatGPT with Plus, Team or Enterprise, open Settings, then Apps, enable Developer Mode, create an app, and enter the same URL. On OpenClaw, install from the ClawHub registry:

openclaw plugins install clawhub:offerhopper.ai

Screenshots, the Claude Desktop bridge config, and the full walkthrough are in the MCP Developer Setup Guide.

2. Give your assistant the context

Paste your standing context as a skill (see The Skill and Its Setup), or, if you only ever plan to chat, inline the five values that skill lists: your PLZ, household size, transport mode, what an hour of your time is worth, and your two shopping lists written in German.

3. Run one query

Check my fresh basket. Is it worth a trip today?

That's the whole query. Your location, transport, household sizing and the reporting format all come from the context you installed. If you skipped step 2, you'll need to spell those out each time instead. For the household these examples come from, a four-item version of that basket returned one stop at a REWE 643m away, €10.06 all in, 7% travel overhead, verdict worth it.

Before you trust that total, read selected_product on every line. Why that matters, and how to read the rest of the response, is its own section below. Or skip the manual setup entirely and hand this page to your agent.


The Skill and Its Setup

Your assistant is where this context belongs. offerhopper.ai holds nothing about you between calls, so the standing facts live with you and get passed in only when a query needs them. Put them in a skill rather than loose instructions: scheduled runs are separate sessions that can load a skill but may not inherit a project's custom instructions, so a skill is the one home both your chats and your Monday run read from.

If you only ever plan to chat, the block can live in your assistant's custom instructions instead: Project or Global Custom Instructions in Claude, saved memory in ChatGPT, MEMORY.md in OpenClaw. If you intend to schedule anything, use a skill file (grocery-offerhopper/SKILL.md), because that's the one source both a chat session and an unattended run read from. This choice decides whether your automation works later, so make it now.

Note the frontmatter. A scheduled task that can't find its config is almost always a skill with a missing or malformed name / description:

---
name: grocery-offerhopper
description: >
  Standing context for grocery and drugstore planning via offerhopper.ai. Use
  for the weekly shop, the fresh basket, the stock-up check, meal planning, and
  any "is a trip worth it" question in Germany.
---

Rely on plan_optimal_shopping_route for real-time prices and accurate,
shopping-specific routing. Whenever the answer turns on real prices or distances
near me, it takes priority over any figure from memory or the web.

# Household facts  (yours to fill in)
- Location: [PLZ or town]. Used for every query unless I say otherwise.
- Household size: [e.g. 2 adults]. Sizes unstated quantities.
- Transport: [Car, or Bicycle / e-scooter, or Walking].
- Time value: [EUR per hour]. Passed as hour_cost.
- Distance cost: [EUR per km, car only]. Passed as km_cost.
- In-store time: [minutes per stop]. Passed as shopping_time_per_store.
- Dietary: [e.g. milk lactose-free; nothing else].
- Shopping rhythm: [every few days, or one weekly shop].

# Baskets  (yours to fill in, in German)
- Fresh basket: [staples, in German], sized for [N] days.
- Stock-up list: [non-perishables, in German].

# Rules  (the product's opinion, leave these as written)
1. Item names in German. Name the specific intent when I care which product
   arrives (Kopfsalat not Salat, Gouda am Stück 400g not Käse). Leave a word
   broad when I genuinely don't mind (Obst).
2. Size unstated quantities for the household. For units, see Quantities below.
3. Report the verdict, the travel overhead percent, the basket total, and the
   in-store time cost separately from the headline.
4. Name every selected_product that doesn't match what I asked for, and every
   item above its market_average. Relevance is how well the pick fits my intent,
   not a price score. A lower relevance usually means a looser match was taken
   because it was significantly cheaper, so read that item's alternatives array,
   report the exact-match option and the price difference, and let me judge the
   trade. A broad word that resolved to a good-value pick, or a pack size that
   beat the unit I counted in, is not a mismatch. If the pick is genuinely wrong,
   the query was ambiguous and needs a more precise word.
5. Stock-up alerts only when an item is more than 25% off and the verdict is
   worth_it. Otherwise say nothing's worth a trip.
6. If I mention driving somewhere, pass it as end_location so the detour is
   costed, not a round trip.
7. Don't propose extra stops the tool didn't route. Trust its stop count.

# Quantities  (pick the unit that pins the amount)
Where the amount depends on size or weight (meat, fish, cheese, most produce),
always use grams or litres, never a piece count: one piece is an unpredictable
average weight, so 3 pieces can come to roughly 300g when you wanted 600g. Use
pieces only for discrete units you actually count (eggs, a cucumber, a tin, a
named pack). For pack-sold items a bare number can bind to pack count and
oversize the buy (8 wraps read as 8 packs), so give grams or an explicit pack
count there too. If a quantity comes back mis-scaled, re-run that one line in
grams and replace it in the final list, don't just flag it; the list I shop from
has to be right.

# Reporting  (unprompted, in the first summary)
Put two things in the first summary without being asked. First, every
selected_product below relevance 1.0, by name: when the engine took a cheaper
substitute, give the exact-match alternative and the price difference so I can
judge whether the closer fit is worth it; otherwise flag it as worth a check in
store. Second, every line at 25% off or more, named with its price and percent.
If a line is both a deal and a substitution, say both so neither hides the other.

# Workflows  (multi-call procedures, the product's opinion, leave as written)

## Weekly menu from price signals
1. Probe. Two broad queries, batched: proteins, then carbs + vegetables. Pass
   hour_cost=0 and km_cost=0 on the probe so the engine returns the genuine
   cheapest price per item and isn't nudged toward a nearer store by trip
   friction. You're reading the market, not routing yet.
2. Score. For each item compute 1 - (price / market_average). >= 0.25 strong,
   0.10 to 0.25 moderate, < 0.10 weak. Sort descending.
3. Compose. Build the week's dinners on the strong items, use moderate ones as
   support, skip the weak proteins. Keep an exception like salmon only when its
   own discount justifies it.
4. Reconcile quantities before pricing. Tally every ingredient across all the
   dinners and pass the summed amount, not one pack per name (unit preference
   as in rule 2). Say "frisch" when I mean fresh.
5. Validate. Run the reconciled list once for the real route, with my normal
   hour_cost and km_cost from Household facts.
6. Measure the real saving. Re-run the identical list with max_stores=1 and diff
   the totals against the multi-stop route. That difference is the trip's true
   worth, not the tool's own savings figure.

Rate limit: 3 calls per 30s window; a 4th trips a 60s block. The probe (2) plus
validate (1) spends the budget, so let the window clear before any fix-up re-run.

## Edit a live plan  (guest, diet change, half the fridge already bought)
Keep the days that already worked and re-solve only the changed ones. Prefer a
cuisine that satisfies the constraint instead of fighting it (Mexican for
gluten-free leans on corn and rice). Re-price only the affected items.

## Swap one item on a live route
Sometimes the user wants one line changed on a route you already priced. A rejected
pick, a brand or diet or pack preference, or "I'd rather have X" where X is already
listed as an alternative. Call `swap_route_item` for that. Don't re-plan the whole
list.

Don't swap just to grab a cheaper or higher-scoring option. The engine already
weighed price against relevance and travel. Explain the current pick first. Swap only
when the user actually wants something else.

Pass the route's `share_url` (or its 8-char id) as `share`. Then two ids, copied
straight from the last reply: `offer_id` is the line on the route now, `alt_id` is the
one to swap in. Take `alt_id` from that item's `alternatives` (same store) or its
`cross_store_alternatives` (a store already on the route). Never build or guess an id.

A swap can change the quantity, not just the price. buy_count is recalculated to still
cover the amount asked for. In one run a whole roasting chicken swapped for thighs came
back as 2 packs instead of 1, at a higher line total. Report that, not just the new
unit price.

A swap moves an item between stops that already exist. It can't add a stop. For a
better deal at a store not on the route, run a fresh plan. A cross-store swap can empty
a stop and drop it, which changes the trip maths. So read trip_verdict again on the
returned route. Don't assume it held. Report the swap like any result: the new
selected_product, its relevance and discount, and the new basket total. It counts as
one call against the 3-per-30s limit.

## Single item, is a special trip worth it
Ask for the one product. Read candidate_store_routes and the
single_item_comparison hint. The cheapest shelf often isn't the recommended stop
once the drive is costed; report both and the gap.

Why the numbers matter

Household size changes what a bare word means. Joghurt with no quantity defaults to a single 150g pot; for a three-person week it's a 1kg tub, which lets the engine compare bulk against small packs properly (which pack is actually cheaper goes into that).

Time value works in both directions, and the default is €12.00/hour. Above it, marginal stops die: at €25/hour a 15-minute detour costs €6.25, so saving €1.00 on coffee is a clear loss and the tool will tell you so. Below it they come alive: at €6.00/hour that same detour costs €1.50, and one household at that rate had two-store routes beat their forced single-store equivalents by €1.25 and €4.28 on two different menus. Split-store shopping being rarely worth it follows from the default hourly rate rather than from any law of German retail. Set yours honestly and the advice changes with it.

Fill in the facts and baskets now; leave the basket wording rough. The next section is how to write list items the matcher resolves the way you meant, and you'll come back and tighten this skill once you've seen your own results.


Getting Good Results

Two skills matter: writing list items the matcher resolves the way you meant, and reading the response at the level where you make a decision. (The field-by-field response reference is in the developer guide; here we stay on what to do with it.)

Writing lists that match

offerhopper.ai does not do literal string matching against product names. It resolves your words to the most plausible shopping intent, then finds products for that intent. Most of the time this is what you want: "Hackfleisch" finds mince without you specifying a brand, pack size or fat content. The failure mode is subtler than vague words failing. A word can carry more than one reasonable intent, and the matcher picks one.

Two real resolutions from a run using a generic staples list:

Asked for Matched to
Salat Cherry Romatomaten 250g
Käse Exquisa Frischkäse Fitline Kräuter 200g

Neither is absurd. You can make a salad out of cherry tomatoes, and Frischkäse is cheese. They're defensible readings of an ambiguous word, they just weren't what the shopper meant, and the basket total was calculated against them. The fix is to name the intent rather than the category:

Instead of Write
Salat Kopfsalat, or Salatmischung, or Cherry Tomaten
Käse Gouda am Stück 400g, or Emmentaler Scheiben 200g

Not every broad word needs narrowing. Obst returns a sensible fruit picked on value, which is right if what you meant was "some fruit, whatever's good this week". You delegated the choice and the engine made it on price. Leave a word broad when you genuinely don't mind what arrives, and narrow it when you do.

Quantities are compared by value, not by your unit. A run asking for 10 Rollen Toilettenpapier came back with an eight-roll pack. That looks like a miss and isn't one. Rolls are a poor unit for toilet paper because sheet counts vary (we break down this math in Which Pack Is Actually Cheaper?), so eight rolls at 220 sheets each can beat ten shorter ones on both quantity and price. A 1kg tub of yoghurt wins over six 150g pots the same way. Correcting that would cost you money.

German product nouns are the most reliable input, because the search runs against German retail data. English works, and a fair amount of natural language and unit normalization happens on our side to make sure it does, but not in every case. One that fell through: a menu-planning run asked for 1 bag mixed salad, found no catalogue match, and fell back to a synthetic assumed price marked is_synthetic: true. Re-running with 1 bag Salatmischung matched a real product at €1.98. A synthetic line is the strongest signal you have that your wording needs work. Talk to your agent in whatever language you like, and have it write the item list in German where it can.

Precision is rewarded when you actually care about the product. Swirl Müllbeutel 35L matched exactly: Swirl Reißfest und Dicht Tragegriff 35l, 15 Stück. Brand plus volume plus intent leaves nothing to resolve. And bad resolutions get corrected over time, so if a result looks wrong it's worth re-running before assuming the product doesn't exist.

Reading the response

Three independent questions come back, and collapsing them is the most common misreading: whether the trip is worth making, whether it's a good deal, and how well the match fits.

The verdict is not one yes/no. The engine returns one of four headline states, and each points at a different fix:

Headline What it means What to do
worth_it Travel and time overhead are both fine. Proceed.
not_worth_travel The drive is disproportionate to the basket. Suggest a nearer store, a lighter travel mode, or going only if already en route.
not_worth_time The store is near, but the basket's too small for a full stop. Travel isn't the problem. Fold it into a bigger shop, or add items.
not_worth_both Both axes are bad. Skip it, or bundle into a larger trip.

The other two questions sit alongside it: whether it's a good deal (the savings, independent of the trip verdict) and how well the match fits. That last one is the confidence, or relevance, score: the engine's trust that it found the right product for your intent, not a judgment about price. It matches your intent as closely as it can, but it also makes a call: when a looser match is significantly cheaper, it takes it. So a high score means the pick fits what you asked; a lower score usually means it accepted a cheaper substitute that fits less exactly. That's a trade to check, not an error to undo. Open the item's alternatives, where the exact match sits with its price, and decide whether the closer fit is worth the difference. Only when the pick is genuinely wrong for you is it a wording problem: an ambiguous word like Salat gets a confident match to cherry tomatoes, and the fix is a more precise term, then a re-run.

Two things worth knowing at the decision level. The headline price leaves out in-store time, so when you're comparing multi-stop options ask the agent to add it back before ranking them. And coverage is asymmetric: special offers across all the major German grocers and drugstores, but live everyday shelf prices for only Aldi, REWE, dm, Rossmann and Müller. That shapes routing more than any single price, because a store where we only see the promotions has everything else estimated, and a pile of estimates rarely wins a whole-basket comparison even when it holds the single cheapest item on your list. The tool is choosing one destination for your whole list, not a winner per item.

Once you've learned how your lists resolve, come back and update the skill's basket section with the wording that worked.


Advanced Use Cases

The skill above carries the procedures. This section is what they look like on a real week, including the parts that went wrong, so you recognise them when they happen to you.

Price-signal planning, a worked run

Most meal planning runs backwards: decide what to cook, then find out whether the ingredients are affordable. The skill's price-signal workflow flips that, and here's a real week near Trier. Because the probe runs with trip friction switched off, the two broad queries show the genuine cheapest price per item rather than one nudged toward a nearer store. The signal came back clear: pork mince and pork hip steaks well under their local averages, salmon 25% off at Netto, tofu the cheapest protein on the board at 0.95 EUR, and rice, pasta, potatoes and onions all discounted. Salmon aside, the cheap proteins were pork and tofu, so the week leaned on those.

Then the part the first draft always gets wrong: quantities. A menu is not a flat shopping list. Ingredients recur across dinners, so one pack per name systematically underbuys. Broccoli, potatoes and pasta each showed up in two dinners and peppers in three; a naive list ordered one of each and would have come up half short. Salmon was the sharper version of the same failure: asked for "salmon x4 (2 packs)", the matcher chased a gram target and bought nine packs, 900g for 13.41 EUR against a 450g need. The fix is to tally each ingredient across every dinner and pass the summed weight, and to specify fish by weight ("450g Lachsfilet") rather than by pack. Say "frischer Brokkoli" too, or the engine will quietly take a cheaper frozen pack. Counts have the mirror trap: "8 Tortilla Wraps" can bind the 8 to pack count and buy eight packs, forty-odd wraps. Give grams or an explicit "1 Packung" for pack-sold items, and when a line still comes back oversized, re-run that one line in grams so the list you actually shop from is corrected, not merely flagged.

Validated, the reconciled list came back at three stops, 40.67 EUR all in. To get the saving that actually matters, the agent forced the identical basket through a single store and diffed the totals: 53.18 EUR at one shop against 40.67 across three, so the trip was worth 12.51 EUR that week. That single-store price is what max_stores=1 is for as a measuring tool, and it's the only honest reason to touch that parameter.

Editing a plan when life changes

The plans that survive contact with real life are the ones you edit when something changes, not the ones built from scratch. A guest arrives, a craving lands, half the fridge is already bought. The assistant handles the edit without throwing away the price work underneath it.

A worked case: a coeliac guest from Thursday on. The instruction was to keep the three nights that already worked, make everything from Thursday gluten-free, and swap two nights for something worth cooking for a visitor. The move that does the most work is picking a cuisine that satisfies the constraint instead of fighting it. Swapping two nights to Mexican solved most of the gluten problem for free, because the food leans on corn and rice rather than wheat: Thursday's stuffed peppers went Mexican-spiced over rice, Friday's wheat-pasta tuna salad became a lime tuna salad on corn tortillas, and the wheat pasta left the list entirely. Re-running the adjusted week turned up the usual texture of a real run. The corn flour had no catalogue match and came back synthetic, and "Mais" resolved to corn on the cob rather than the tinned kernels the salad wanted. Both are wording fixes, and neither is visible if you only read the headline.

Single items, personas, aisle order

Not every question is a basket. "Is the butter actually cheaper at the Netto once I count the drive?" is a single-item question, and the response is built for it: ask for one product and the engine ranks every store in range by effective total and names the recommended stop, which often isn't the cheapest shelf once the drive is costed. Reach for it when you're deciding whether one discount is worth a special trip.

Two smaller levers come for free. The engine ships three effort-vs-price presets (saver, smart, fast) and resolves to smart unless told otherwise, so you rarely set one by hand; it's worth asking your agent to read resource://config/defaults for the live numbers rather than trusting a figure from a blog post. And every item comes back tagged with a section of the shop, so an agent can sort your printed list into aisle order and you walk the store once instead of doubling back.


Automating Weekly Checks with Scheduled Tasks

German promotion cycles run weekly, starting Monday or Thursday depending on the chain. A daily check reruns identical flyer data and burns tokens for nothing. Monday at 08:00 is the right slot.

Where scheduling lives

Platform Where Execution
Claude Cowork, on the Scheduled page. Paid plans only, and Cowork is in beta rolling out by plan tier. Remote. Runs while your machine is asleep, unless the task is pointed at a local folder.
OpenClaw Natural language, in any session. Runs through your gateway.
ChatGPT No native background execution for custom MCP connectors. Manual trigger only.

Scheduling is not something you request inside a normal Claude chat. It's a Cowork feature with its own page, so asking a regular chat session to schedule a recurring task will not create one.

Setting up in Claude

Click Scheduled in the left sidebar, then New task. "Create with Claude" opens a session that interviews you and proposes the task; "Set up manually" gives you the fields directly: name, prompt, approval mode, frequency, model, and optionally a working folder.

Two settings decide whether it works unattended. Leave the folder field empty, because pointing a task at a local folder forces local-only execution and it will then only fire when your machine is awake. And check the approval mode, because a task that pauses for permission hasn't run, it has stalled until you notice. Fire one run on demand before trusting the schedule.

Setting up in OpenClaw

No JSON, no crontab:

Hi, can you do a stockup check weekly for me every Monday morning 8:00am?

The agent confirms the schedule and timezone back to you.

Giving the task a memory

Each scheduled run is a fresh session with no memory of last week, so left alone it'll report the same offer three Mondays running. If that bothers you, give it a small state file saved to your account: a short table of the lowest price you've seen per item and when you last bought it, read at the start of the run and appended at the end. That turns "more than 25% off" into "at or below the lowest we've recorded", which is the question you actually had. The full pattern, with a starting file and task prompt, is in the scheduled-task pattern below.

The full state-file pattern

Fix the memory problem with three layers rather than a longer prompt.

The durable config goes in a skill. Location, household size, hourly rate, transport rules and the lists themselves don't change week to week, and scheduled tasks can load skills.

The run state goes in a file saved to your Claude account, not a folder on your computer. The task reads it at the start and appends at the end.

The task prompt holds only the trigger and the reporting rules, short enough to read in one screen.

The obvious use of state is deduplication. The more valuable one is calibration. discount_pct is calculated against that store's own regular_price, so a 51% discount is measured from a baseline the retailer sets. After eight weeks of recorded prices, your threshold stops being "more than 25% off" and becomes "at or below the lowest we've seen", which is the question you actually had. The third use is restock timing: fifteen bin bags last about six weeks, and an alert twelve days after you bought them is noise.

A starting state file. Keep it small, since it's read into context every run:

# Stock-up state

## Observed price floors
| Item | Lowest seen | Store | Date | Shelf price |
|---|---|---|---|---|
| Dr. Oetker Tradizionale Salame 385g | 1.79 | REWE | 2026-07-28 | 3.69 |
| Swirl Müllbeutel 35L, 15 Stück | 2.59 | REWE | 2026-07-28 | 2.59 |

## Purchases
| Date | Item | Quantity | Paid | Reorder after |
|---|---|---|---|---|
| 2026-07-28 | Swirl Müllbeutel 35L | 15 bags | 2.59 | 2026-09-08 |

## Alerts sent
| Date | Item | Price | Bought? |
|---|---|---|---|
| 2026-07-28 | Dr. Oetker Tradizionale | 1.79 | yes |

And the task prompt:

Run my weekly stock-up check.

1. Read stock-up-state.md.
2. Run the stock-up list through offerhopper.ai using my standing config.
3. Compare each returned price against the lowest price recorded in the
   state file, not against the shelf price the store reports.
4. Message me only if an item is at or below its recorded floor AND the
   verdict is worth_it AND the item is past its reorder date. Otherwise
   reply "Nothing worth a trip this week."
5. Append this run's prices to the observed floors table. Log any alert
   sent. Leave the Bought? column for me to fill in.

Your first Monday will be chatty, because an empty state file makes every price look like a floor. It quietens down as the log fills in.


Hand the Whole Setup to Your Agent

Everything above can be done by hand. It doesn't have to be. If your assistant can read a URL and call the connector, hand it this page and let it run the setup, interviewing you for whatever it needs. That produces a config built from your actual results rather than from the examples.

Connect the MCP server first, then paste this:

Read https://offerhopper.ai/blog/en/ai-grocery-shopping-assistant and set up my
grocery automation end to end.

Work in this order and don't skip ahead:

1. Confirm you can see the offerhopper.ai tool. If you can't, stop and
   tell me how to connect it before doing anything else.

2. Interview me for everything the guide's context block needs and I
   haven't already told you: my PLZ or town, household size, transport,
   hourly time value, dietary constraints, how often I shop, whether
   drugstore items are in scope, and my two lists. Ask in small batches.
   If a value I give differs from the guide's default, tell me what it
   changes about the advice I'll get. Mention the extras you can offer:
   sorting the final list into aisle order, and a saver/smart/fast
   persona if I want to bias the price-versus-effort trade-off, though
   smart is a sensible default.

3. Draft my fresh basket and stock-up list in German, naming specific
   product intent rather than categories, as the guide describes. Show
   me both lists and let me correct them before you run anything.

4. Run the fresh basket once. Report the verdict, the travel overhead,
   the headline cost, and the in-store time cost as a separate figure.

5. Audit that run the way the guide's "Getting Good Results" section
   describes: for every item, compare selected_product against what I
   asked for, and flag intent mismatches, synthetic prices, and anything
   priced above market_average. Don't flag a broad word that resolved to
   a good-value pick, or a pack size that beat the unit I counted in;
   both are correct. Propose better wording for whatever is genuinely
   wrong and re-run to confirm. Don't skip this because the confidence
   score looks good.

6. Write my final context block using the values I confirmed. Tell me
   where to put it: custom instructions if I'm only going to chat, a
   skill if I'm going to schedule.

7. Tell me exactly how to create the weekly stock-up task on my
   platform, and give me the task prompt and the starting state file to
   go with it. Do not claim to have scheduled anything you cannot
   actually schedule from where you are.

Ask me when you're unsure rather than assuming. I'd rather answer three
questions than fix a wrong basket.

Step 2 should feel like a short interview, not a form. Step 5 is the one that gets skipped and the one that pays: a first-draft basket almost always contains one item the matcher read differently than you did, so if your agent reports a clean run with nothing flagged, ask it to list every selected_product explicitly before you believe it. And step 7 is where agents overreach: an agent that says it scheduled something when it only wrote you a prompt has left you with an automation that will never fire.


Frequently Asked Questions

How do I connect offerhopper.ai to my AI assistant?

Server URL https://mcp.offerhopper.ai/mcp, no authentication, on Claude or ChatGPT. On OpenClaw, openclaw plugins install clawhub:offerhopper.ai. Full walkthrough in the MCP Developer Setup Guide.

Does offerhopper.ai store my location or shopping habits?

No. There are no accounts, no stored location data, and searches are discarded after execution. Your context lives inside your own assistant, under your control.

Can it handle specific brands?

Yes, and precision helps. Swirl Müllbeutel 35L matched exactly. The more of the intent you specify, the less the matcher has to guess.

What if I shop every few days rather than weekly?

Split it. Keep the fresh basket manual and sized for two or three days, and schedule only the stock-up check weekly. Promotion cycles are weekly regardless of how often you shop, so there's nothing to gain from checking offers more often than that.

Does it work outside Germany?

No. German supermarkets, discounters and drugstores only. A German 5-digit PLZ or city name is required.

Can I use Google Gemini?

Gemini currently restricts custom MCP integration to Vertex AI and Spark accounts in select regions, mostly US paid developer tiers. For consumer use in Europe, Claude, ChatGPT and OpenClaw are the workable options.

offerhopper.ai

About offerhopper.ai

The AI-driven shopping route planner for expats and locals in Germany.