getProductPrices
Find the cheapest price for any product by retrieving crowd-sourced price data with its barcode.
Instructions
Get crowd-sourced price data for a specific product - see where it costs less
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| barcode | Yes | Product barcode to get prices for | |
| page | No | ||
| pageSize | No |
Implementation Reference
- src/tools/price-tools.ts:33-63 (handler)The core handler function that executes the tool logic. It fetches crowd-sourced price data from the Open Food Facts Prices API for a given product barcode, with pagination support.
async function getProductPrices(barcode: string, page: number, pageSize: number): Promise<PricesSearchResult> { const url = new URL(`${PRICES_URL}/prices`); url.searchParams.set('product_code', barcode); url.searchParams.set('page', page.toString()); url.searchParams.set('size', pageSize.toString()); url.searchParams.set('order_by', '-date'); const response = await fetch(url.toString()); if (!response.ok) { throw new Error(`Failed to get prices: ${response.status}`); } const data = await response.json(); const prices = (data.items || []).map((p: any): PriceResult => ({ productCode: p.product_code, price: p.price, currency: p.currency, locationName: p.location?.osm_display_name || 'Unknown location', locationId: p.location_id, date: p.date, proofId: p.proof_id })); return { prices, count: data.total || prices.length, page, pageSize }; } - src/tools/price-tools.ts:143-175 (registration)The registration of the 'getProductPrices' tool on the MCP server, including description, input schema, and the async handler callback that calls getProductPrices() and formats the response.
export function registerPriceTools(server: McpServer): void { server.registerTool('getProductPrices', { description: 'Get crowd-sourced price data for a specific product - see where it costs less', inputSchema: productPricesSchema }, async ({ barcode, page, pageSize }) => { try { const result = await getProductPrices(barcode, page ?? 1, pageSize ?? 20); if (result.prices.length === 0) { return { content: [{ type: 'text' as const, text: `No price data available for product ${barcode}. Price data is crowd-sourced and may not be available for all products.` }] }; } let message = `Found ${result.count} price records for product ${barcode}:\n\n`; result.prices.forEach((p, i) => { message += `${i + 1}. ${p.price} ${p.currency} at ${p.locationName}\n`; message += ` Date: ${p.date}\n\n`; }); return { content: [{ type: 'text' as const, text: message + `\n\nRaw data:\n${JSON.stringify(result, null, 2)}` }] }; } catch (error: any) { return { content: [{ type: 'text' as const, text: `Error: ${error.message}` }], isError: true }; } }); - src/tools/price-tools.ts:7-11 (schema)The input schema for getProductPrices using Zod validation: barcode (string), page (number with default 1), pageSize (number with default 20).
const productPricesSchema = { barcode: z.string().describe('Product barcode to get prices for'), page: z.number().default(1), pageSize: z.number().default(20) }; - src/tools/types.ts:112-128 (helper)Type definitions used by getProductPrices: PriceResult interface (productCode, price, currency, locationName, locationId, date, proofId) and PricesSearchResult interface (prices array, count, page, pageSize).
// Price types export interface PriceResult { productCode: string; price: number; currency: string; locationName: string; locationId: number; date: string; proofId: number; } export interface PricesSearchResult { prices: PriceResult[]; count: number; page: number; pageSize: number; } - src/tools/types.ts:130-135 (helper)The PRICES_URL constant ('https://prices.openfoodfacts.org/api/v1') used by getProductPrices to construct the API endpoint.
// Constants export const BASE_URL = 'https://world.openfoodfacts.org'; export const SEARCH_API_URL = 'https://search.openfoodfacts.org'; export const ROBOTOFF_URL = 'https://robotoff.openfoodfacts.org/api/v1'; export const PRICES_URL = 'https://prices.openfoodfacts.org/api/v1';