Skip to main content
Glama
benpeke

Turbify Store MCP Server

by benpeke

advanced_search_catalog_items

Search Turbify Store catalog items using multiple criteria with flexible matching options and pagination controls.

Instructions

    Advanced search for items in the Turbify Store catalog with multiple criteria.
    
    Args:
        criteria_list: List of criteria dictionaries, each with 'attribute', 'operator', and 'value'
                      Example: [{"attribute": "price", "operator": "gt", "value": "10.00"}]
        match_type: How to match criteria - "all" (AND) or "any" (OR) (default: "all")
        table_id: Optional table ID to search within
        start_index: Starting index for pagination (default: 1)
        end_index: Ending index for pagination (default: 100, max: 1000)
    
    Returns:
        JSON string with search results
    

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
criteria_listYes
match_typeNoall
table_idNo
start_indexNo
end_indexNo

Implementation Reference

  • The core handler function for the 'advanced_search_catalog_items' tool. It validates input criteria, performs type checks on attributes and operators, and calls the Turbify API's advanced_search method to retrieve matching item IDs.
    @mcp.tool()
    def advanced_search_catalog_items(
        criteria_list: List[dict],
        match_type: str = "all",
        table_id: Optional[str] = None,
        start_index: int = 1,
        end_index: int = 100
    ) -> str:
        """
        Advanced search for items in the Turbify Store catalog with multiple criteria.
        
        Args:
            criteria_list: List of criteria dictionaries, each with 'attribute', 'operator', and 'value'
                          Example: [{"attribute": "price", "operator": "gt", "value": "10.00"}]
            match_type: How to match criteria - "all" (AND) or "any" (OR) (default: "all")
            table_id: Optional table ID to search within
            start_index: Starting index for pagination (default: 1)
            end_index: Ending index for pagination (default: 100, max: 1000)
        
        Returns:
            JSON string with search results
        """
        if end_index > 1000:
            end_index = 1000
    
        # Validate match_type
        if match_type not in ["all", "any"]:
            return json.dumps({
                "status": "error",
                "success": False,
                "errors": ["match_type must be 'all' or 'any'"],
                "criteria_list": criteria_list
            }, indent=2)
    
        # Validate criteria_list
        if not criteria_list:
            return json.dumps({
                "status": "error",
                "success": False,
                "errors": ["At least one search criterion is required"],
                "criteria_list": criteria_list
            }, indent=2)
    
        # Define attribute types and supported operators based on API documentation
        string_attributes = {
            "id", "name", "code", "options", "headline", "caption", "abstract", "label", 
            "product url", "manufacturer", "brand", "gender", "upc", "manufacturer-part-number", 
            "model-number", "isbn", "merchant-category", "color", "size", "age-range", 
            "promo-text", "style-number", "style", "ypath", "yahoo-shopping-category", "ean"
        }
        
        numeric_attributes = {
            "price", "sale-price", "ship-weight", "msrp", "personalization-charge"
        }
        
        boolean_attributes = {
            "orderable", "taxable", "gift-certificate", "need-bill", "need-payment", 
            "need-ship", "in-yshopping"
        }
        
        enumeration_attributes = {
            "age-group", "availability", "classification", "condition", "gender", "medium"
        }
        
        all_valid_attributes = string_attributes | numeric_attributes | boolean_attributes | enumeration_attributes
        valid_operators = {"lt", "gt", "cn", "eq"}
    
        # Validate each criterion
        for i, criteria in enumerate(criteria_list):
            if not all(key in criteria for key in ["attribute", "operator", "value"]):
                return json.dumps({
                    "status": "error",
                    "success": False,
                    "errors": [f"Criterion {i} must have 'attribute', 'operator', and 'value' fields"],
                    "criteria_list": criteria_list
                }, indent=2)
            
            attribute = criteria["attribute"]
            operator = criteria["operator"].lower()  # Operators are case-sensitive and must be lowercase
            value = str(criteria["value"])
            
            # Validate operator
            if operator not in valid_operators:
                return json.dumps({
                    "status": "error",
                    "success": False,
                    "errors": [f"Criterion {i}: Invalid operator '{operator}'. Valid operators are: {', '.join(valid_operators)}"],
                    "criteria_list": criteria_list
                }, indent=2)
            
            # Validate attribute
            if attribute not in all_valid_attributes:
                # Check if it might be a custom field (we'll allow it but warn in documentation)
                pass  # Custom fields are allowed
            
            # Validate operator against attribute type
            if attribute in boolean_attributes:
                if operator != "eq":
                    return json.dumps({
                        "status": "error",
                        "success": False,
                        "errors": [f"Criterion {i}: Boolean attributes only support 'eq' operator"],
                        "criteria_list": criteria_list
                    }, indent=2)
                if value not in ["0", "1"]:
                    return json.dumps({
                        "status": "error",
                        "success": False,
                        "errors": [f"Criterion {i}: Boolean attributes only accept values '0' or '1'"],
                        "criteria_list": criteria_list
                    }, indent=2)
            elif attribute in numeric_attributes:
                if operator in ["lt", "gt", "eq"]:
                    try:
                        float(value)
                    except ValueError:
                        return json.dumps({
                            "status": "error",
                            "success": False,
                            "errors": [f"Criterion {i}: Numeric attributes require numeric values"],
                            "criteria_list": criteria_list
                        }, indent=2)
            elif attribute in enumeration_attributes:
                if operator != "eq":
                    return json.dumps({
                        "status": "error",
                        "success": False,
                        "errors": [f"Criterion {i}: Enumeration attributes only support 'eq' operator"],
                        "criteria_list": criteria_list
                    }, indent=2)
    
        try:
            response = client.advanced_search(criteria_list, match_type, table_id, start_index, end_index)
            
            # Extract item IDs from the response
            item_ids = response.item_ids if response.item_ids else []
            
            return json.dumps({
                "status": response.status,
                "success": response.is_success,
                "messages": response.success_messages,
                "errors": response.error_messages,
                "criteria_list": criteria_list,
                "match_type": match_type,
                "table_id": table_id,
                "start_index": start_index,
                "end_index": end_index,
                "total_items": len(item_ids),
                "item_ids": item_ids
            }, indent=2)
            
        except APIError as e:
            return json.dumps({
                "status": "error",
                "success": False,
                "errors": [str(e)],
                "criteria_list": criteria_list
            }, indent=2)
  • Input schema defined by function type hints (List[dict] for criteria, strings, ints) and comprehensive docstring explaining parameters, operators, attributes, and validation rules.
    def advanced_search_catalog_items(
        criteria_list: List[dict],
        match_type: str = "all",
        table_id: Optional[str] = None,
        start_index: int = 1,
        end_index: int = 100
    ) -> str:
        """
        Advanced search for items in the Turbify Store catalog with multiple criteria.
        
        Args:
            criteria_list: List of criteria dictionaries, each with 'attribute', 'operator', and 'value'
                          Example: [{"attribute": "price", "operator": "gt", "value": "10.00"}]
            match_type: How to match criteria - "all" (AND) or "any" (OR) (default: "all")
            table_id: Optional table ID to search within
            start_index: Starting index for pagination (default: 1)
            end_index: Ending index for pagination (default: 100, max: 1000)
        
        Returns:
            JSON string with search results
        """
  • Top-level registration of catalog tools (including advanced_search_catalog_items) by calling register_catalog_tools on the MCP server instance during server setup.
    register_catalog_tools(mcp)
  • Function that defines all catalog tools using @mcp.tool() decorators, effectively registering them when called.
    def register_catalog_tools(mcp):
        """Register catalog management tools with the MCP server."""
        
        # Initialize client (will be reused across tool calls)
        client = TurbifyStoreAPIClient()
        
        @mcp.tool()
        def get_catalog_items(item_ids: List[str]) -> str:
            """
            Get details for multiple catalog items.
            
            Args:
                item_ids: List of item IDs to retrieve
            
            Returns:
                JSON string with item details
            """
            try:
                response = client.get_item_details(item_ids)
                
                # Convert items to dictionaries if they're not already
                items = response.items if response.items else []
                serializable_items = []
                for item in items:
                    if item and not isinstance(item, dict):
                        # If it's a Pydantic model, convert to dict
                        if hasattr(item, 'dict'):
                            serializable_items.append(item.dict())
                        # If it's a dataclass, convert to dict
                        elif hasattr(item, '__dict__'):
                            serializable_items.append(item.__dict__)
                        # Otherwise, try to serialize as is
                        else:
                            serializable_items.append(dict(item))
                    else:
                        serializable_items.append(item)
                
                return json.dumps({
                    "status": response.status,
                    "success": response.is_success,
                    "messages": response.success_messages,
                    "errors": response.error_messages,
                    "item_count": len(serializable_items),
                    "items": serializable_items
                }, indent=2)
                
            except APIError as e:
                return json.dumps({
                    "status": "error",
                    "success": False,
                    "errors": [str(e)],
                    "item_ids": item_ids
                }, indent=2)
        
        @mcp.tool()
        def search_catalog_items(
            keyword: str,
            start_index: int = 1,
            end_index: int = 100
        ) -> str:
            """
            Search for items in the Turbify Store catalog.
            Note: This simple search only matches the keyword against item ID, name, or code fields.
            For more advanced search capabilities against other fields, use advanced_search_catalog_items.
            
            Args:
                keyword: Search keyword (matches against ID, name, or code fields only)
                start_index: Starting index for pagination (default: 1)
                end_index: Ending index for pagination (default: 100, max: 1000)
            
            Returns:
                JSON string with search results
            """
            if end_index > 1000:
                end_index = 1000
    
            try:
                response = client.simple_search(keyword, start_index, end_index)
                
                # Extract item IDs from the response
                item_ids = response.item_ids if response.item_ids else []
                
                return json.dumps({
                    "status": response.status,
                    "success": response.is_success,
                    "messages": response.success_messages,
                    "errors": response.error_messages,
                    "keyword": keyword,
                    "start_index": start_index,
                    "end_index": end_index,
                    "total_items": len(item_ids),
                    "item_ids": item_ids
                }, indent=2)
                
            except APIError as e:
                return json.dumps({
                    "status": "error",
                    "success": False,
                    "errors": [str(e)],
                    "keyword": keyword
                }, indent=2)
    
        @mcp.tool()
        def advanced_search_catalog_items(
            criteria_list: List[dict],
            match_type: str = "all",
            table_id: Optional[str] = None,
            start_index: int = 1,
            end_index: int = 100
        ) -> str:
            """
            Advanced search for items in the Turbify Store catalog with multiple criteria.
            
            Args:
                criteria_list: List of criteria dictionaries, each with 'attribute', 'operator', and 'value'
                              Example: [{"attribute": "price", "operator": "gt", "value": "10.00"}]
                match_type: How to match criteria - "all" (AND) or "any" (OR) (default: "all")
                table_id: Optional table ID to search within
                start_index: Starting index for pagination (default: 1)
                end_index: Ending index for pagination (default: 100, max: 1000)
            
            Returns:
                JSON string with search results
            """
            if end_index > 1000:
                end_index = 1000
    
            # Validate match_type
            if match_type not in ["all", "any"]:
                return json.dumps({
                    "status": "error",
                    "success": False,
                    "errors": ["match_type must be 'all' or 'any'"],
                    "criteria_list": criteria_list
                }, indent=2)
    
            # Validate criteria_list
            if not criteria_list:
                return json.dumps({
                    "status": "error",
                    "success": False,
                    "errors": ["At least one search criterion is required"],
                    "criteria_list": criteria_list
                }, indent=2)
    
            # Define attribute types and supported operators based on API documentation
            string_attributes = {
                "id", "name", "code", "options", "headline", "caption", "abstract", "label", 
                "product url", "manufacturer", "brand", "gender", "upc", "manufacturer-part-number", 
                "model-number", "isbn", "merchant-category", "color", "size", "age-range", 
                "promo-text", "style-number", "style", "ypath", "yahoo-shopping-category", "ean"
            }
            
            numeric_attributes = {
                "price", "sale-price", "ship-weight", "msrp", "personalization-charge"
            }
            
            boolean_attributes = {
                "orderable", "taxable", "gift-certificate", "need-bill", "need-payment", 
                "need-ship", "in-yshopping"
            }
            
            enumeration_attributes = {
                "age-group", "availability", "classification", "condition", "gender", "medium"
            }
            
            all_valid_attributes = string_attributes | numeric_attributes | boolean_attributes | enumeration_attributes
            valid_operators = {"lt", "gt", "cn", "eq"}
    
            # Validate each criterion
            for i, criteria in enumerate(criteria_list):
                if not all(key in criteria for key in ["attribute", "operator", "value"]):
                    return json.dumps({
                        "status": "error",
                        "success": False,
                        "errors": [f"Criterion {i} must have 'attribute', 'operator', and 'value' fields"],
                        "criteria_list": criteria_list
                    }, indent=2)
                
                attribute = criteria["attribute"]
                operator = criteria["operator"].lower()  # Operators are case-sensitive and must be lowercase
                value = str(criteria["value"])
                
                # Validate operator
                if operator not in valid_operators:
                    return json.dumps({
                        "status": "error",
                        "success": False,
                        "errors": [f"Criterion {i}: Invalid operator '{operator}'. Valid operators are: {', '.join(valid_operators)}"],
                        "criteria_list": criteria_list
                    }, indent=2)
                
                # Validate attribute
                if attribute not in all_valid_attributes:
                    # Check if it might be a custom field (we'll allow it but warn in documentation)
                    pass  # Custom fields are allowed
                
                # Validate operator against attribute type
                if attribute in boolean_attributes:
                    if operator != "eq":
                        return json.dumps({
                            "status": "error",
                            "success": False,
                            "errors": [f"Criterion {i}: Boolean attributes only support 'eq' operator"],
                            "criteria_list": criteria_list
                        }, indent=2)
                    if value not in ["0", "1"]:
                        return json.dumps({
                            "status": "error",
                            "success": False,
                            "errors": [f"Criterion {i}: Boolean attributes only accept values '0' or '1'"],
                            "criteria_list": criteria_list
                        }, indent=2)
                elif attribute in numeric_attributes:
                    if operator in ["lt", "gt", "eq"]:
                        try:
                            float(value)
                        except ValueError:
                            return json.dumps({
                                "status": "error",
                                "success": False,
                                "errors": [f"Criterion {i}: Numeric attributes require numeric values"],
                                "criteria_list": criteria_list
                            }, indent=2)
                elif attribute in enumeration_attributes:
                    if operator != "eq":
                        return json.dumps({
                            "status": "error",
                            "success": False,
                            "errors": [f"Criterion {i}: Enumeration attributes only support 'eq' operator"],
                            "criteria_list": criteria_list
                        }, indent=2)
    
            try:
                response = client.advanced_search(criteria_list, match_type, table_id, start_index, end_index)
                
                # Extract item IDs from the response
                item_ids = response.item_ids if response.item_ids else []
                
                return json.dumps({
                    "status": response.status,
                    "success": response.is_success,
                    "messages": response.success_messages,
                    "errors": response.error_messages,
                    "criteria_list": criteria_list,
                    "match_type": match_type,
                    "table_id": table_id,
                    "start_index": start_index,
                    "end_index": end_index,
                    "total_items": len(item_ids),
                    "item_ids": item_ids
                }, indent=2)
                
            except APIError as e:
                return json.dumps({
                    "status": "error",
                    "success": False,
                    "errors": [str(e)],
                    "criteria_list": criteria_list
                }, indent=2)
    
        @mcp.tool()
        def create_items(items: List[CatalogItem]) -> str:
            """
            Create items in Turbify Merchant Solutions catalog using the Catalog API.
            HTML included in the request values must be contained within CDATA tags.
    
            Args:
                items: List of item dictionaries to create. Each item must have:
                    - id (str): Unique item ID
                    - name (str): Item name
                    - price (float): Item price
                    - orderable (bool): Whether item is orderable
                    - taxable (bool): Whether item is taxable
                    - table_id (str): Table ID where item belongs
                    - custom_data (list, optional): List of {"name": str, "value": str} custom attributes
    
            Returns:
                JSON string with creation results including success status and any error messages
                """
            # Debugging: Check if items is None or contains None values
            if items is None:
                logger.error("create_items called with None items")
                return json.dumps({
                    "status": "error",
                    "success": False,
                    "errors": ["items parameter is None"],
                    "items_processed": 0
                }, indent=2)
            
            # Check for None values in the list
            none_items = [i for i in items if i is None]
            if none_items:
                logger.error(f"create_items called with None items in list: {none_items}")
                return json.dumps({
                    "status": "error",
                    "success": False,
                    "errors": [f"items list contains {len(none_items)} None values"],
                    "items_processed": 0
                }, indent=2)
            
            try:
                response = client.create_items(items)
                
                return json.dumps({
                    "status": response.status,
                    "success": response.is_success,
                    "messages": response.success_messages,
                    "errors": response.error_messages,
                    "items_processed": len(items)
                }, indent=2)
                
            except APIError as e:
                return json.dumps({
                    "status": "error",
                    "success": False,
                    "errors": [str(e)],
                    "items_processed": 0
                }, indent=2)
    
        @mcp.tool()
        def update_items(items: List[CatalogItem]) -> str:
            """
            Update items in Turbify Merchant Solutions catalog using the Catalog API. TableId and Id cannot be updated.
            HTML included in the request values must be contained within CDATA tags.
    
            Args:
                items: List of item dictionaries to update. Each item must have:
                    - id (str): Unique item ID, this cannot be updated
    
            Returns:
                JSON string with creation results including success status and any error messages
                """
            try:
                response = client.update_items(items)
                
                return json.dumps({
                    "status": response.status,
                    "success": response.is_success,
                    "messages": response.success_messages,
                    "errors": response.error_messages,
                    "items_processed": len(items)
                }, indent=2)
                
            except APIError as e:
                return json.dumps({
                    "status": "error",
                    "success": False,
                    "errors": [str(e)],
                    "items_processed": 0
                }, indent=2)
    
        @mcp.tool()
        def delete_items(item_ids: List[str]) -> str:
            """
            Delete multiple items in a single operation.
            
            Args:
                item_ids: List of item IDs to delete
            
            Returns:
                JSON string with operation result
            """
            try:
                response = client.delete_items(item_ids)
                
                return json.dumps({
                    "status": response.status,
                    "success": response.is_success,
                    "messages": response.success_messages,
                    "errors": response.error_messages,
                    "items_processed": len(item_ids)
                }, indent=2)
                
            except APIError as e:
                return json.dumps({
                    "status": "error",
                    "success": False,
                    "errors": [str(e)],
                    "items_processed": 0
                }, indent=2)

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/benpeke/turbify_store_mcp'

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