Swissgroceries MCP
Swissgroceries MCP is a real-time Swiss grocery price-comparison and shopping-planning server that lets an LLM search products, compare prices, and plan trips across Migros, Coop, Aldi, Denner, Lidl, Farmy, Volgshop, Otto's, and Coop City.
Search products cross-chain (
search_products): keyword search across all configured Swiss chains with filters for price (maxPrice), size in ml (sizeRange), and tags (organic, vegan, budget, gluten-free, regional, swiss-made, etc.); supports pagination vialimit/offsetandstoreIds; returns results grouped by chain plus asourcesfreshness map and anerrorslist so you can distinguish "no results" from "chain failed".Find stores near you (
find_stores): locate branches by Swiss ZIP, GPS coordinates, or free-text address, with optional chain filter and a 1–50 km radius.Get full product detail (
get_product): drill into a specific chain + product ID for price, brand, size, unit price, tags, category, image, active promotions, and freshness flags.Browse promotions (
get_promotions): list current deals across chains, filtered by keyword, chain, store ID, or expiring within 1–60 days.Check per-store stock (
find_stock): see which branches of a chain carry a product, optionally near a location or for a single store ID (unsupported chains return a clear error).Plan a shopping trip (
plan_shopping): given a list of items (generic queries or pinned SKUs with quantities, filters, and preferred chains), produce a multi-store plan usingsingle_store,split_cart(with a configurable CHF per-stop penalty), orabsolute_cheapeststrategy.Diagnose connectivity (
health_check): probe each chain adapter for status, latency, and capabilities, useful when a chain is missing from results.Prompt templates:
weekly_deals_digest,compare_basket_across_chains, andcheapest_recipe_ingredients(including from a recipe URL) as one-click starting points in MCP-aware clients.Resource:
swissgroceries://chainsexposes the registered adapters and their capability flags (productSearch,productDetail,storeSearch,promotions,perStoreStock,perStorePricing).No accounts needed: works over stdio with any MCP client (Claude Desktop/Code, Cursor, Cline, Continue, VS Code) via
npx -y @nicktcode/swissgroceries-mcp, with built-in caching, retries, rate limiting, and circuit breakers.
Real-time Swiss grocery shopping over the Model Context Protocol. Search products, compare prices across Migros, Coop, Aldi, Denner, Lidl, Farmy, Volgshop, Otto's, and Coop City, see weekly promotions, and plan multi-store shopping trips. Works with any MCP-compatible client (Claude Desktop, Claude Code, Cursor, Cline, Continue, VS Code MCP extensions, custom clients).
Disclaimer
This is a personal fun project. It is not affiliated with, endorsed by, or sponsored by Migros, Coop, Aldi, Denner, Lidl, Farmy, Volg, Otto's, or any other retailer. It uses publicly accessible mobile-app endpoints to make Swiss grocery shopping a bit smarter for end users.
If you represent any of these stores and have concerns (about API usage, branding, scraping rate, or anything else), please reach out to the maintainer through GitHub and we will work it out. No need to escalate.
API stability: the chain APIs used here are unofficial and can change at any time. The maintainer is not responsible for failures caused by upstream changes; please open an issue with the response sample so the affected adapter can be updated.
PRs welcome. New chains, better matchers, smarter strategies, bug fixes, doc improvements; all encouraged. See CONTRIBUTING.md.
Install
No accounts, no tokens, no API keys required. The Denner adapter self-registers an anonymous client on first use; everything else uses public endpoints.
Claude Desktop (one-click)
Download swissgroceries-mcp.mcpb from the Releases page and:
macOS: double-click or drag onto the Claude Desktop app icon.
Windows: Settings → Extensions → Advanced → Install Extension → select the file.
Claude Code (one-liner)
claude mcp add swissgroceries -- npx -y @nicktcode/swissgroceries-mcpCursor / Cline / Continue / VS Code / Claude Desktop (manual config)
Most MCP-compatible clients accept the same JSON server entry. Add it to your client's MCP config file (paths vary, see your client's docs):
{
"mcpServers": {
"swissgroceries": {
"command": "npx",
"args": ["-y", "@nicktcode/swissgroceries-mcp"]
}
}
}Common config paths:
Claude Desktop:
~/Library/Application Support/Claude/claude_desktop_config.json(macOS),%APPDATA%\Claude\claude_desktop_config.json(Windows).Cursor:
.cursor/mcp.jsonin the project, or~/.cursor/mcp.jsonglobally.Cline / Continue / VS Code: see each client's MCP documentation.
Custom clients: any stdio-based MCP client can spawn
npx -y @nicktcode/swissgroceries-mcpdirectly.
Related MCP server: trundler
What you can ask
Price comparison
"Where is milk cheapest near 8001 Zürich right now?"
"Compare pasta prices across Migros and Coop."
"Show me organic milk options under CHF 3."
Shopping planning
"I need milk, bread, eggs, chicken, and pasta near 8050. Where should I shop to keep costs down?"
"Plan my weekly shop for 5 items near 4052 Basel, one stop only."
"Split my cart across stores for the absolute lowest total, but add a 2 CHF penalty per extra trip."
Promotions and deals
"What is on sale at Aldi this week?"
"Any Migros deals on cheese ending this week?"
"Show me all promotions across chains for pasta."
Stores and stock
"Find Coop stores within 3 km of Bern Hauptbahnhof."
"Which Migros near me has product 4389992 in stock?"
"List Denner branches near 8050."
Tools
Tool | What it does |
| Find grocery stores near a location, filtered by chain and radius. |
| Cross-chain product search with normalised price, unit price, size, and tags. |
| Full product details for a chain plus product ID pair. |
| Current promotional deals, filterable by chain, keyword, store, or expiry. |
| Stores of a chain that have a given product in stock. |
| Plan a multi-store trip for a shopping list near a location. |
| Probe each registered chain adapter and report status, latency, and capabilities. |
Each tool exposes rich JSON Schema with field-level descriptions, so the LLM knows when and how to call it.
Prompts
Prompt | What it does |
| Summarise this week's best grocery deals across the configured chains. Optional category, chains, and location filters. |
| Compare a shopping basket across all chains and recommend single-store vs split-cart strategies. |
| From an ingredient list (or recipe URL) find the cheapest place to buy each item and produce a consolidated shopping plan. |
Prompts are surfaced as one-click templates in MCP-aware clients (Claude Desktop, Cursor, Cline). They produce structured user messages that drive the right tool calls — useful as a starting point and as discovery of what the server can do.
Resources
URI | Content |
| JSON list of registered chain adapters and their capabilities ( |
Chain coverage
Chain | Product search | Promotions | Per-store stock | Nutrition | Auth |
Migros | Full catalog | Yes | Yes | Yes (product detail) | Guest token (auto, rotated on expiry) |
Coop | coop.ch catalog (coopathome) | Yes | Yes (geo) | Yes (product detail) | None |
Aldi | Full catalog | Yes | No | No (API exposes only allergen claims) | None |
Denner | Full catalog | Yes | No | No | Anonymous self-auth (signup + signin, rotated) |
Lidl | Online assortment via the lidl.ch storefront (current + upcoming weekly offers, ~600 items; food only, de/fr/it) | Yes (weekly leaflet) | No | No | None |
Farmy | Full catalog (organic delivery) | Yes (strikeout-price filter) | No (delivery-only) | No | None |
Volgshop | Full catalog | Yes ( | No (delivery-only) | Yes (parsed from free-text attribute, basis assumed 100g) | None |
Otto's | Grocery-adjacent (food, drugstore, baby) | Yes (priceLabels facet) | Yes (per-store stockLevel) | No | None |
Coop City | Department-store catalog (coop-city.ch: cosmetics, household, fashion, electronics). Opt-in: only queried when | Yes ( | Yes (per-store stockLevel, capped at 50) | No | None |
NormalizedProduct.nutrition is normalized to a per-100g (or per-100ml) basis with the standard Swiss labelling fields — energyKj, energyKcal, fat, saturatedFat, carbs, sugar, fiber, protein, salt — so consumers can sort across chains ("highest protein per 100g lasagne") even though each upstream API exposes the data on a different surface and in a different shape. Missing or unparseable fields are left undefined; adapters never fabricate values.
Configuration
Env var | Default | Effect |
| (unset) | Optional pre-supplied Denner Bearer JWT. Without it, the adapter self-registers anonymously on first use and rotates the token automatically. |
|
| Default Lidl store ID used when no |
| (default iOS Safari UA) | Override the User-Agent for Coop calls if DataDome ever blocks the default. |
|
|
|
| (unset) | Set to |
How it works
Each grocery chain is wrapped in an independent adapter (src/adapters/<chain>/) that handles authentication, HTTP calls, and raw-to-normalised mapping. Adapters all produce the same NormalizedProduct, NormalizedStore, and NormalizedPromotion shapes (defined in src/adapters/types.ts), so the rest of the system never has to know which chain it is talking to.
The HTTP utility (src/util/http.ts) underpins every adapter except Migros (which delegates to the migros-api-wrapper library): in-memory response caching with a 5-minute TTL, retry with exponential backoff (3 attempts, 250 ms base), per-host rate limiting (~10 requests per second), and a per-host circuit breaker that opens after 5 consecutive failures and resets after 60 seconds.
The shopping planner (src/services/planner.ts) fans out store and product searches in parallel across all active adapters, then feeds results into a strategy solver (src/services/strategy.ts) that supports three modes:
single_store: minimise the number of stops.split_cart: cheapest split across chains, with a configurable per-stop penalty.absolute_cheapest: cheapest split, ignoring stop count.
Cross-chain comparisons are kept fair by a category-text canonicality filter (src/services/matcher.ts, isCanonical). When at least one chain returns a product whose category text matches the query, results from chains that only returned tangential products (for example, Apfelschorle when searching for "apfel") are dropped from the comparison matrix for that item.
MCP client (any LLM)
│
│ MCP tool call
▼
src/index.ts ── buildRegistry() ────────────────────────────────────────┐
│ │
│ routes to tool handler │
▼ ▼
src/tools/ src/adapters/
find_stores.ts ──► geocoding ──► adapter.searchStores migros/
search_products.ts ──────────────► adapter.searchProducts coop/
get_product.ts ──────────────► adapter.getProduct aldi/
get_promotions.ts ──────────────► adapter.getPromotions denner/ (auto-auth)
find_stock.ts ──────────────► adapter.findStoresWithStock lidl/
plan_shopping.ts ──► geocoding ──► planner ──► strategy solver
│
NormalizedProduct
NormalizedStore
NormalizedPromotionBuild from source
node --version # requires Node.js >=20
git clone https://github.com/nicktcode/swissgroceries-mcp
cd swissgroceries-mcp
npm install
npm run buildTo also build the .mcpb bundle locally:
npx tsx scripts/build-mcpb.tsTroubleshooting
Coop "DataDome challenge" error
You hit Coop's bot protection. Set SWISSGROCERIES_USER_AGENT_COOP to a freshly captured iOS Safari User-Agent string and try again.
Denner "auth_expired" error
Rare, since the adapter rotates its token automatically. If it persists, unset any custom DENNER_JWT and let the adapter re-bootstrap from scratch.
Lidl returns 0 results
Lidl Switzerland has no online grocery shop; lidl.ch only lists the current and upcoming weekly offers (around 600 items, food and non-food). Search covers that assortment, so a product that isn't on offer this or next week returns 0 results. This is expected.
ZIP unknown error
The static lookup table covers all 3,190 official Swiss postcodes. If yours is missing, pass { lat, lng } directly or open an issue with the missing PLZ.
Migros stores nowhere near my location
The Migros store-search API caps at ~10 results per query. The adapter passes a city hint derived from your ZIP. If you call the adapter directly without ZIP-based geocoding, pass cityHint explicitly.
Development
npm test # full test suite, no network calls
RUN_LIVE=1 npm test # also runs live smoke tests against real chain APIs
npm run dev # tsx watcher for local iteration
SWISSGROCERIES_DISABLE_CACHE=1 RUN_LIVE=1 npm test # cache off, useful for debugging
SWISSGROCERIES_LOG_LEVEL=debug npm run dev # verbose loggingThe test suite uses Vitest. Fixture JSON files live under tests/fixtures/<chain>/. Capture scripts in scripts/ show how to refresh them.
Adding a new chain
See CONTRIBUTING.md for the full guide. Quick version:
Capture API responses with Charles Proxy or mitmproxy on the chain's iOS or Android app.
Create
src/adapters/<chain>/{client,tags,normalize,index}.tsfollowing the existing patterns. Usesrc/adapters/aldi/as the simplest reference.Map raw responses to
NormalizedProduct,NormalizedStore, andNormalizedPromotion.Declare capability flags accurately.
Register the adapter in
src/index.ts'sbuildRegistry().Add fixture-based tests under
tests/adapters/.
License
PolyForm Shield 1.0.0 from v0.10.0. You can use and modify it for
anything that doesn't compete with this software or the author's own products
built on it (Swiss grocery search, price comparison and shopping apps or
services), even if your product is free. Personal use in your own MCP client is
fine. See LICENSING.md or contact nick@thommen.it.
v0.7.5 to v0.9.0 were released under AGPL-3.0-only and v0.7.4 and earlier under MIT; those published versions keep their original licenses.
Available Tools
7 toolsfind_stockA
Check which stores of a given chain have a specific product in stock. Optionally filter by proximity to GPS coordinates or query a single store by ID. Not all chains support per-store stock queries; unsupported chains return a clear error. Use for "is this product available near Zurich HB?", "which Coop has item X in stock?".
| Name | Required | Description | Default |
|---|---|---|---|
| near | No | Optional location to filter nearby stores. Pass coordinates, a Swiss ZIP, or a free-text address. If omitted, all stores may be queried. | |
| chain | Yes | The chain to query for stock. Only chains with perStoreStock capability are supported. | |
| storeId | No | Query a single specific store by its chain-specific store ID. Takes precedence over `near`. | |
| productId | Yes | Chain-specific product ID to check stock for. Obtain via search_products or get_product. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations, so the description carries the full burden. It valuably discloses the unsupported-chain error behavior, but says nothing about auth requirements, rate limits, or the cost/latency of the Nominatim geocoding path (only the schema hints at preferring zip/lat/lng for speed).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded core capability followed by filtering options, caveat, and examples — all earning their place. The closing example list is slightly redundant with the schema's own description text but stays short.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Reasonable for a read-style lookup, but with no annotations and no output schema the description never states the return shape (store list with stock status?) or whether results are live. An agent can call it, but is left inferring the response.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so chain, productId, near, and storeId are already fully documented including precedence and the productId provenance. The description merely restates the proximity and single-store options, adding no syntax or format detail beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource+scope: which stores of a given chain carry a specific product in stock. The illustrative queries ("which Coop has item X in stock?") make it distinguishable from sibling find_stores and search_products.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explains the two usage modes (proximity filter vs. single store by ID), gives example queries, and warns that unsupported chains error out. It stops short of naming find_stores/search_products as the alternatives to use when the question is about stores or products rather than stock.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_storesA
Find grocery stores near a location, filtered by chain and search radius. Accepts a Swiss ZIP code, GPS coordinates, or a free-text address as the search center. Returns store name, address, chain, location, and opening hours where available. Use for "find a Migros near me", "which Coop branches are in 8001?", or before checking stock.
| Name | Required | Description | Default |
|---|---|---|---|
| near | Yes | Center of the search radius. Pass either coordinates, a Swiss ZIP, or a free-text address. | |
| chains | No | Limit results to specific chains. Omit to search all configured chains. Coop City (coopcity) is opt-in and only included when listed here. | |
| radiusKm | No | Search radius in kilometers (1–50). Defaults to 5 km. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full disclosure burden. It does state the return fields (name, address, chain, location, hours 'where available') and that a ZIP, coordinates, or free-text address are all accepted, which is genuinely useful. However it omits any mention of geocoding latency/cost, rate limits, permission requirements, or result-count/pagination behavior, leaving notable gaps for a no-annotation tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tight sentences: purpose first, then input modes, then return shape, then usage examples. Every sentence earns its place with no redundant restatement.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, and the description compensates by enumerating the returned fields, so an agent knows what to expect. Input modes are fully covered. It could go slightly further on result limits or the store-vs-stock distinction, but it is essentially complete for this complexity level.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so all three parameters (near variants, chains, radiusKm) are already fully documented in the schema, including defaults, the 1–50 km bound, and the coopcity opt-in rule. The description adds no syntax or semantic detail beyond what the schema provides; baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb+resource ('Find grocery stores') with scope qualifiers (near a location, filtered by chain and radius). It is clearly distinguishable from siblings like search_products, get_product, and find_stock, which operate on products/stock rather than store locations.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete trigger phrases ('find a Migros near me', 'which Coop branches are in 8001?') and a sequencing cue ('before checking stock') that implies chaining with find_stock. It stops short of explicitly naming the sibling to use for stock lookups, so no true when-not/alternative statement.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_productA
Fetch full product details for a specific chain + product ID pair. Returns { product, fetchedAt, fromCache }: the product carries price, brand, size, unit price, tags, category, image URL, and active promotions, while fetchedAt + fromCache report how fresh the data is. Obtain product IDs from search_products. Useful for drilling into a search result. Use for "get details for this Migros product" or "what is the unit price of this item?".
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Chain-specific product identifier, e.g. Migros cumulus ID or Coop product number. Obtain via search_products. | |
| chain | Yes | The grocery chain that owns this product ID. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden, and it does disclose notable behavior: the response shape { product, fetchedAt, fromCache } and that fromCache indicates caching/freshness. It stops short of covering failure modes, permissions, or rate limits.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose and return shape are front-loaded, and the trailing example prompts are short and illustrative. It is slightly longer than strictly needed, but every sentence carries some routing or behavioral information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description usefully enumerates the returned fields (price, brand, size, unit price, tags, category, image, promotions), which is the right compensation. Remaining gap is error behavior for invalid or unknown IDs.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and both parameters are already documented (chain enum, chain-specific ID). The description's 'chain + product ID pair' framing reinforces the composite-key semantics but adds little the schema doesn't already say.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Fetch full product details for a specific chain + product ID pair') and immediately distinguishes itself from search_products by being the drill-down step.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It names the prerequisite source of IDs ('Obtain product IDs from search_products') and characterizes the use case ('drilling into a search result'), with concrete example queries. It doesn't explicitly exclude other siblings, but no genuine alternative exists for fetching full details.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_promotionsA
List current promotional deals across configured Swiss grocery chains. Filter by chain, keyword, store ID, or how many days until the promotion expires. Returns promotion name, discount, validity dates, and applicable stores. Use for "what is on sale this week?", "any Migros deals on cheese?", or "promotions ending today".
| Name | Required | Description | Default |
|---|---|---|---|
| query | No | Optional keyword to filter promotions by product name, e.g. "Käse", "wine". | |
| chains | No | Limit to specific chains. Omit to fetch promotions from all configured chains. Coop City (coopcity) is opt-in and only included when listed here. | |
| storeIds | No | Restrict to promotions valid at these store IDs (chain-specific). Obtain store IDs from find_stores. | |
| endingWithinDays | No | Only return promotions ending within this many days (1–60). Useful for "ending soon" queries. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden: 'List' implies a read-only operation and it discloses the return fields (name, discount, validity dates, applicable stores), which is useful since no output schema exists. However, it says nothing about pagination, result caps, rate limits, or the opt-in behavior of some chains, so behavioral disclosure is only partial.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, front-loaded with the core action, then filters, then return shape, then usage examples. Every sentence earns its place and there is no redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-required-param read tool with no annotations and no output schema, the description covers what it does, what can be filtered, and what is returned. Missing only operational details such as pagination or limits, which are minor for this shape.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all four parameters thoroughly (including the coopcity opt-in nuance and the find_stores reference). The description's filter summary ('chain, keyword, store ID, or how many days until the promotion expires') largely restates the schema without adding syntax or format detail.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('List current promotional deals across configured Swiss grocery chains') and enumerates the filter dimensions. The domain is clearly distinct from siblings like search_products and find_stores, though no sibling is named explicitly for differentiation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The 'Use for ...' clause with three concrete example queries ('what is on sale this week?', 'any Migros deals on cheese?', 'promotions ending today') gives clear context for invocation. It stops short of naming alternatives or when-not-to-use cases (e.g., use search_products for non-promotional items).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
health_checkA
Probe each registered chain adapter with a trivial query and report status, latency, and capability flags. Use this when a chain seems missing from results or when debugging adapter problems.
| Name | Required | Description | Default |
|---|---|---|---|
| chains | No | Chains to probe. Default: all configured. | |
| timeoutMs | No | Per-chain timeout in milliseconds. Default 5000. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden, and it does disclose the probing mechanism (one trivial query per adapter) and the reported fields, which reassures the agent this is non-mutating. It omits that probing all adapters can take up to timeoutMs per chain (potentially tens of seconds), and never explicitly states the operation is read-only.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences: the first defines behavior and output, the second defines when to reach for it. Nothing is wasted and the core capability is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Zero required parameters, no output schema, and complete schema descriptions mean the description only needs to explain purpose, trigger, and return shape — all of which it does. Nothing an agent needs in order to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, and both 'chains' (default: all configured) and 'timeoutMs' (default 5000, max 30000) are documented in the schema itself. The description adds no parameter-level meaning, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (probe), resource (each registered chain adapter), mechanism (trivial query), and return payload (status, latency, capability flags). It is unmistakably distinct from the sibling tools, all of which fetch store/product/promotion data rather than adapter diagnostics.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives two explicit trigger conditions: a chain appearing to be missing from results, and debugging adapter problems. No when-not guidance or named alternative is given, but the diagnostic framing implicitly excludes normal search workflows.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
plan_shoppingA
Plan a multi-store shopping trip near a location, picking the best products across configured Swiss grocery chains. Items can be generic ("milch", "pasta") or pinned to a specific SKU. Returns a primary plan plus alternatives. Use when the user gives a list of items and asks "where should I shop?" or "what's cheapest?". Strategies: single_store (one chain), split_cart (multi-chain with stop penalty), absolute_cheapest (no penalty).
| Name | Required | Description | Default |
|---|---|---|---|
| near | Yes | Shopper's location — used to find nearby stores. Pass coordinates, ZIP, or address. | |
| items | Yes | The list of items to shop for. At least one item required. | |
| chains | No | Restrict the plan to these chains. Omit to consider all configured chains. Coop City (coopcity) is opt-in and only included when listed here. | |
| radiusKm | No | Only consider stores within this radius of the provided location (1–50 km). Default 5 km. | |
| strategy | Yes | single_store: buy everything at one chain (minimises trips). split_cart: allow multiple chains but add splitPenaltyChf per extra stop. absolute_cheapest: pick the cheapest source per item regardless of stops. | |
| splitPenaltyChf | No | Cost in CHF added per extra store stop in split_cart strategy. Default 2.00. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full disclosure burden. It does reveal return behavior ('Returns a primary plan plus alternatives') and the semantics of the three strategies including the stop penalty, which is useful. It does not state that the operation is read-only/non-mutating, whether it hits live chain APIs, or any limits — significant omissions given zero annotation coverage.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four sentences, front-loaded with purpose, then behavior, then usage trigger, then strategy summary. Efficient overall, though the strategy list duplicates the strategy enum's schema description and could be trimmed.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a fairly complex composite tool (6 params, nested item objects, three strategies) with no output schema and no annotations, the description says the result is 'a primary plan plus alternatives' but never sketches what that plan contains or how cost/stops are reported. It is adequate to invoke, but not complete enough to predict the response shape.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents every parameter including the strategy enum and the splitPenaltyChf semantics. The description's note that items can be generic or pinned to a SKU, and its strategy recap, largely restate what the schema already says, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description gives a specific verb and resource — 'Plan a multi-store shopping trip near a location, picking the best products across configured Swiss grocery chains' — which is a composite planning task no sibling (search_products, find_stores) performs. It is clear and distinctive, but it never names or contrasts with the sibling tools an agent might otherwise reach for.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives a concrete trigger: 'Use when the user gives a list of items and asks "where should I shop?" or "what's cheapest?"'. That is strong context, but there is no explicit exclusion (e.g., 'for a single product lookup use search_products instead'), so the agent must infer the boundary.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_productsA
Search for products across configured Swiss grocery chains (Migros, Coop, Aldi, Denner, Lidl, Farmy, Volgshop, Otto's, Coop City) by keyword. The coop chain covers the coop.ch supermarket assortment (groceries, household, drugstore) only. The Coop City department-store range (coop-city.ch: cosmetics, fashion, household, electronics) is the separate coopcity chain, which is opt-in: include it in chains to search it (it is skipped when chains is omitted). Supports optional filters for price, size range, and product tags (organic, vegan, budget, etc.). Returns results grouped by chain with normalised price, unit price, size, and promotion info. A sources map reports each chain's data freshness (fetchedAt timestamp + fromCache flag) so you can tell the user how current the prices are. Every queried chain has a key in byChain; when a chain fails (e.g. bot protection or an outage) its array is empty and errors lists the chain with a code and reason, so check errors before telling the user a chain has no results. Use for "find organic milk under 2 CHF", "compare pasta prices", or "search for gluten-free bread".
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of results per chain (1–50). Defaults to chain-specific limit. | |
| query | Yes | Search term in any language, e.g. "Milch", "pâtes", "Bier". At least 1 character. | |
| chains | No | Restrict search to specific chains. Omit to search all configured chains in parallel. Coop City (coopcity) is opt-in and only included when listed here. | |
| offset | No | Skip the first N results per chain. Use with `limit` to paginate. Default 0. | |
| filters | No | Optional product filters applied after search. | |
| storeIds | No | Filter results to products available in these store IDs (chain-specific internal IDs). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so well: it discloses failure semantics (empty array + entry in `errors` with code/reason on bot protection or outage), data-freshness reporting via a `sources` map (fetchedAt + fromCache), per-chain grouping, promotion info, and the opt-in behavior of coopcity. This is exactly the operational context an agent needs beyond structured fields.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single dense paragraph that is well front-loaded with the core purpose before drilling into chain semantics, return format, and failure handling. Every clause carries information, though the paragraph is long enough that the return-format and error details could be trimmed or split for readability.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite having no output schema, the description explains the result shape (byChain grouping, normalised price/unit price/size/promotion, sources freshness, errors array) and the key edge cases (failed chains produce empty arrays, coopcity opt-in). Nothing essential for correct invocation or result interpretation appears missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real meaning for the `chains` parameter by explaining what coop vs coopcity actually cover and that coopcity is silently skipped when `chains` is omitted. Filters (price, size, tags) are summarized but the schema already details them, so this is a modest lift above baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Starts with a specific verb+resource and scope: 'Search for products across configured Swiss grocery chains ... by keyword.' It names the chains and distinguishes the coop vs coopcity assortments, so an agent can tell it apart from find_stores, get_product, and get_promotions without opening schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides concrete use cases ('find organic milk under 2 CHF', 'compare pasta prices') and explicit conditional guidance for the opt-in coopcity chain and for checking `errors` before reporting empty results. It stops short of stating when to prefer get_product or get_promotions over this search tool, so it lacks explicit sibling exclusion.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
7 tool updates
v0.11.0- Changed
find_stock1 field changed- changed
Input schema / properties / chain / enumPrevious value: -[ - "migros", - "coop", - "aldi", - "denner", - "lidl", - "farmy", - "volgshop", - "ottos" -]New value: +[ + "migros", + "coop", + "aldi", + "denner", + "lidl", + "farmy", + "volgshop", + "ottos", + "coopcity" +]
- Changed
find_stores2 fields changed- changed
Input schema / properties / chains / descriptionPrevious value: -"Limit results to specific chains. Omit to search all configured chains."New value: +"Limit results to specific chains. Omit to search all configured chains. Coop City (coopcity) is opt-in and only included when listed here." - changed
Input schema / properties / chains / items / enumPrevious value: -[ - "migros", - "coop", - "aldi", - "denner", - "lidl", - "farmy", - "volgshop", - "ottos" -]New value: +[ + "migros", + "coop", + "aldi", + "denner", + "lidl", + "farmy", + "volgshop", + "ottos", + "coopcity" +]
- Changed
get_product1 field changed- changed
Input schema / properties / chain / enumPrevious value: -[ - "migros", - "coop", - "aldi", - "denner", - "lidl", - "farmy", - "volgshop", - "ottos" -]New value: +[ + "migros", + "coop", + "aldi", + "denner", + "lidl", + "farmy", + "volgshop", + "ottos", + "coopcity" +]
- Changed
get_promotions2 fields changed- changed
Input schema / properties / chains / descriptionPrevious value: -"Limit to specific chains. Omit to fetch promotions from all configured chains."New value: +"Limit to specific chains. Omit to fetch promotions from all configured chains. Coop City (coopcity) is opt-in and only included when listed here." - changed
Input schema / properties / chains / items / enumPrevious value: -[ - "migros", - "coop", - "aldi", - "denner", - "lidl", - "farmy", - "volgshop", - "ottos" -]New value: +[ + "migros", + "coop", + "aldi", + "denner", + "lidl", + "farmy", + "volgshop", + "ottos", + "coopcity" +]
- Changed
health_check1 field changed- changed
Input schema / properties / chains / items / enumPrevious value: -[ - "migros", - "coop", - "aldi", - "denner", - "lidl", - "farmy", - "volgshop", - "ottos" -]New value: +[ + "migros", + "coop", + "aldi", + "denner", + "lidl", + "farmy", + "volgshop", + "ottos", + "coopcity" +]
- Changed
plan_shopping4 fields changed- changed
Input schema / properties / chains / descriptionPrevious value: -"Restrict the plan to these chains. Omit to consider all configured chains."New value: +"Restrict the plan to these chains. Omit to consider all configured chains. Coop City (coopcity) is opt-in and only included when listed here." - changed
Input schema / properties / chains / items / enumPrevious value: -[ - "migros", - "coop", - "aldi", - "denner", - "lidl", - "farmy", - "volgshop", - "ottos" -]New value: +[ + "migros", + "coop", + "aldi", + "denner", + "lidl", + "farmy", + "volgshop", + "ottos", + "coopcity" +] - changed
Input schema / properties / items / items / properties / preferredChain / enumPrevious value: -[ - "migros", - "coop", - "aldi", - "denner", - "lidl", - "farmy", - "volgshop", - "ottos" -]New value: +[ + "migros", + "coop", + "aldi", + "denner", + "lidl", + "farmy", + "volgshop", + "ottos", + "coopcity" +] - changed
Input schema / properties / items / items / properties / preferredProductId / properties / chain / enumPrevious value: -[ - "migros", - "coop", - "aldi", - "denner", - "lidl", - "farmy", - "volgshop", - "ottos" -]New value: +[ + "migros", + "coop", + "aldi", + "denner", + "lidl", + "farmy", + "volgshop", + "ottos", + "coopcity" +]
- Changed
search_products2 fields changed- changed
Input schema / properties / chains / descriptionPrevious value: -"Restrict search to specific chains. Omit to search all configured chains in parallel."New value: +"Restrict search to specific chains. Omit to search all configured chains in parallel. Coop City (coopcity) is opt-in and only included when listed here." - changed
Input schema / properties / chains / items / enumPrevious value: -[ - "migros", - "coop", - "aldi", - "denner", - "lidl", - "farmy", - "volgshop", - "ottos" -]New value: +[ + "migros", + "coop", + "aldi", + "denner", + "lidl", + "farmy", + "volgshop", + "ottos", + "coopcity" +]
7 tool updates
v0.7.10- First observed
find_stock - First observed
find_stores - First observed
get_product - First observed
get_promotions - First observed
health_check - First observed
plan_shopping - First observed
search_products
TDQS
Scored across 7 tools
Each tool targets a distinct resource and action: stores (find_stores), product search (search_products), product detail (get_product), deals (get_promotions), stock (find_stock), diagnostics (health_check), and trip planning (plan_shopping). There is minor conceptual adjacency between search_products and get_promotions, but descriptions make the boundaries clear. No two tools appear to do the same thing.
All names are snake_case and mostly follow a verb_noun pattern (find_stores, search_products, get_product, get_promotions, find_stock, plan_shopping). 'health_check' breaks the pattern as a noun_phrase rather than verb_noun, and verb choice varies (find/search/get) though each is used sensibly. Minor deviation only.
Seven tools is well within the sweet spot for a grocery comparison server. Each tool earns its place covering a distinct capability, and there is no filler or redundancy.
The surface covers the full read-only lifecycle: locate stores, search and inspect products, find deals, check stock, and plan trips, plus a diagnostic probe. Minor gaps exist (no basket/cart or ordering flow), but for a price-and-availability domain these are likely out of scope and agents can work around them.
Maintenance
Related MCP Connectors
Unlock the power of food transparency with our Open Food Facts MCP server. Easily look up any food
Live prices, deals & optimal multi-stop shopping routes for German grocery & drug stores.
Hosted MCP for e-commerce: live product catalog, stock, and pricing for AI agents.
Google Shopping products, prices, sellers, and deals as structured data via a hosted MCP server.
Related MCP Servers
- AlicenseCqualityCmaintenanceA Model Context Protocol server enabling product searches across e-commerce platforms, price history tracking, and product specification-based searches using natural language prompts.2123 PyPI19MIT
- AlicenseAqualityBmaintenanceA local MCP server for grocery shopping, enabling product search, specials, and browsing across NZ supermarkets, with cart and order history for Countdown/Woolworths via browser-assisted login.141MIT
- AlicenseAqualityDmaintenanceMCP server that queries Norwegian grocery store flyers (kundeaviser) to help AI agents plan cheap meals based on current offers.71MIT
- AlicenseNot gradedqualityCmaintenanceMCP server for Dutch supermarkets and drugstores, enabling product search, price comparison, promotions, shopping lists, meal planning, price alerts, and budget tracking via natural language.MIT