MCP server and CLI for kassal.app grocery price API (personal use)
Strong tool definitions with comprehensive descriptions and well-structured schemas. All 16 tools have clear, action-verb names and detailed descriptions (avg 180 chars, within baseline 194). Input schemas use Zod with proper types and constraints. Key strengths: enum constraints (sort, aggregation), min/max bounds on numeric params, clear parameter descriptions with context. Weaknesses: output schemas not documented (no return type specs visible); some parameter descriptions could be more prescriptive about format/constraints; error handling guidance absent from descriptions.
Add an item to a kassal.app shopping list. Pass product_id (from search_products) to link the item to a product — the list then shows which store has it cheapest. Without product_id it is a free-text item.
Compare the price of one product (by EAN/barcode) across all Norwegian stores kassal.app tracks. Returns offers sorted cheapest first, plus cheapest/most expensive and the potential saving in NOK. Each offer has price_age_days; offers whose price is >90 days old are marked stale and excluded from cheapest/saving (stores stop updating prices for delisted products). This is the main tool for finding which store sells a product cheapest.
Look up a product from a store's product-page URL (e.g. a meny.no or oda.com link) and compare its price across stores. Sorted cheapest first.
Create a new shopping list on kassal.app. It appears in the user's Kassalapp mobile app.
Delete a kassal.app shopping list and ALL its items. Cannot be undone; the list may have been created by the user in their mobile app, so confirm with the user first. Requires confirmation string 'DELETE LIST'.
Output schemas not documented. Tool descriptions explain what is returned (e.g., 'Returns offers sorted cheapest first, plus cheapest/most expensive and potential saving in NOK') but no formal return type schema is visible in code. LLMs cannot plan downstream tool calls without knowing response structure.
Error handling guidance absent. Descriptions do not explain what errors can occur, how to recover, or what the LLM should do if a call fails. E.g., compare_prices_by_ean mentions 'stale' offers but does not explain what happens if EAN is invalid or not found.
Minimal parameter descriptions for simple lookups. Tools like get_product, get_store, rename_shopping_list have single-param inputs with minimal context. E.g., 'store_id' lacks guidance on where to obtain it or what happens if invalid.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 64 | 2026-07-28+ | v2 |
Get historical prices for up to 100 products (by EAN) across stores. Useful for spotting price trends and whether a 'sale' is genuine. Free tier covers up to 90 days.
Get full product details by kassal.app product id: description, ingredients, allergens, nutrition, weight and price history.
Look up a single product's details from a store's product-page URL, without cross-store comparison.
Get one kassal.app shopping list with its items. Items linked to a product include EAN, the cheapest store right now, potential saving and offer count — use compare_prices_by_ean on the EAN for the full store-by-store breakdown.
List the user's shopping lists on kassal.app (synced with the Kassalapp mobile app). Returns id, title and last-updated. Use get_shopping_list for the items.
Get details for one physical store by its kassal.app store id.
Remove one item from a kassal.app shopping list.
Rename an existing kassal.app shopping list.
Search Norwegian grocery products across stores (Meny, Kiwi, Rema 1000, Oda, Joker, Spar…) via kassal.app. Norwegian terms work best (e.g. 'melk', 'brød'). Returns compact results with price, unit price, store and EAN. To find where a product is cheapest: search for it here, then pass its EAN to compare_prices_by_ean. Note: unique=true collapses duplicates but drops price/store info — leave it off when you need prices.
Find physical grocery stores by name or near a location (lat/lng + km radius). Returns address, opening hours and contact info. group filters by chain, e.g. 'MENY_NO', 'KIWI', 'REMA_1000', 'SPAR_NO', 'JOKER_NO'.
Update a shopping list item: check/uncheck it (checked), change the text, or link a product (product_id).
Destructive operations lack confirmation pattern. delete_shopping_list requires a confirmation string, which is good, but remove_shopping_list_item does not. Agents could accidentally delete items without explicit user consent.
No pagination guidance in descriptions. search_products and search_stores accept page/size params but descriptions do not explain default behavior, max results, or when pagination is required. Agents may not know to paginate large result sets.