Skip to main content
Glama
CupOfOwls

Kroger MCP Server

search_products

Find grocery items at Kroger stores by searching product names, filtering by brand or fulfillment method, and specifying store location.

Instructions

    Search for products at a Kroger store.
    
    Args:
        search_term: Product search term (e.g., "milk", "bread", "organic apples")
        location_id: Store location ID (uses preferred location if not provided)
        limit: Number of results to return (1-50)
        fulfillment: Filter by fulfillment method (csp=curbside pickup, delivery, pickup)
        brand: Filter by brand name
    
    Returns:
        Dictionary containing product search results
    

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
search_termYes
location_idNo
limitNoNumber of results to return (1-50)
fulfillmentNo
brandNo

Implementation Reference

  • The core handler function decorated with @mcp.tool() that implements the search_products tool logic: validates inputs, gets Kroger client, calls API, formats results (products with pricing, inventory, aisles, images), handles errors.
    @mcp.tool()
    async def search_products(
        search_term: str,
        location_id: Optional[str] = None,
        limit: int = Field(default=10, ge=1, le=50, description="Number of results to return (1-50)"),
        fulfillment: Optional[Literal["csp", "delivery", "pickup"]] = None,
        brand: Optional[str] = None,
        ctx: Context = None
    ) -> Dict[str, Any]:
        """
        Search for products at a Kroger store.
        
        Args:
            search_term: Product search term (e.g., "milk", "bread", "organic apples")
            location_id: Store location ID (uses preferred location if not provided)
            limit: Number of results to return (1-50)
            fulfillment: Filter by fulfillment method (csp=curbside pickup, delivery, pickup)
            brand: Filter by brand name
        
        Returns:
            Dictionary containing product search results
        """
        # Use preferred location if none provided
        if not location_id:
            location_id = get_preferred_location_id()
            if not location_id:
                return {
                    "success": False,
                    "error": "No location_id provided and no preferred location set. Use set_preferred_location first."
                }
        
        if ctx:
            await ctx.info(f"Searching for '{search_term}' at location {location_id}")
        
        client = get_client_credentials_client()
        
        try:
            products = client.product.search_products(
                term=search_term,
                location_id=location_id,
                limit=limit,
                fulfillment=fulfillment,
                brand=brand
            )
            
            if not products or "data" not in products or not products["data"]:
                return {
                    "success": False,
                    "message": f"No products found matching '{search_term}'",
                    "data": []
                }
            
            # Format product data
            formatted_products = []
            for product in products["data"]:
                formatted_product = {
                    "product_id": product.get("productId"),
                    "upc": product.get("upc"),
                    "description": product.get("description"),
                    "brand": product.get("brand"),
                    "categories": product.get("categories", []),
                    "country_origin": product.get("countryOrigin"),
                    "temperature": product.get("temperature", {})
                }
                
                # Add item information (size, price, etc.)
                if "items" in product and product["items"]:
                    item = product["items"][0]
                    formatted_product["item"] = {
                        "size": item.get("size"),
                        "sold_by": item.get("soldBy"),
                        "inventory": item.get("inventory", {}),
                        "fulfillment": item.get("fulfillment", {})
                    }
                    
                    # Add pricing information
                    if "price" in item:
                        price = item["price"]
                        formatted_product["pricing"] = {
                            "regular_price": price.get("regular"),
                            "sale_price": price.get("promo"),
                            "regular_per_unit": price.get("regularPerUnitEstimate"),
                            "formatted_regular": format_currency(price.get("regular")),
                            "formatted_sale": format_currency(price.get("promo")),
                            "on_sale": price.get("promo") is not None and price.get("promo") < price.get("regular", float('inf'))
                        }
                
                # Add aisle information
                if "aisleLocations" in product:
                    formatted_product["aisle_locations"] = [
                        {
                            "description": aisle.get("description"),
                            "number": aisle.get("number"),
                            "side": aisle.get("side"),
                            "shelf_number": aisle.get("shelfNumber")
                        }
                        for aisle in product["aisleLocations"]
                    ]
                
                # Add image information
                if "images" in product and product["images"]:
                    formatted_product["images"] = [
                        {
                            "perspective": img.get("perspective"),
                            "url": img["sizes"][0].get("url") if img.get("sizes") else None,
                            "size": img["sizes"][0].get("size") if img.get("sizes") else None
                        }
                        for img in product["images"]
                        if img.get("sizes")
                    ]
                
                formatted_products.append(formatted_product)
            
            if ctx:
                await ctx.info(f"Found {len(formatted_products)} products")
            
            return {
                "success": True,
                "search_params": {
                    "search_term": search_term,
                    "location_id": location_id,
                    "limit": limit,
                    "fulfillment": fulfillment,
                    "brand": brand
                },
                "count": len(formatted_products),
                "data": formatted_products
            }
            
        except Exception as e:
            if ctx:
                await ctx.error(f"Error searching products: {str(e)}")
            return {
                "success": False,
                "error": str(e),
                "data": []
            }
  • Pydantic-based input schema defined by function parameters, including search_term (required), optional filters like limit, fulfillment, brand, with validation via Field.
    async def search_products(
        search_term: str,
        location_id: Optional[str] = None,
        limit: int = Field(default=10, ge=1, le=50, description="Number of results to return (1-50)"),
        fulfillment: Optional[Literal["csp", "delivery", "pickup"]] = None,
        brand: Optional[str] = None,
        ctx: Context = None
    ) -> Dict[str, Any]:
  • Registers all product tools including search_products by calling the register_tools function from product_tools module.
    product_tools.register_tools(mcp)
  • Imports the product_tools module which contains the register_tools function.
    from .tools import product_tools
  • Uses helper get_client_credentials_client() and calls the underlying Kroger SDK client.product.search_products() API method.
    client = get_client_credentials_client()
    
    try:
        products = client.product.search_products(
            term=search_term,
            location_id=location_id,
            limit=limit,
            fulfillment=fulfillment,
            brand=brand
        )
Behavior2/5

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

No annotations are provided, so the description carries the full burden of behavioral disclosure. It mentions that the tool 'Search[es] for products' but doesn't describe important behaviors: whether it requires authentication, has rate limits, returns paginated results, or handles errors. The description adds minimal context beyond the basic action, which is insufficient for a tool with no 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.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately sized and front-loaded: it starts with a clear purpose statement, followed by a structured 'Args' and 'Returns' section. Every sentence earns its place by providing essential information without redundancy. The formatting with bullet-like sections enhances readability without unnecessary verbosity.

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?

Given the complexity (5 parameters, no annotations, no output schema), the description is partially complete. It covers parameter semantics well but lacks behavioral context (e.g., authentication needs, error handling) and doesn't explain the return value beyond 'Dictionary containing product search results'. For a search tool with multiple parameters and no output schema, more detail on behavior and returns would improve completeness.

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 description coverage is low (20%), but the description compensates well by explaining all parameters in the 'Args' section. It adds meaning beyond the schema: for example, it clarifies that 'search_term' accepts examples like 'milk', 'bread', 'organic apples', and that 'fulfillment' filters by methods such as 'csp=curbside pickup, delivery, pickup'. This provides practical context that the schema lacks.

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: 'Search for products at a Kroger store.' It specifies the verb ('Search') and resource ('products'), making it easy to understand what the tool does. However, it doesn't explicitly differentiate from sibling tools like 'search_products_by_id' or 'get_product_details', which is why it doesn't reach a score of 5.

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. It doesn't mention sibling tools like 'search_products_by_id' (which likely searches by product ID rather than term) or 'get_product_details' (which might retrieve detailed info for a specific product). There's also no information about prerequisites (e.g., authentication) or typical use cases, leaving the agent to infer usage.

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/CupOfOwls/kroger-mcp'

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