Skip to main content
Glama
hung-ngm

coles-woolworths

by hung-ngm

get_woolworths_products

Search for Woolworths products by query to find items and compare options for shopping decisions.

Instructions

Search for products at Woolworths.

Args:
    query: The product search query.
    limit: Maximum number of products to return.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYes
limitNo

Implementation Reference

  • main.py:61-92 (handler)
    The MCP tool handler function 'get_woolworths_products' decorated with @mcp.tool(). It calls the woolworths_search_products helper, applies the limit, formats the product information (name, price, unit, store), and handles errors.
    @mcp.tool()
    async def get_woolworths_products(query: str, limit: int = 10) -> str:
        """Search for products at Woolworths.
    
        Args:
            query: The product search query.
            limit: Maximum number of products to return.
        """
        try:
            search_results = await asyncio.to_thread(woolworths_search_products, query=query)
    
            if search_results.get("status") == "error":
                return f"Error fetching Woolworths products: {search_results.get('message', 'Unknown error')}\nResponse: {search_results.get('response_text', '')}"
    
            products = search_results.get("products", [])
    
            # Apply limit after fetching
            products = products[: min(limit, len(products))]
    
            if not products:
                return f"No products found at Woolworths for '{query}'."
    
            formatted_products = []
            for p in products:
                price_str = f"${p['price']:.2f}" if p['price'] is not None else "N/A"
                unit_str = p['unit'] if p['unit'] else "N/A" # Ensure unit is not None
                formatted_products.append(
                    f"Name: {p['name']}\nPrice: {price_str}\nUnit: {unit_str}\nStore: {p['store']}"
                )
            return "\n---\n".join(formatted_products)
        except Exception as e:
            return f"An unexpected error occurred in get_woolworths_products: {str(e)}"
  • The core helper function 'search_products' (exported as 'woolworths_search_products') that performs HTTP GET to Woolworths search API, parses nested JSON products, extracts name, price (from Price/InstorePrice/WasPrice), determines unit from multiple fields with priorities, structures into list of products dicts, returns success/error dict.
    def search_products(query: str) -> Dict[str, Any]:
        """
        Search for products using the Woolworths API.
        
        Args:
            query (str): The search query
        
        Returns:
            Dict[str, Any]: The search results
        """
        try:
            # Format the API URL
            url = format_api_url(query)
            
            # Set up the headers
            headers = {
                "Accept": "application/json",
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
            }
            
            # Make the API request
            response = requests.get(url, headers=headers)
            
            # Check if the request was successful
            if response.status_code != 200:
                return {
                    "status": "error",
                    "message": f"API request failed with status code {response.status_code}",
                    "response_text": response.text
                }
            
            # Parse the JSON response
            response_data = response.json()
            
            # Extract and format the products
            products = []
            # The outer "Products" key in response_data contains a list of product categories/groups.
            # Each category/group then has its own "Products" key containing a list of actual products.
            if "Products" in response_data and response_data["Products"]:
                for product_category in response_data.get("Products", []): # product_category is a dict
                    actual_product_list = product_category.get("Products")
                    
                    if not isinstance(actual_product_list, list):
                        # Handle cases where product_category might be a product itself (e.g. direct search hit or promotion)
                        if isinstance(product_category, dict) and "Stockcode" in product_category and "Products" not in product_category:
                            actual_product_list = [product_category] # Treat the category as a single product list
                        else:
                            # If it's not a list and not a product-like dict, skip it.
                            # print(f"Skipping product_category due to unexpected structure: {product_category.get('Name')}")
                            continue
    
                    for product in actual_product_list: # This 'product' is the actual product item
                        
                        # Extract product information
                        name = product.get("DisplayName", product.get("Name", ""))
                        
                        # Extract price information
                        price = product.get("Price")
                        if price is None:
                            price = product.get("InstorePrice") # Check InstorePrice as well
                        if price is None:
                            price = product.get("WasPrice") # Fallback to WasPrice
                        
                        # Extract unit information
                        unit = ""
                        package_size_str = product.get("PackageSize", "")
                        cup_string_str = product.get("CupString", "")
                        cup_measure_str = product.get("CupMeasure", "")
                        api_unit_field = product.get("Unit", "")
    
                        # Priority 1: PackageSize
                        if package_size_str and isinstance(package_size_str, str):
                            ps_lower = package_size_str.lower()
                            if "kg" in ps_lower: unit = "kg"
                            elif "g" in ps_lower and "kg" not in ps_lower: unit = "g"
                            elif "l" in ps_lower and "ml" not in ps_lower: unit = "L"
                            elif "ml" in ps_lower: unit = "ml"
                            elif "each" in ps_lower: unit = "each"
                            elif "pack" in ps_lower or "pk" in ps_lower: unit = "pack"
                        
                        # Priority 2: CupString
                        if not unit and cup_string_str and isinstance(cup_string_str, str):
                            cs_lower = cup_string_str.lower()
                            parts = cs_lower.split('/')
                            target_str_for_unit = parts[-1].strip() if len(parts) > 1 else cs_lower
    
                            if "kg" in target_str_for_unit: unit = "kg"
                            elif "g" in target_str_for_unit and "kg" not in target_str_for_unit: unit = "g"
                            elif "l" in target_str_for_unit and "ml" not in target_str_for_unit: unit = "L"
                            elif "ml" in target_str_for_unit: unit = "ml"
                            elif "each" in target_str_for_unit or "ea" in target_str_for_unit: unit = "each"
                            elif "pack" in target_str_for_unit or "pk" in target_str_for_unit: unit = "pack"
    
                        # Priority 3: CupMeasure
                        if not unit and cup_measure_str and isinstance(cup_measure_str, str):
                            cm_lower = cup_measure_str.lower()
                            if "kg" in cm_lower: unit = "kg"
                            elif "g" in cm_lower and "kg" not in cm_lower: unit = "g"
                            elif "l" in cm_lower and "ml" not in cm_lower: unit = "L"
                            elif "ml" in cm_lower: unit = "ml"
                            elif "each" in cm_lower: unit = "each"
                            elif "pack" in cm_lower or "pk" in cm_lower: unit = "pack"
                        
                        # Priority 4: API "Unit" field (often "Each")
                        if not unit and api_unit_field and isinstance(api_unit_field, str):
                            auf_lower = api_unit_field.lower()
                            if "each" == auf_lower: # Exact match for "each"
                                unit = "each"
                            # Can add other specific unit mappings from api_unit_field if needed
                    
                        # Add the product to the list
                        products.append({
                            "name": name,
                            "price": float(price) if price is not None else None,
                            "unit": unit,
                            "store": STORE_NAME
                        })
            
            return {
                "status": "success",
                "query": query,
                "products": products,
                "product_count": len(products)
            }
        except Exception as e:
            return {
                "status": "error",
                "message": str(e)
            }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It mentions searching but doesn't describe what the search returns (e.g., product details, pricing, availability), whether it requires authentication, rate limits, or error conditions. This leaves significant gaps in understanding the tool's behavior.

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?

The description is appropriately sized with a clear purpose statement followed by parameter details. It's front-loaded with the main function and uses a structured format for parameters, though the 'Args' section could be integrated more smoothly into the narrative flow.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity of a product search tool with no annotations and no output schema, the description is incomplete. It doesn't explain what the return values look like (e.g., product list format), error handling, or contextual constraints like availability by region, making it inadequate for full understanding.

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?

The description includes an 'Args' section that lists and briefly describes both parameters ('query' and 'limit'), adding meaning beyond the input schema which has 0% description coverage. However, it doesn't provide details like query syntax examples, limit constraints, or default behavior, leaving some semantic gaps.

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 clearly states the tool's purpose with a specific verb ('Search') and resource ('products at Woolworths'), making it immediately understandable. However, it doesn't explicitly differentiate from its sibling tool 'get_coles_products' beyond the brand name, which is implied but not stated as a distinguishing factor.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. There's no mention of the sibling tool 'get_coles_products' or any context about when Woolworths-specific searches are appropriate versus other product search options.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/hung-ngm/coles-woolworths-mcp-server'

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