shopify_analyze
Analyze any Shopify store to extract product listings, pricing distribution, top vendors, detected apps (Klaviyo, Yotpo, etc.), theme, and collections from a single URL.
Instructions
Analyze any Shopify store — products, pricing distribution, top vendors, detected apps (Klaviyo, Yotpo, etc.), theme, and collections.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | Shopify store URL, e.g. gymshark.com |
Implementation Reference
- src/mcp-stdio.ts:31-47 (registration)Registration of 'shopify_analyze' tool in ListToolsRequestSchema handler, defining its name, description, and inputSchema (url string).
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ { name: "shopify_analyze", description: "Analyze any Shopify store — products, pricing distribution, top vendors, detected apps (Klaviyo, Yotpo, etc.), theme, and collections.", inputSchema: { type: "object", properties: { url: { type: "string", description: "Shopify store URL, e.g. gymshark.com", }, }, required: ["url"], }, }, - src/mcp-stdio.ts:37-46 (schema)Input schema for shopify_analyze: object with required 'url' string property (Shopify store URL).
inputSchema: { type: "object", properties: { url: { type: "string", description: "Shopify store URL, e.g. gymshark.com", }, }, required: ["url"], }, - src/mcp-stdio.ts:177-181 (handler)Tool call handler case for 'shopify_analyze' — extracts 'url' from args and delegates to analyzeShopifyStore() imported from ./tools/shopify.js.
case "shopify_analyze": { const { url } = args as { url: string }; result = await analyzeShopifyStore(url); break; } - src/tools/shopify.ts:120-231 (handler)Core handler function analyzeShopifyStore() which fetches products JSON, collections JSON, and homepage HTML in parallel, computes pricing stats, top vendors, top product types, sale detection, detects apps (via HTML signatures), detects theme, and returns a ShopifyAnalysis object.
export async function analyzeShopifyStore( storeUrl: string ): Promise<ShopifyAnalysis> { const baseUrl = normalizeStoreUrl(storeUrl); const headers = { "User-Agent": "Mozilla/5.0 (compatible; IntelligenceAPI/1.0; +https://intelligence-api.io)", Accept: "application/json", }; // Fetch products, collections, and homepage in parallel const [productsRes, collectionsRes, homepageRes] = await Promise.allSettled([ fetch(`${baseUrl}/products.json?limit=250`, { headers }), fetch(`${baseUrl}/collections.json?limit=250`, { headers }), fetch(baseUrl, { headers: { ...headers, Accept: "text/html" }, }), ]); let products: ShopifyProduct[] = []; let collections: ShopifyCollection[] = []; let homepageHtml = ""; if (productsRes.status === "fulfilled" && productsRes.value.ok) { const json = (await productsRes.value.json()) as { products?: ShopifyProduct[]; }; products = json.products ?? []; } if (collectionsRes.status === "fulfilled" && collectionsRes.value.ok) { const json = (await collectionsRes.value.json()) as { collections?: ShopifyCollection[]; }; collections = json.collections ?? []; } if (homepageRes.status === "fulfilled" && homepageRes.value.ok) { homepageHtml = await homepageRes.value.text(); } // Compute pricing stats const allPrices: number[] = []; let saleCount = 0; for (const product of products) { for (const variant of product.variants) { const price = parseFloat(variant.price); if (!isNaN(price) && price > 0) allPrices.push(price); if (variant.compare_at_price && parseFloat(variant.compare_at_price) > price) { saleCount++; } } } const priceRange = { min: allPrices.length ? Math.min(...allPrices) : 0, max: allPrices.length ? Math.max(...allPrices) : 0, avg: allPrices.length ? Math.round((allPrices.reduce((a, b) => a + b, 0) / allPrices.length) * 100) / 100 : 0, }; // Vendor aggregation const vendorCounts: Record<string, number> = {}; for (const p of products) { if (p.vendor) vendorCounts[p.vendor] = (vendorCounts[p.vendor] ?? 0) + 1; } const topVendors = Object.entries(vendorCounts) .sort((a, b) => b[1] - a[1]) .slice(0, 10) .map(([vendor, count]) => ({ vendor, count })); // Product type aggregation const typeCounts: Record<string, number> = {}; for (const p of products) { if (p.product_type) typeCounts[p.product_type] = (typeCounts[p.product_type] ?? 0) + 1; } const topProductTypes = Object.entries(typeCounts) .sort((a, b) => b[1] - a[1]) .slice(0, 10) .map(([type, count]) => ({ type, count })); // Store name from homepage let storeName = baseUrl.replace(/^https?:\/\//, "").split(".")[0]; if (homepageHtml) { const $ = cheerio.load(homepageHtml); const titleEl = $("title").first().text().trim(); if (titleEl) storeName = titleEl.split(/[|\-–]/)[0].trim(); } return { store_url: baseUrl, store_name: storeName, product_count: products.length, collection_count: collections.length, price_range: priceRange, top_vendors: topVendors, top_product_types: topProductTypes, has_sale_items: saleCount > 0, sale_percentage: products.length > 0 ? Math.round((saleCount / products.length) * 100) : 0, detected_apps: homepageHtml ? detectApps(homepageHtml) : [], detected_theme: homepageHtml ? detectTheme(homepageHtml) : "Unknown", collections: collections.slice(0, 20), sample_products: products.slice(0, 10), analyzed_at: new Date().toISOString(), }; } - src/tools/shopify.ts:66-95 (helper)Helper function detectApps() and APP_SIGNATURES constant — scans homepage HTML for known Shopify app signatures (Klaviyo, Yotpo, Hotjar, Gorgias, etc.) to detect installed apps.
const APP_SIGNATURES: Record<string, string[]> = { "Klaviyo": ["klaviyo"], "Yotpo": ["yotpo"], "Privy": ["privy"], "Hotjar": ["hotjar"], "Gorgias": ["gorgias"], "ReCharge": ["recharge"], "Bold Commerce": ["boldcommerce", "boldapps"], "Loox": ["loox"], "Okendo": ["okendo"], "Smile.io": ["smile.io", "lion-apps"], "Sezzle": ["sezzle"], "Afterpay": ["afterpay"], "Klarna": ["klarna"], "Judge.me": ["judge.me"], "Stamped.io": ["stamped.io"], "Omnisend": ["omnisend"], "SMSBump": ["smsbump"], }; function detectApps(html: string): string[] { const detected: string[] = []; const lower = html.toLowerCase(); for (const [app, signatures] of Object.entries(APP_SIGNATURES)) { if (signatures.some((sig) => lower.includes(sig))) { detected.push(app); } } return detected; }