Skip to main content
Glama
nicktcode

Swissgroceries MCP

by nicktcode

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-mcp

Cursor / 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.json in the project, or ~/.cursor/mcp.json globally.

  • Cline / Continue / VS Code: see each client's MCP documentation.

  • Custom clients: any stdio-based MCP client can spawn npx -y @nicktcode/swissgroceries-mcp directly.

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_stores

Find grocery stores near a location, filtered by chain and radius.

search_products

Cross-chain product search with normalised price, unit price, size, and tags.

get_product

Full product details for a chain plus product ID pair.

get_promotions

Current promotional deals, filterable by chain, keyword, store, or expiry.

find_stock

Stores of a chain that have a given product in stock.

plan_shopping

Plan a multi-store trip for a shopping list near a location.

health_check

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

weekly_deals_digest

Summarise this week's best grocery deals across the configured chains. Optional category, chains, and location filters.

compare_basket_across_chains

Compare a shopping basket across all chains and recommend single-store vs split-cart strategies.

cheapest_recipe_ingredients

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

swissgroceries://chains

JSON list of registered chain adapters and their capabilities (productSearch, productDetail, storeSearch, promotions, perStoreStock, perStorePricing).

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 (on_sale filter)

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 chains includes coopcity

Yes (hasDiscount facet, current price only, no was-price or end date)

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

DENNER_JWT

(unset)

Optional pre-supplied Denner Bearer JWT. Without it, the adapter self-registers anonymously on first use and rotates the token automatically.

LIDL_DEFAULT_STORE

CH0149

Default Lidl store ID used when no storeIds are passed.

SWISSGROCERIES_USER_AGENT_COOP

(default iOS Safari UA)

Override the User-Agent for Coop calls if DataDome ever blocks the default.

SWISSGROCERIES_LOG_LEVEL

info

silent, info, or debug.

SWISSGROCERIES_DISABLE_CACHE

(unset)

Set to 1 to bypass the in-memory HTTP cache (useful for debugging).

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
                                            NormalizedPromotion

Build from source

node --version   # requires Node.js >=20
git clone https://github.com/nicktcode/swissgroceries-mcp
cd swissgroceries-mcp
npm install
npm run build

To also build the .mcpb bundle locally:

npx tsx scripts/build-mcpb.ts

Troubleshooting

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 logging

The 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:

  1. Capture API responses with Charles Proxy or mitmproxy on the chain's iOS or Android app.

  2. Create src/adapters/<chain>/{client,tags,normalize,index}.ts following the existing patterns. Use src/adapters/aldi/ as the simplest reference.

  3. Map raw responses to NormalizedProduct, NormalizedStore, and NormalizedPromotion.

  4. Declare capability flags accurately.

  5. Register the adapter in src/index.ts's buildRegistry().

  6. 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 tools
find_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?".

NameRequiredDescriptionDefault
nearNoOptional location to filter nearby stores. Pass coordinates, a Swiss ZIP, or a free-text address. If omitted, all stores may be queried.
chainYesThe chain to query for stock. Only chains with perStoreStock capability are supported.
storeIdNoQuery a single specific store by its chain-specific store ID. Takes precedence over `near`.
productIdYesChain-specific product ID to check stock for. Obtain via search_products or get_product.

TDQS

A3.8/5.0
Behavior3/5

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.

Conciseness4/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

NameRequiredDescriptionDefault
nearYesCenter of the search radius. Pass either coordinates, a Swiss ZIP, or a free-text address.
chainsNoLimit results to specific chains. Omit to search all configured chains. Coop City (coopcity) is opt-in and only included when listed here.
radiusKmNoSearch radius in kilometers (1–50). Defaults to 5 km.

TDQS

A4/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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?".

NameRequiredDescriptionDefault
idYesChain-specific product identifier, e.g. Migros cumulus ID or Coop product number. Obtain via search_products.
chainYesThe grocery chain that owns this product ID.

TDQS

A4.1/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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".

NameRequiredDescriptionDefault
queryNoOptional keyword to filter promotions by product name, e.g. "Käse", "wine".
chainsNoLimit to specific chains. Omit to fetch promotions from all configured chains. Coop City (coopcity) is opt-in and only included when listed here.
storeIdsNoRestrict to promotions valid at these store IDs (chain-specific). Obtain store IDs from find_stores.
endingWithinDaysNoOnly return promotions ending within this many days (1–60). Useful for "ending soon" queries.

TDQS

A3.8/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines4/5

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.

NameRequiredDescriptionDefault
chainsNoChains to probe. Default: all configured.
timeoutMsNoPer-chain timeout in milliseconds. Default 5000.

TDQS

A4.3/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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).

NameRequiredDescriptionDefault
nearYesShopper's location — used to find nearby stores. Pass coordinates, ZIP, or address.
itemsYesThe list of items to shop for. At least one item required.
chainsNoRestrict the plan to these chains. Omit to consider all configured chains. Coop City (coopcity) is opt-in and only included when listed here.
radiusKmNoOnly consider stores within this radius of the provided location (1–50 km). Default 5 km.
strategyYessingle_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.
splitPenaltyChfNoCost in CHF added per extra store stop in split_cart strategy. Default 2.00.

TDQS

A3.6/5.0
Behavior3/5

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.

Conciseness4/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines4/5

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".

NameRequiredDescriptionDefault
limitNoMaximum number of results per chain (1–50). Defaults to chain-specific limit.
queryYesSearch term in any language, e.g. "Milch", "pâtes", "Bier". At least 1 character.
chainsNoRestrict search to specific chains. Omit to search all configured chains in parallel. Coop City (coopcity) is opt-in and only included when listed here.
offsetNoSkip the first N results per chain. Use with `limit` to paginate. Default 0.
filtersNoOptional product filters applied after search.
storeIdsNoFilter results to products available in these store IDs (chain-specific internal IDs).

TDQS

A4.6/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

  1. 7 tool updatesv0.11.0
    • Changedfind_stock1 field changed
      • changedInput schema / properties / chain / enum
        Previous value: -[
        -  "migros",
        -  "coop",
        -  "aldi",
        -  "denner",
        -  "lidl",
        -  "farmy",
        -  "volgshop",
        -  "ottos"
        -]New value: +[
        +  "migros",
        +  "coop",
        +  "aldi",
        +  "denner",
        +  "lidl",
        +  "farmy",
        +  "volgshop",
        +  "ottos",
        +  "coopcity"
        +]
    • Changedfind_stores2 fields changed
      • changedInput schema / properties / chains / description
        Previous 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."
      • changedInput schema / properties / chains / items / enum
        Previous value: -[
        -  "migros",
        -  "coop",
        -  "aldi",
        -  "denner",
        -  "lidl",
        -  "farmy",
        -  "volgshop",
        -  "ottos"
        -]New value: +[
        +  "migros",
        +  "coop",
        +  "aldi",
        +  "denner",
        +  "lidl",
        +  "farmy",
        +  "volgshop",
        +  "ottos",
        +  "coopcity"
        +]
    • Changedget_product1 field changed
      • changedInput schema / properties / chain / enum
        Previous value: -[
        -  "migros",
        -  "coop",
        -  "aldi",
        -  "denner",
        -  "lidl",
        -  "farmy",
        -  "volgshop",
        -  "ottos"
        -]New value: +[
        +  "migros",
        +  "coop",
        +  "aldi",
        +  "denner",
        +  "lidl",
        +  "farmy",
        +  "volgshop",
        +  "ottos",
        +  "coopcity"
        +]
    • Changedget_promotions2 fields changed
      • changedInput schema / properties / chains / description
        Previous 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."
      • changedInput schema / properties / chains / items / enum
        Previous value: -[
        -  "migros",
        -  "coop",
        -  "aldi",
        -  "denner",
        -  "lidl",
        -  "farmy",
        -  "volgshop",
        -  "ottos"
        -]New value: +[
        +  "migros",
        +  "coop",
        +  "aldi",
        +  "denner",
        +  "lidl",
        +  "farmy",
        +  "volgshop",
        +  "ottos",
        +  "coopcity"
        +]
    • Changedhealth_check1 field changed
      • changedInput schema / properties / chains / items / enum
        Previous value: -[
        -  "migros",
        -  "coop",
        -  "aldi",
        -  "denner",
        -  "lidl",
        -  "farmy",
        -  "volgshop",
        -  "ottos"
        -]New value: +[
        +  "migros",
        +  "coop",
        +  "aldi",
        +  "denner",
        +  "lidl",
        +  "farmy",
        +  "volgshop",
        +  "ottos",
        +  "coopcity"
        +]
    • Changedplan_shopping4 fields changed
      • changedInput schema / properties / chains / description
        Previous 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."
      • changedInput schema / properties / chains / items / enum
        Previous value: -[
        -  "migros",
        -  "coop",
        -  "aldi",
        -  "denner",
        -  "lidl",
        -  "farmy",
        -  "volgshop",
        -  "ottos"
        -]New value: +[
        +  "migros",
        +  "coop",
        +  "aldi",
        +  "denner",
        +  "lidl",
        +  "farmy",
        +  "volgshop",
        +  "ottos",
        +  "coopcity"
        +]
      • changedInput schema / properties / items / items / properties / preferredChain / enum
        Previous value: -[
        -  "migros",
        -  "coop",
        -  "aldi",
        -  "denner",
        -  "lidl",
        -  "farmy",
        -  "volgshop",
        -  "ottos"
        -]New value: +[
        +  "migros",
        +  "coop",
        +  "aldi",
        +  "denner",
        +  "lidl",
        +  "farmy",
        +  "volgshop",
        +  "ottos",
        +  "coopcity"
        +]
      • changedInput schema / properties / items / items / properties / preferredProductId / properties / chain / enum
        Previous value: -[
        -  "migros",
        -  "coop",
        -  "aldi",
        -  "denner",
        -  "lidl",
        -  "farmy",
        -  "volgshop",
        -  "ottos"
        -]New value: +[
        +  "migros",
        +  "coop",
        +  "aldi",
        +  "denner",
        +  "lidl",
        +  "farmy",
        +  "volgshop",
        +  "ottos",
        +  "coopcity"
        +]
    • Changedsearch_products2 fields changed
      • changedInput schema / properties / chains / description
        Previous 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."
      • changedInput schema / properties / chains / items / enum
        Previous value: -[
        -  "migros",
        -  "coop",
        -  "aldi",
        -  "denner",
        -  "lidl",
        -  "farmy",
        -  "volgshop",
        -  "ottos"
        -]New value: +[
        +  "migros",
        +  "coop",
        +  "aldi",
        +  "denner",
        +  "lidl",
        +  "farmy",
        +  "volgshop",
        +  "ottos",
        +  "coopcity"
        +]
  2. 7 tool updatesv0.7.10
    • First observedfind_stock
    • First observedfind_stores
    • First observedget_product
    • First observedget_promotions
    • First observedhealth_check
    • First observedplan_shopping
    • First observedsearch_products

TDQS

A4/5.0

Scored across 7 tools

Disambiguation5/5

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.

Naming Consistency4/5

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.

Tool Count5/5

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.

Completeness4/5

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

ActivityMaintained
ResponsivenessWithin a week

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    B
    maintenance
    A 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.
    14
    1
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    MCP server that queries Norwegian grocery store flyers (kundeaviser) to help AI agents plan cheap meals based on current offers.
    7
    1
    MIT