Seed Demo Store
store_demo_seedGenerate a demo store with 20 products, 40 customers, and 150+ orders for testing ShopOps tools without real credentials. Returns a store ID for use with inventory, customer segmentation, and order analysis tools.
Instructions
Create a realistic demo store populated with 20 products, 40 customers across 6 archetype buckets (champions, loyal, new, at-risk, hibernating, one-off), and 150+ orders spanning the last 6 months. Use this to explore ShopOps without real Shopify or WooCommerce credentials — every tool (inventory_status, customers_segment, order_anomalies, report_weekly, etc.) will return meaningful output on the returned store_id. Safe to call multiple times; each call creates a new demo store with a unique ID. Returns the store_id plus product/customer/order counts.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- src/index.ts:108-125 (registration)Tool registration for 'store_demo_seed' using server.registerTool. Defines metadata (title, description, empty inputSchema, annotations) and an async handler that calls seedDemoStore() and returns the result as JSON.
// ── Tool: store_demo_seed ───────────────────────────────────────── server.registerTool( 'store_demo_seed', { title: 'Seed Demo Store', description: 'Create a realistic demo store populated with 20 products, 40 customers across 6 archetype buckets (champions, loyal, new, at-risk, hibernating, one-off), and 150+ orders spanning the last 6 months. Use this to explore ShopOps without real Shopify or WooCommerce credentials — every tool (inventory_status, customers_segment, order_anomalies, report_weekly, etc.) will return meaningful output on the returned store_id. Safe to call multiple times; each call creates a new demo store with a unique ID. Returns the store_id plus product/customer/order counts.', inputSchema: z.object({}), annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, }, async () => { try { const result = await seedDemoStore(); return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }], }; } catch (e) { return handleToolError(e); } } ); - src/index.ts:117-124 (handler)Inline async handler for the store_demo_seed tool. Calls seedDemoStore() imported from './services/demo-seed.js', wraps the result in a 'text' content response, and catches errors via handleToolError.
async () => { try { const result = await seedDemoStore(); return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }], }; } catch (e) { return handleToolError(e); } } - src/index.ts:112-114 (schema)Input/output schema for store_demo_seed. Uses z.object({}) (empty object, no inputs). The output type is inferred from the DemoSeedResult interface returned by seedDemoStore.
title: 'Seed Demo Store', description: 'Create a realistic demo store populated with 20 products, 40 customers across 6 archetype buckets (champions, loyal, new, at-risk, hibernating, one-off), and 150+ orders spanning the last 6 months. Use this to explore ShopOps without real Shopify or WooCommerce credentials — every tool (inventory_status, customers_segment, order_anomalies, report_weekly, etc.) will return meaningful output on the returned store_id. Safe to call multiple times; each call creates a new demo store with a unique ID. Returns the store_id plus product/customer/order counts.', inputSchema: z.object({}), - src/services/demo-seed.ts:128-135 (schema)DemoSeedResult interface defining the return type: store_id, store_name, products, customers, orders, and message.
export interface DemoSeedResult { store_id: string; store_name: string; products: number; customers: number; orders: number; message: string; } - src/services/demo-seed.ts:144-300 (helper)seedDemoStore() — the core implementation that creates a demo store with 20 products (from PRODUCT_TEMPLATES), 40 customers across 6 RFM archetypes, and 150+ orders spanning the last 6 months. Generates deterministic-ish random data, persists everything via the Storage interface, and returns a DemoSeedResult.
export async function seedDemoStore(store?: Storage): Promise<DemoSeedResult> { const s = store ?? defaultStorage; const now = new Date(); const storeId = uuidv4(); // 1. Create the store record const storeRecord: StoreConfig = { id: storeId, name: 'Acme Demo Store', platform: 'shopify', url: 'https://acme-demo.myshopify.com', api_key: 'demo_token_not_real', api_secret: undefined, connected_at: now.toISOString(), last_sync_at: now.toISOString(), product_count: PRODUCT_TEMPLATES.length, order_count: 0, // filled in below customer_count: 0, // filled in below }; // 2. Create products const products: Product[] = PRODUCT_TEMPLATES.map((t, i) => ({ id: `${storeId}_p_${i + 1}`, store_id: storeId, external_id: `demo-sku-${i + 1}`, title: t.title, sku: `ACME-${String(i + 1).padStart(4, '0')}`, price: t.price, compare_at_price: t.price > 80 ? Math.round(t.price * 1.25 * 100) / 100 : null, cost_price: t.cost, currency: 'USD', inventory_quantity: t.stock, category: t.category, status: 'active', created_at: new Date(now.getTime() - 180 * 86_400_000).toISOString(), updated_at: now.toISOString(), })); // 3. Create customers + orders by archetype const customers: Customer[] = []; const orders: Order[] = []; let customerIdx = 0; let orderCounter = 1000; for (const { archetype, count } of ARCHETYPE_MIX) { const params = archetypeParams(archetype); for (let k = 0; k < count; k++) { const seedBase = customerIdx * 101 + k * 17; const name = pick(CUSTOMER_NAMES, customerIdx); const email = name.toLowerCase().replace(/\W+/g, '.') + '@example.test'; const country = pick(COUNTRIES, customerIdx); const city = pick(CITIES[country], customerIdx + 1); const orderCount = randInt(params.orderCount[0], params.orderCount[1], seedBase + 1); const avgSpend = rand(params.avgSpend[0], params.avgSpend[1], seedBase + 2); const firstDaysAgo = randInt(params.daysAgo[0], params.daysAgo[1] + 30, seedBase + 3); const lastDaysAgo = randInt(params.daysAgo[0], params.daysAgo[1], seedBase + 4); const customer: Customer = { id: `${storeId}_c_${customerIdx + 1}`, store_id: storeId, external_id: `demo-cust-${customerIdx + 1}`, email, name, total_orders: orderCount, total_spent: 0, // filled in below first_order_at: new Date(now.getTime() - firstDaysAgo * 86_400_000).toISOString(), last_order_at: new Date(now.getTime() - lastDaysAgo * 86_400_000).toISOString(), avg_order_value: 0, // filled in below country, city, tags: archetype === 'champion' ? ['vip'] : [], created_at: new Date(now.getTime() - (firstDaysAgo + 15) * 86_400_000).toISOString(), }; customers.push(customer); // Generate this customer's orders let customerTotal = 0; for (let o = 0; o < orderCount; o++) { const product = pickWeighted(PRODUCT_TEMPLATES.map((p, idx) => ({ ...p, idx })), seedBase + o * 3 + 5); const quantity = randInt(1, 3, seedBase + o * 3 + 6); const unitPrice = product.price; const lineTotal = unitPrice * quantity; const tax = Math.round(lineTotal * 0.08 * 100) / 100; const shipping = lineTotal > 100 ? 0 : 8.95; const total = Math.round((lineTotal + tax + shipping) * 100) / 100; customerTotal += total; // Spread orders from first to last let daysAgo: number; if (orderCount === 1) { daysAgo = lastDaysAgo; } else { const ratio = o / (orderCount - 1); daysAgo = Math.round(firstDaysAgo - (firstDaysAgo - lastDaysAgo) * ratio); } const orderTs = new Date(now.getTime() - daysAgo * 86_400_000); orderCounter++; const item: OrderItem = { product_id: `${storeId}_p_${product.idx + 1}`, title: product.title, sku: `ACME-${String(product.idx + 1).padStart(4, '0')}`, quantity, unit_price: unitPrice, total: Math.round(lineTotal * 100) / 100, }; orders.push({ id: `${storeId}_o_${orderCounter}`, store_id: storeId, external_id: `demo-order-${orderCounter}`, order_number: String(orderCounter), customer_id: customer.id, customer_email: customer.email, status: daysAgo < 3 ? 'processing' : daysAgo < 14 ? 'shipped' : 'delivered', subtotal: Math.round(lineTotal * 100) / 100, tax_total: tax, shipping_total: shipping, discount_total: 0, total, currency: 'USD', items: [item], shipping_country: country, shipping_city: city, payment_method: 'card', ip_address: null, created_at: orderTs.toISOString(), updated_at: orderTs.toISOString(), }); } customer.total_spent = Math.round(customerTotal * 100) / 100; customer.avg_order_value = orderCount > 0 ? Math.round((customerTotal / orderCount) * 100) / 100 : 0; customerIdx++; } } // 4. Persist everything storeRecord.order_count = orders.length; storeRecord.customer_count = customers.length; await s.addStore(storeRecord); await s.upsertProducts(products); await s.upsertOrders(orders); await s.upsertCustomers(customers); return { store_id: storeId, store_name: storeRecord.name, products: products.length, customers: customers.length, orders: orders.length, message: `Demo store "${storeRecord.name}" seeded with ${products.length} products, ${customers.length} customers, and ${orders.length} orders. Use this store_id with any ShopOps tool to explore the product without real Shopify or WooCommerce credentials.`, }; }