search_products
Find food products using search terms and filters for categories, brands, countries, nutrition grades, or processing levels to make informed choices.
Instructions
Search for food products with various filters and criteria
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| search | No | Search terms for product names, brands, or ingredients | |
| categories | No | Filter by categories (e.g., 'beverages', 'dairy') | |
| brands | No | Filter by brand names | |
| countries | No | Filter by countries (e.g., 'france', 'united-states') | |
| nutrition_grades | No | Filter by Nutri-Score grades (a, b, c, d, e) | |
| nova_groups | No | Filter by NOVA processing groups (1, 2, 3, 4) | |
| sort_by | No | Sort results by: popularity, product_name, created_datetime, last_modified_datetime | |
| page | No | Page number for pagination (default: 1) | |
| page_size | No | Number of results per page (default: 20, max: 100) |
Implementation Reference
- src/handlers.ts:34-61 (handler)Main handler function for search_products tool: calls client.searchProducts, handles empty results, formats summary and product list into text 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)Tool definition including name, description, and detailed input schema for search_products with optional parameters like search, categories, brands, etc.{ 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)Registration in the main server request handler switch statement: dispatches search_products calls to handlers.handleSearchProducts.case 'search_products': return await handlers.handleSearchProducts(args);
- src/client.ts:56-92 (helper)Client method that performs the actual HTTP search request to OpenFoodFacts API, builds query params, handles rate limiting, and parses 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; } }