Skip to main content
Glama
mathem.py•6.61 kB
from fastmcp import FastMCP import httpx import json import os import re import csv from typing import Any, Dict, List from urllib.parse import quote import scrape_schema_recipe # type: ignore mcp = FastMCP("MatMCP đź›’") def load_recipes_from_csv() -> List[Dict[str, str]]: recipes = [] csv_file = "recipes.csv" try: if os.path.exists(csv_file): with open(csv_file, "r", encoding="utf-8") as f: reader = csv.DictReader(f) for row in reader: recipes.append({ "id": row["id"], "title": row["title"], "url": row["url"] }) except Exception: pass return recipes RECIPES: List[Dict[str, str]] = load_recipes_from_csv() def fetch_and_parse_recipe(url: str) -> Dict[str, Any]: headers = {"User-Agent": "MatMCP/0.1"} with httpx.Client( follow_redirects=True, timeout=30.0, headers=headers, ) as client: response = client.get(url) response.raise_for_status() html = response.text recipes = scrape_schema_recipe.loads( html, python_objects=False, ) if recipes and isinstance(recipes, list): recipe = recipes[0] if isinstance(recipe, dict): return recipe return {} def search_ingredients(query: str) -> List[Dict[str, Any]]: encoded_query = quote(query) search_url = f"https://www.mathem.se/se/search/products/?q={encoded_query}" headers = {"User-Agent": "MatMCP/0.1"} with httpx.Client( follow_redirects=True, timeout=30.0, headers=headers, ) as client: response = client.get(search_url) response.raise_for_status() html = response.text json_match = re.search( r'<script id="__NEXT_DATA__"[^>]*>(.+?)</script>', html, re.DOTALL ) if not json_match: return [] try: next_data = json.loads(json_match.group(1)) search_data = ( next_data.get("props", {}) .get("pageProps", {}) .get("dehydratedState", {}) ) queries = search_data.get("queries", []) product_query = None for query_item in queries: if "searchpageresponse" in str(query_item.get("queryKey", [])): product_query = query_item break if not product_query: return [] items = product_query.get("state", {}).get("data", {}).get("items", []) products = [] for item in items: if item.get("type") != "product": continue attrs = item.get("attributes", {}) product = { "id": attrs.get("id"), "name": attrs.get("name", ""), "description": attrs.get("nameExtra", ""), "brand": attrs.get("brand", ""), "price": ( f"{attrs.get('grossPrice', '')} " f"{attrs.get('currency', 'SEK')}" ), "unit_price": ( f"{attrs.get('grossUnitPrice', '')} " f"{attrs.get('currency', 'SEK')} /" f"{attrs.get('unitPriceQuantityAbbreviation', '')}" ), } classifiers = attrs.get("clientClassifiers", []) if classifiers: labels = [ c.get("name", "") for c in classifiers if c.get("name") ] if labels: product["labels"] = labels promotions = attrs.get("promotions", []) if promotions: offers = [ p.get("title", "") for p in promotions if p.get("title") ] if offers: product["offer"] = ", ".join(offers) if product["name"]: products.append(product) return products[:7] except (json.JSONDecodeError, KeyError, AttributeError): return [] def read_cookie_from_file() -> str: cookie_file = "cookie.txt" try: if os.path.exists(cookie_file): with open(cookie_file, "r", encoding="utf-8") as f: return f.read().strip() except Exception: pass return "" def add_to_basket(product_id: int, quantity: int = 1) -> Dict[str, Any]: url = ( "https://www.mathem.se/tienda-web-api/v1/cart/items/" "?group_by=recipes" ) payload = {"items": [{"product_id": product_id, "quantity": quantity}]} cookie = read_cookie_from_file() headers = { "User-Agent": "MatMCP/0.1", "Content-Type": "application/json", } if cookie: headers["Cookie"] = cookie try: with httpx.Client(timeout=30.0) as client: response = client.post(url, json=payload, headers=headers) response.raise_for_status() return { "success": True, "status_code": response.status_code, "response": response.json() if response.content else {}, "product_id": product_id, "quantity": quantity, } except httpx.HTTPStatusError as e: return { "success": False, "error": f"HTTP {e.response.status_code}", "message": e.response.text if e.response else "Unknown error", "product_id": product_id, "quantity": quantity, } except Exception as e: return { "success": False, "error": "Request failed", "message": str(e), "product_id": product_id, "quantity": quantity, } @mcp.tool def list_recipes() -> List[Dict[str, str]]: """List available recipes""" return RECIPES @mcp.tool def get_recipe_by_index(index: int) -> Dict[str, Any]: """Get a recipe by index from the list""" if index < 0 or index >= len(RECIPES): return {} url = RECIPES[index]["url"] return fetch_and_parse_recipe(url) @mcp.tool def search_mathem_ingredients(query: str) -> List[Dict[str, Any]]: """Search for ingredients on Mathem.se grocery store""" return search_ingredients(query) @mcp.tool def add_to_mathem_basket(product_id: int, quantity: int = 1) -> bool: """Add a product to the Mathem.se shopping basket""" result = add_to_basket(product_id, quantity) return result.get("success", False) if __name__ == "__main__": mcp.run()

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sleipner42/mathem-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server