Skip to main content
Glama

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

TableJSON Schema
NameRequiredDescriptionDefault
urlYesShopify 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"],
          },
        },
  • 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"],
    },
  • 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;
    }
  • 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(),
      };
    }
  • 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;
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided; description only implies analysis without stating read-only nature, data freshness, rate limits, or side effects. The term 'analyze' suggests non-destructive but is not explicit.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Single sentence with clear enumeration of outputs. No unnecessary words; front-loaded with action and scope.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Lists key outputs (products, pricing, etc.) compensating for lack of output schema. However, missing behavioral details (e.g., read-only, prerequisites) given no annotations.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Parameter 'url' has adequate schema description with example. Schema coverage is 100%, so description adds no extra meaning beyond schema. Baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description clearly states the tool analyzes a Shopify store with specific outputs (products, pricing, vendors, apps, theme, collections). However, it does not distinguish from sibling tool 'shopify_products' which may have overlapping purpose.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance on when to use this tool versus alternatives like 'shopify_products'. The description only says 'Analyze any Shopify store', lacking usage context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/samrothschild23/intelligence-api'

If you have feedback or need assistance with the MCP directory API, please join our Discord server