Pricing Analysis
pricing_analyzeAnalyze pricing across products using margin calculation and sales velocity to generate price optimization suggestions. Returns per-product data including current price, cost, margin, daily units sold, revenue, and suggested price.
Instructions
Analyze pricing across products with margin calculation, sales velocity, and rule-based price optimization suggestions. Returns an array where each element contains product_title, current_price, cost, margin_percent, daily_units_sold, revenue_per_day, suggested_price (or null if no change recommended), and suggestion_reason. Pass product_id to scope to a single product, omit for full catalog.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| store_id | Yes | UUID of a connected store (returned by store_connect with action="connect" or visible in store_connect with action="list" / the store_overview resource) | |
| product_id | No | Restrict the analysis to a single product by external_id. Omit to analyse the entire active catalog. |
Implementation Reference
- src/tools/pricing.ts:7-94 (handler)The actual handler function 'analyzePricing' that executes pricing analysis logic. It fetches products/orders from storage, calculates margins, sales velocity, discounts, and applies rule-based logic to suggest price changes (or none). Returns sorted array of PricingAnalysis objects.
export async function analyzePricing(storeId: string, productId?: string): Promise<PricingAnalysis[]> { validateUUID(storeId, 'store'); const store = await storage.getStoreById(storeId); if (!store) throw new NotFoundError('Store', storeId); const products = await storage.getProducts(storeId); const orders = await storage.getOrders(storeId); const targetProducts = productId ? products.filter((p) => p.id === productId) : products.filter((p) => p.status === 'active'); if (productId && targetProducts.length === 0) throw new NotFoundError('Product', productId); const now = Date.now(); const thirtyDaysAgo = now - 30 * MS_PER_DAY; const recentOrders = orders.filter((o) => new Date(o.created_at).getTime() >= thirtyDaysAgo && o.status !== 'cancelled' && o.status !== 'refunded' ); return targetProducts.map((product) => { // Count units sold and revenue in last 30 days let unitsSold = 0; let revenue = 0; for (const order of recentOrders) { for (const item of order.items) { if (item.product_id === product.id) { unitsSold += item.quantity; revenue += item.total; } } } const avgUnitsPerDay = unitsSold / 30; const revenuePerDay = revenue / 30; // Margin calculation const marginPercent = product.cost_price !== null && product.cost_price > 0 ? Math.round(((product.price - product.cost_price) / product.price) * 10000) / 100 : null; // Discount from compare_at_price const discountPercent = product.compare_at_price !== null && product.compare_at_price > product.price ? Math.round(((product.compare_at_price - product.price) / product.compare_at_price) * 10000) / 100 : null; // Price elasticity hint and suggestion let elasticityHint: string; let suggestedPrice: number | null = null; let suggestionReason: string; if (unitsSold === 0) { elasticityHint = 'No sales data — consider lowering price to attract buyers or improving visibility'; suggestedPrice = product.price > 0 ? Math.round(product.price * 0.85 * 100) / 100 : null; suggestionReason = 'No sales in 30 days. A 15% price reduction may increase conversion.'; } else if (avgUnitsPerDay > 5 && marginPercent !== null && marginPercent < 20) { elasticityHint = 'High volume but low margin — demand is strong, consider gradual price increase'; suggestedPrice = Math.round(product.price * 1.10 * 100) / 100; suggestionReason = 'Strong demand with low margin. A 10% increase likely sustainable.'; } else if (avgUnitsPerDay > 3 && (marginPercent === null || marginPercent >= 40)) { elasticityHint = 'Good volume and healthy margin — pricing appears optimal'; suggestionReason = 'Current pricing is well-balanced. No change recommended.'; } else if (avgUnitsPerDay < 0.5 && (marginPercent === null || marginPercent > 50)) { elasticityHint = 'Low volume with high margin — price may be too high for demand'; suggestedPrice = Math.round(product.price * 0.90 * 100) / 100; suggestionReason = 'Low sales volume despite high margin. Consider a 10% price reduction.'; } else { elasticityHint = 'Moderate performance — test small price changes and measure impact'; suggestionReason = 'No strong signal. A/B test with 5% variation to find optimal price point.'; } return { product_id: product.id, product_title: product.title, current_price: product.price, cost_price: product.cost_price, margin_percent: marginPercent, compare_at_price: product.compare_at_price, discount_percent: discountPercent, avg_units_per_day: Math.round(avgUnitsPerDay * 100) / 100, revenue_per_day: Math.round(revenuePerDay * 100) / 100, price_elasticity_hint: elasticityHint, suggested_price: suggestedPrice, suggestion_reason: suggestionReason, }; }).sort((a, b) => b.revenue_per_day - a.revenue_per_day); } - src/models/store.ts:149-163 (schema)Zod schema and TypeScript type 'PricingAnalysis' defining the return shape: product_id, product_title, current_price, cost_price, margin_percent, avg_units_per_day, revenue_per_day, suggested_price, suggestion_reason, etc.
export const PricingAnalysisSchema = z.object({ product_id: z.string(), product_title: z.string(), current_price: z.number(), cost_price: z.number().nullable(), margin_percent: z.number().nullable(), compare_at_price: z.number().nullable(), discount_percent: z.number().nullable(), avg_units_per_day: z.number(), revenue_per_day: z.number(), price_elasticity_hint: z.string(), suggested_price: z.number().nullable(), suggestion_reason: z.string(), }); export type PricingAnalysis = z.infer<typeof PricingAnalysisSchema>; - src/index.ts:168-186 (registration)Registration of the 'pricing_analyze' tool via server.registerTool, with inputSchema (store_id UUID, optional product_id), description, and async handler that calls analyzePricing.
// ── Tool: pricing_analyze ───────────────────────────────────────── server.registerTool( 'pricing_analyze', { title: 'Pricing Analysis', description: 'Analyze pricing across products with margin calculation, sales velocity, and rule-based price optimization suggestions. Returns an array where each element contains product_title, current_price, cost, margin_percent, daily_units_sold, revenue_per_day, suggested_price (or null if no change recommended), and suggestion_reason. Pass product_id to scope to a single product, omit for full catalog.', inputSchema: z.object({ store_id: z.string().uuid().describe('UUID of a connected store (returned by store_connect with action="connect" or visible in store_connect with action="list" / the store_overview resource)'), product_id: z.string().optional().describe('Restrict the analysis to a single product by external_id. Omit to analyse the entire active catalog.'), }), annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }, }, async ({ store_id, product_id }) => { try { const result = await analyzePricing(store_id, product_id); return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] }; } catch (e) { return handleToolError(e); } } );