search_products
Find food products using customizable filters like categories, brands, countries, or nutrition grades. Sort results by popularity, name, or date to access detailed product information efficiently.
Instructions
Search for food products with various filters and criteria
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| brands | No | Filter by brand names | |
| categories | No | Filter by categories (e.g., 'beverages', 'dairy') | |
| countries | No | Filter by countries (e.g., 'france', 'united-states') | |
| nova_groups | No | Filter by NOVA processing groups (1, 2, 3, 4) | |
| nutrition_grades | No | Filter by Nutri-Score grades (a, b, c, d, e) | |
| page | No | Page number for pagination (default: 1) | |
| page_size | No | Number of results per page (default: 20, max: 100) | |
| search | No | Search terms for product names, brands, or ingredients | |
| sort_by | No | Sort results by: popularity, product_name, created_datetime, last_modified_datetime |
Implementation Reference
- src/handlers.ts:34-61 (handler)The primary handler function for the 'search_products' tool. It invokes the client's searchProducts method, handles empty results, formats the search summary and product list, and returns the MCP-formatted response.async handleSearchProducts(params: any) { const response = await this.client.searchProducts(params); if (response.products.length === 0) { return { content: [ { type: "text" as const, text: "No products found matching your search criteria.", }, ], }; } const summary = `Found ${response.count} products (showing page ${response.page} of ${response.page_count}):\n\n`; const productList = response.products .map((product, index) => `${index + 1}. ${this.formatProductSummary(product)}`) .join('\n\n'); return { content: [ { type: "text" as const, text: summary + productList, }, ], }; }
- src/tools.ts:18-67 (schema)The tool schema definition including name, description, and detailed inputSchema with all parameters for search_products.{ name: "search_products", description: "Search for food products with various filters and criteria", inputSchema: { type: "object", properties: { search: { type: "string", description: "Search terms for product names, brands, or ingredients", }, categories: { type: "string", description: "Filter by categories (e.g., 'beverages', 'dairy')", }, brands: { type: "string", description: "Filter by brand names", }, countries: { type: "string", description: "Filter by countries (e.g., 'france', 'united-states')", }, nutrition_grades: { type: "string", description: "Filter by Nutri-Score grades (a, b, c, d, e)", }, nova_groups: { type: "string", description: "Filter by NOVA processing groups (1, 2, 3, 4)", }, sort_by: { type: "string", description: "Sort results by: popularity, product_name, created_datetime, last_modified_datetime", enum: ["popularity", "product_name", "created_datetime", "last_modified_datetime"], }, page: { type: "number", description: "Page number for pagination (default: 1)", minimum: 1, }, page_size: { type: "number", description: "Number of results per page (default: 20, max: 100)", minimum: 1, maximum: 100, }, }, required: [], }, },
- src/index.ts:48-49 (registration)The registration/dispatch case in the server's CallToolRequestHandler switch statement that routes 'search_products' calls to the handler.case 'search_products': return await handlers.handleSearchProducts(args);
- src/client.ts:56-92 (helper)Core helper method in the OpenFoodFactsClient that performs the actual HTTP search request to the OpenFoodFacts API, applies rate limiting, constructs query parameters, and parses the response.async searchProducts(params: { search?: string; categories?: string; brands?: string; countries?: string; page?: number; page_size?: number; sort_by?: string; nutrition_grades?: string; nova_groups?: string; } = {}): Promise<SearchResponse> { await this.checkRateLimit('search'); const searchParams = new URLSearchParams(); if (params.search) searchParams.append('search_terms', params.search); if (params.categories) searchParams.append('categories_tags', params.categories); if (params.brands) searchParams.append('brands_tags', params.brands); if (params.countries) searchParams.append('countries_tags', params.countries); if (params.nutrition_grades) searchParams.append('nutrition_grades_tags', params.nutrition_grades); if (params.nova_groups) searchParams.append('nova_groups_tags', params.nova_groups); if (params.sort_by) searchParams.append('sort_by', params.sort_by); searchParams.append('page', String(params.page || 1)); searchParams.append('page_size', String(Math.min(params.page_size || 20, 100))); searchParams.append('json', '1'); try { const response = await this.client.get(`/cgi/search.pl?${searchParams.toString()}`); return SearchResponseSchema.parse(response.data); } catch (error) { if (axios.isAxiosError(error)) { throw new Error(`Search failed: ${error.response?.status} ${error.message}`); } throw error; } }