{"name":"manamurah-mcp-server","version":"2.13.0","description":"MCP server for Malaysian PriceCatcher consumer price data. 15 strongly-typed tools proxied from manamurah.com, plus 6 embedded reference resources and 4 guided prompts with argument autocomplete.","publisher":"manamurah.com","license":"MIT","homepage":"https://manamurah.com","documentation":"https://mcp.manamurah.com/","changelog":"https://mcp.manamurah.com/changelog","icon":"https://manamurah.com/apple-touch-icon.png","protocolVersion":"2025-06-18","capabilities":{"tools":{},"prompts":{"listChanged":false},"resources":{"listChanged":false},"completions":{}},"endpoints":{"mcp":"/mcp","changelog":"/changelog","server_card":"/.well-known/mcp/server-card.json"},"tool_count":15,"tools":[{"name":"search_items","description":"Search the Malaysian PriceCatcher item catalogue by name in any language (Malay/English/Chinese/Tamil). Use this FIRST when the user mentions a food item by name to resolve it to the item_code that every other tool requires. Returns up to 20 matches with translations. Do not use for prices — chain to find_cheapest or price_history next. Tip: the full active item list (code + Malay/English name + category) is also available as the `manamurah://catalogue/items` resource — read it once to skip repeated lookups.","inputSchema":{"type":"object","properties":{"query":{"type":"string","minLength":1,"maxLength":64,"description":"Search text in any supported language. Examples: 'watermelon', 'tembikai', 'ayam', '鸡肉'."},"category":{"type":"string","maxLength":64,"description":"Optional item_category filter. Examples: 'BUAH-BUAHAN' (fruits), 'SAYUR-SAYURAN' (vegetables), 'DAGING' (meat), 'IKAN-IKAN' (fish). Case-sensitive."}},"required":["query"],"additionalProperties":false}},{"name":"find_cheapest","description":"Return up to 10 premises with the lowest price this week, cheapest first. Anchor EITHER on a numeric item_code (one SKU — call search_items first to resolve a name) OR on a category slug (e.g. 'BERAS' for rice, 'AYAM' for chicken) to search across every SKU in that category and return each store's single cheapest one — use category when the user names a staple ('rice', 'chicken') rather than a specific product. For 'cheapest near me / near <place>', pass latitude+longitude (geocode the place yourself; don't ask for coordinates) with an optional radius_km. PriceCatcher samples only a few SKUs per store each week, so a narrow geo/chain/SKU filter is often empty — when that happens the query AUTO-WIDENS (drops chain, expands the radius, then falls back to district/state/national) and reports how far in `widened`+`widen_note`: surface that caveat instead of claiming a store has no stock.","inputSchema":{"type":"object","properties":{"item_code":{"type":"integer","minimum":1,"description":"Numeric item ID from search_items. Omit when using category."},"category":{"type":"string","maxLength":64,"description":"item_category slug to search across all its SKUs (e.g. 'BERAS', 'AYAM'). Returns each store's single cheapest SKU. Use instead of item_code for staple categories."},"latitude":{"type":"number","minimum":0,"maximum":8,"description":"With longitude, restrict to stores near this coordinate. Geocode a named place (e.g. 'Kota Kemuning') yourself; do not ask the user for coordinates."},"longitude":{"type":"number","minimum":99,"maximum":120},"radius_km":{"type":"number","minimum":0.1,"maximum":50,"description":"Search radius in km (default 5). Auto-widens if nothing is found nearby."},"state":{"type":"string","maxLength":64,"description":"Malaysian state or federal territory, e.g. 'Selangor', 'W.P. Kuala Lumpur', 'Pulau Pinang'. Case-sensitive."},"district":{"type":"string","maxLength":64,"description":"Exact district name — e.g. 'Petaling', 'Klang', 'Kota Bharu'."},"chain":{"type":"string","maxLength":64,"description":"Exact chain name — e.g. 'AEON', 'MYDIN', 'LOTUS'S'."},"chain_type":{"type":"string","enum":["HYPERMARKET","SUPERMARKET","MINIMART","SUNDRY","CONVENIENCE","WET_MARKET","BORONG","RESTAURANT","FOODCOURT"],"description":"Filter by chain category."}},"additionalProperties":false}},{"name":"price_history","description":"Get weekly price trend for one item at a rollup scope (national / state / district / chain / urbanisation / region / chain_group). Oldest-first time series, up to 52 weeks. Use for questions like 'how has X trended over 3 months'. scope='region' compares peninsular vs East Malaysia ('semenanjung' vs 'borneo') — useful for headline averages that aren't peninsular-weighted like 'national'. scope='chain_group' rolls premises up by storefront type (slug values: 'supermarket', 'kedai-runcit', 'pasar') and is monthly-only — the upstream API only has chain_group rollups in the monthly index, so weekly history is not available.","inputSchema":{"type":"object","properties":{"item_code":{"type":"integer","minimum":1},"scope":{"type":"string","enum":["national","state","district","chain","urbanisation","region","chain_group"],"description":"Rollup scope. Non-'national' scopes require scope_value."},"scope_value":{"type":"string","maxLength":64,"description":"Required when scope != 'national'. State name for scope='state', chain name for 'chain', 'URBAN'/'SUBURBAN'/'RURAL' for 'urbanisation', 'semenanjung'/'borneo' for 'region', 'supermarket'/'kedai-runcit'/'pasar' for 'chain_group' (note hyphen in 'kedai-runcit')."},"weeks":{"type":"integer","minimum":1,"maximum":52,"description":"How many weeks of history to return (1-52, default 26)."}},"required":["item_code"],"additionalProperties":false}},{"name":"nearby_premises","description":"Find premises within a geographic radius of a coordinate. Up to 25 premises sorted by distance. Use when the user gives a location or asks 'shops near me'. Chain with find_cheapest for price checks at specific premises.","inputSchema":{"type":"object","properties":{"latitude":{"type":"number","minimum":0,"maximum":8,"description":"Latitude in degrees. Malaysia bbox is roughly 0.85–7.5."},"longitude":{"type":"number","minimum":99,"maximum":120,"description":"Longitude in degrees. Malaysia bbox is roughly 99.5–119.5."},"radius_km":{"type":"number","minimum":0.1,"maximum":50,"description":"Search radius in km (default 5)."},"chain_type":{"type":"string","enum":["HYPERMARKET","SUPERMARKET","MINIMART","SUNDRY","CONVENIENCE","WET_MARKET","BORONG","RESTAURANT","FOODCOURT"]},"chain":{"type":"string","maxLength":64}},"required":["latitude","longitude"],"additionalProperties":false}},{"name":"compare_prices","description":"Compare one item's current-week price across national / state / district / chain / urbanisation dimensions in a single call. Use for 'is this a good price' or 'how does X compare across states'. Resolve names to ids/slugs via the `manamurah://catalogue/items` and `manamurah://catalogue/states` resources.","inputSchema":{"type":"object","properties":{"item_code":{"type":"integer","minimum":1},"weekdate":{"type":"string","description":"Optional ISO-8601 week starting Monday, e.g. '2026-04-20'. Defaults to latest available."}},"required":["item_code"],"additionalProperties":false}},{"name":"list_chains","description":"Enumerate known retail chains with premise counts and geographic spread. Up to 50 chains sorted by premise count. Use to discover valid chain names before filtering find_cheapest or nearby_premises. The whole active chain set is also available as the `manamurah://catalogue/chains` resource.","inputSchema":{"type":"object","properties":{"query":{"type":"string","maxLength":64,"description":"Substring match on chain name (case-insensitive)."},"chain_type":{"type":"string","enum":["HYPERMARKET","SUPERMARKET","MINIMART","SUNDRY","CONVENIENCE","WET_MARKET","BORONG","RESTAURANT","FOODCOURT"]},"state":{"type":"string","maxLength":64,"description":"Malaysian state or federal territory, e.g. 'Selangor', 'W.P. Kuala Lumpur', 'Pulau Pinang'. Case-sensitive."}},"additionalProperties":false}},{"name":"price_change","description":"Compare one item's current week to its price 1/3/6/12 months ago at any rollup scope (national / state / district / chain / urbanisation / region / chain_group). Returns current_week, comparison_week, absolute_change, pct_change, and direction (up/down/stable). Use for 'how much has X changed' questions. scope='chain_group' rolls premises up by storefront type — slug values 'supermarket', 'kedai-runcit', 'pasar' — and resolves on the monthly index (no period flag needed).","inputSchema":{"type":"object","properties":{"item_code":{"type":"integer","minimum":1},"months":{"type":"integer","enum":[1,3,6,12],"description":"Comparison window. One of 1, 3, 6, 12."},"scope":{"type":"string","enum":["national","state","district","chain","urbanisation","region","chain_group"]},"scope_value":{"type":"string","maxLength":64}},"required":["item_code"],"additionalProperties":false}},{"name":"top_movers","description":"The items that moved most in price this week vs last week (or month vs prev month with period='monthly'). Returns top N risers and fallers sorted by absolute percentage change. Use for 'what went up/down this week' or 'biggest price changes'. The scope filters (state / region / chain_group) are mutually exclusive — pass at most one. chain_group requires period='monthly' (storefront-type rollups only exist in the monthly index).","inputSchema":{"type":"object","properties":{"category":{"type":"string","maxLength":64,"description":"Optional item_category filter."},"state":{"type":"string","maxLength":64,"description":"When set, movements are computed on the state rollup. Mutually exclusive with region and chain_group."},"region":{"type":"string","enum":["semenanjung","borneo"],"description":"When set, movements are computed on the region rollup ('semenanjung' = peninsular, 'borneo' = Sabah/Sarawak/Labuan). Mutually exclusive with state and chain_group."},"chain_group":{"type":"string","enum":["supermarket","kedai-runcit","pasar"],"description":"When set, movements are computed on the chain_group (storefront-type) rollup. Slug values: 'supermarket', 'kedai-runcit' (note hyphen), 'pasar'. Monthly-only — must be paired with period='monthly'. Mutually exclusive with state and region."},"period":{"type":"string","enum":["weekly","monthly"],"description":"Comparison grain. 'weekly' (default) = WoW; 'monthly' = MoM and surfaces yoy_pct per row. Required to be 'monthly' when chain_group is set."},"limit":{"type":"integer","minimum":1,"maximum":20}},"additionalProperties":false}},{"name":"category_trends","description":"Summarise price movement per item_category over 1/3/6/12 months at any rollup scope (national / state / district / chain / urbanisation / region / chain_group). Groups items by category, returns avg_pct_change + top riser + top faller per category. Use for 'which categories went up' or 'food inflation by category'. scope='chain_group' rolls premises up by storefront type — slug values 'supermarket', 'kedai-runcit', 'pasar' — and resolves on the monthly index.","inputSchema":{"type":"object","properties":{"months":{"type":"integer","enum":[1,3,6,12]},"scope":{"type":"string","enum":["national","state","district","chain","urbanisation","region","chain_group"]},"scope_value":{"type":"string","maxLength":64}},"additionalProperties":false}},{"name":"basket_watch","description":"Track total cost for a 1–20 item basket over 1/3/6/12 months at any rollup scope (national / state / district / chain / urbanisation / region / chain_group). Returns current_total, comparison_total, pct_change, and per-item breakdown. Items missing from either week are listed as missing_items and excluded from totals (no silent zero-fill). scope='chain_group' rolls premises up by storefront type — slug values 'supermarket', 'kedai-runcit', 'pasar' — and resolves on the monthly index.","inputSchema":{"type":"object","properties":{"item_codes":{"type":"array","items":{"type":"integer","minimum":1},"minItems":1,"maxItems":20,"description":"List of 1–20 item_codes from search_items."},"months":{"type":"integer","enum":[1,3,6,12]},"scope":{"type":"string","enum":["national","state","district","chain","urbanisation","region","chain_group"]},"scope_value":{"type":"string","maxLength":64}},"required":["item_codes"],"additionalProperties":false}},{"name":"chain_mom_movers","description":"Find the biggest month-over-month price movers within a chain (or across all chains). Returns top risers and fallers with MoM and YoY per row, plus the premise_count backing each average. Use for 'what's up at AEON this month', 'biggest monthly movers at MYDIN', or 'which DAGING items rose most across hypermarkets'. For week-over-week use top_movers (period='weekly'); for one item's full history use price_history.","inputSchema":{"type":"object","properties":{"chain":{"type":"string","maxLength":64,"description":"Restrict to one chain — e.g. 'AEON', 'MYDIN', 'LOTUS'S'. Omit to rank movers across all chains; each row then carries its own chain field."},"chain_type":{"type":"string","enum":["HYPERMARKET","SUPERMARKET","MINIMART","SUNDRY","CONVENIENCE","WET_MARKET","BORONG","RESTAURANT","FOODCOURT"],"description":"Restrict to one chain category. Combine with category to ask 'which DAGING items moved most across hypermarkets this month'."},"category":{"type":"string","maxLength":64,"description":"Optional item_category filter."},"limit":{"type":"integer","minimum":1,"maximum":20},"min_premises":{"type":"integer","minimum":1,"description":"Minimum reporting premises per chain-item bucket to include (noise gate). Default 3."}},"additionalProperties":false}},{"name":"region_gap","description":"Rank items by Semenanjung vs Borneo regional price gap. Returns top N items where Borneo is pricier (positive gap) and top N where Semenanjung is pricier (negative gap), in one round-trip. Built on the region monthly/weekly rollup. Use for surfacing regional disparities ('what items are notably more expensive in Borneo this month?') without double-querying scope='region' and diffing client-side. Tunable: `category` filter, `period` (monthly default), `limit`, `min_pct` to ignore parity-grade gaps.","inputSchema":{"type":"object","properties":{"category":{"type":"string","maxLength":64,"description":"Optional item_category filter. Examples: 'BUAH-BUAHAN', 'SAYUR-SAYURAN', 'DAGING', 'IKAN-IKAN'. Case-sensitive."},"period":{"type":"string","enum":["weekly","monthly"],"description":"Rollup grain. 'monthly' (default) resolves on the latest available month; 'weekly' on the latest ISO Monday week."},"weekdate":{"type":"string","description":"Weekly mode only. ISO Monday in YYYY-MM-DD form, e.g. '2026-04-20'. Defaults to latest available when omitted."},"month":{"type":"string","description":"Monthly mode only. YYYY-MM, e.g. '2026-04'. Defaults to latest available when omitted."},"limit":{"type":"integer","minimum":1,"maximum":20,"description":"Items per direction (default 10). Returns up to `limit` borneo_pricier rows and up to `limit` semenanjung_pricier rows."},"min_pct":{"type":"number","minimum":0,"description":"Minimum |gap_pct| (in percent) for an item to qualify. Default 1.0 — items within ±1% are treated as parity and excluded."}},"additionalProperties":false}},{"name":"fama_price_history","description":"Daily FAMA price time series for one item at a chosen price level (RUNCIT retail / BORONG wholesale / LADANG farm-gate) and geographic grain. FAMA is a separate catalogue from PriceCatcher — item_id here is FAMA's own 1..46 (not KPDN item_code). Common items: 13=AYAM PROSES STANDARD, 22=AYAM HIDUP, 46=TELUR AYAM, 31=TIMUN HIJAU, 10=KACANG PANJANG HIJAU, 44=BAYAM. Returns up to 90 days oldest-first; missing days (FAMA's publishing lag often hides the last 1–3 days) are listed in missing_dates rather than zero-filled. Do not use for KPDN items (use price_history) or for value-chain spread (use fama_margin).","inputSchema":{"type":"object","properties":{"item_id":{"type":"integer","minimum":1,"description":"FAMA item.id (1..46). Not the PriceCatcher item_code."},"level":{"type":"string","enum":["RUNCIT","BORONG","LADANG"],"description":"Price level: RUNCIT (retail), BORONG (wholesale), or LADANG (farm-gate)."},"grain":{"type":"string","enum":["national","state","daerah"],"description":"Geographic rollup. 'national' (default) for a single aggregate per day; 'state' for one of 16 states; 'daerah' for a specific district (sparse coverage)."},"state_slug":{"type":"string","maxLength":32,"description":"Required when grain='state' or 'daerah'. Lowercase FAMA state slug (e.g. 'johor', 'selangor', 'pulau-pinang')."},"daerah_slug":{"type":"string","maxLength":64,"description":"Required when grain='daerah'. Lowercase FAMA daerah slug scoped within the chosen state (e.g. 'johor-bahru')."},"days":{"type":"integer","minimum":1,"maximum":90,"description":"Trailing window in days (1-90, default 30)."}},"required":["item_id","level"],"additionalProperties":false}},{"name":"fama_margin","description":"Pivot FAMA's three price levels for one item into per-day rows with all three prices side-by-side and the inter-leg markup percentages already computed (ladang_to_borong_pct, borong_to_runcit_pct, ladang_to_runcit_pct). The unique value FAMA enables over weekly KPDN — answering 'where in the value chain did the price move?'. Spread fields are null on days a leg is missing; the coverage block (ladang_days/borong_days/runcit_days/full_chain_days) tells you how reliable the analysis is — a near-zero full_chain_days means the item lacks farm-gate coverage. grain='daerah' is intentionally unsupported because LADANG/BORONG coverage at daerah grain is too sparse. Use fama_price_history if you only need one level.","inputSchema":{"type":"object","properties":{"item_id":{"type":"integer","minimum":1,"description":"FAMA item.id (1..46). Items with all-three-level coverage include 22=AYAM HIDUP, 13=AYAM PROSES STANDARD, 46=TELUR AYAM, and the leafy vegetables."},"grain":{"type":"string","enum":["national","state"],"description":"Geographic rollup. 'national' (default) or 'state'."},"state_slug":{"type":"string","maxLength":32,"description":"Required when grain='state'. Lowercase FAMA state slug."},"days":{"type":"integer","minimum":1,"maximum":90,"description":"Trailing window in days (1-90, default 14)."}},"required":["item_id"],"additionalProperties":false}},{"name":"fama_top_movers","description":"Daily-cadence movers per FAMA price level — daily counterpart to top_movers, separable by level so callers can ask 'what jumped at the farm-gate' (level='LADANG') distinctly from 'what jumped at retail' (level='RUNCIT'). The comparison is anchored on the latest available date in the index (often 2-4 days behind today due to FAMA's publishing lag), not on today; days_actual echoes the realised gap. Items lacking either anchor or comparison observation are excluded — no zero-fill. LADANG coverage is the patchiest — expect shorter lists at level='LADANG' even with min_pct lowered. grain='daerah' is unsupported (sparse coverage).","inputSchema":{"type":"object","properties":{"level":{"type":"string","enum":["RUNCIT","BORONG","LADANG"],"description":"Price level to rank. RUNCIT and BORONG have the densest coverage; LADANG is usable but may return shorter lists."},"grain":{"type":"string","enum":["national","state"],"description":"'national' (default) or 'state'."},"state_slug":{"type":"string","maxLength":32,"description":"Required when grain='state'."},"days":{"type":"integer","minimum":1,"maximum":30,"description":"Lookback distance from the anchor date (1-30, default 7). Anchor is the latest available index date, not today."},"limit":{"type":"integer","minimum":1,"maximum":25,"description":"How many items per direction (1-25, default 10)."},"min_pct":{"type":"number","minimum":0,"description":"Minimum |pct_change| required for an item to qualify (default 1.0). Raise to filter to headline-grade moves only."}},"required":["level"],"additionalProperties":false}}],"resource_count":6,"resources":[{"uri":"manamurah://catalogue/items","name":"items","title":"Item catalogue","description":"All currently-active PriceCatcher items (observed in the last 12 months): code, Malay name, English name, unit, category. No prices — use the price tools. Discontinued items are excluded; for Chinese/Tamil names, aliases, or fuzzy search, call search_items.","mimeType":"application/json"},{"uri":"manamurah://catalogue/states","name":"states","title":"States & federal territories","description":"16 states / federal territories with id, name, slug, and region (semenanjung/borneo).","mimeType":"application/json"},{"uri":"manamurah://catalogue/categories","name":"categories","title":"Item categories","description":"Item categories among recent-active items, with item counts. Use a value as the `category` filter on search_items.","mimeType":"application/json"},{"uri":"manamurah://catalogue/chains","name":"chains","title":"Retail chains","description":"Retail chains and standalone premises observed recently, with premise_count and chain_type, sorted by premise_count. The whole-set companion to the list_chains tool.","mimeType":"application/json"},{"uri":"manamurah://meta/latest-week","name":"latest-week","title":"Data freshness","description":"Current data week + coverage: latest_weekdate, premises_reporting, items_with_data. Read this to know how fresh the prices are.","mimeType":"application/json"},{"uri":"manamurah://docs/methodology","name":"methodology","title":"Methodology & caveats","description":"How prices are computed: weekly-average cadence, equal-premise weighting, outlier filtering, and the n>=30 reliability guidance. Cite these caveats when reporting figures.","mimeType":"text/markdown"}],"prompt_count":4,"prompts":[{"name":"semak-dakwaan-harga","title":"Semak dakwaan harga (fact-check a price claim)","description":"Semak dakwaan harga PriceCatcher dan beri verdict (sahih/tidak tepat/separa tepat). | Fact-check a Malaysian price claim against PriceCatcher data and return a caveat-aware verdict. Output in Bahasa Melayu.","arguments":[{"name":"dakwaan","description":"The price claim to fact-check (free text, Malay or English).","required":true},{"name":"barang","description":"Item to focus on (optional; autocompletes).","required":false},{"name":"negeri","description":"State/FT to scope to (optional; autocompletes).","required":false}]},{"name":"basket-bulanan","title":"Kos basket bulanan (monthly basket cost)","description":"Kira kos basket barangan bulanan dan kenal pasti penggerak harga terbesar. | Total a monthly grocery basket's cost and flag the biggest movers. Output in Bahasa Melayu.","arguments":[{"name":"barang","description":"Comma-separated item names (up to 20; last token autocompletes).","required":true},{"name":"negeri","description":"State/FT to scope to (optional; autocompletes).","required":false}]},{"name":"banding-bandar-vs-nasional","title":"Banding negeri vs nasional (state vs national)","description":"Banding harga barang di sesebuah negeri dengan purata nasional, dengan kaveat liputan. | Compare an item's price in a state vs the national average, with coverage caveats. Output in Bahasa Melayu.","arguments":[{"name":"barang","description":"Item to compare (autocompletes).","required":true},{"name":"negeri","description":"State/FT to compare against the national average (autocompletes).","required":true}]},{"name":"cari-termurah","title":"Cari harga termurah (where is an item cheapest)","description":"Cari premis dengan harga termurah bagi sesuatu barang minggu ini, dengan kaveat liputan. | Find the cheapest premises for an item this week, with coverage caveats. Output in Bahasa Melayu.","arguments":[{"name":"barang","description":"Item to find the cheapest price for (autocompletes).","required":true},{"name":"negeri","description":"State/FT to scope the search to (optional; autocompletes).","required":false},{"name":"daerah","description":"District to narrow to (optional; autocompletes, filtered by negeri).","required":false}]}],"versioning":{"scheme":"semver","current":"2.13.0","policy":{"major":"Breaking: tool removed, required input added, output shape change, enum value removed.","minor":"Additive: new tool, new optional input field, new output field, broader enum.","patch":"Bug fixes, performance, response stability — no schema change."},"deprecation_window_days":90,"changelog":"/changelog"},"data_source":"https://data.gov.my PriceCatcher","data_license":"Open Data Licence (Malaysia) — public government data","upstream":"https://manamurah.com","rate_limit":"120 req / 60s per IP (enforced upstream)","auth":"none — public read-only"}