Skip to main content
Glama

sephora_get_product

Retrieve detailed Sephora product information including descriptions, ingredients, usage instructions, available variants, and customer reviews by providing the product URL.

Instructions

Get detailed information about a specific Sephora product including full description, ingredients, how-to-use instructions, available variants (shades/sizes), and customer reviews.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
product_urlYesFull Sephora product URL, e.g. https://www.sephora.com/product/...
include_reviewsNoWhether to include customer reviews (default: true)
max_reviewsNoMaximum number of reviews to return (1-10)

Implementation Reference

  • The main handler function that executes the sephora_get_product tool logic. It navigates to the product URL, waits for the page to load, and extracts comprehensive product information including name, brand, price, rating, description, ingredients, how-to-use instructions, images, variants (shades/sizes), categories, stock status, loyalty points, and optional customer reviews.
    export async function getProduct(input: GetProductInput): Promise<string> {
      const session = getSession();
      const page = await session.navigateTo(input.product_url);
    
      // Wait for product page to load
      try {
        await page.waitForSelector(
          '[data-comp="ProductSummary"], [class*="product-summary"], h1',
          { timeout: 15000 }
        );
      } catch {
        await page.waitForTimeout(3000);
      }
    
      const details = await page.evaluate(
        ({
          url,
          maxReviews,
          includeReviews,
        }: {
          url: string;
          maxReviews: number;
          includeReviews: boolean;
        }): ProductDetails => {
          // Product name
          const nameEl =
            document.querySelector('[data-at="product_name"]') ??
            document.querySelector('[class*="product-header"] h1') ??
            document.querySelector("h1");
    
          // Brand
          const brandEl =
            document.querySelector('[data-at="brand_name"]') ??
            document.querySelector('[class*="brand-name"]') ??
            document.querySelector('[class*="brandName"]');
    
          // Price
          const priceEl =
            document.querySelector('[data-at="price"]') ??
            document.querySelector('[class*="price-value"]') ??
            document.querySelector('[itemprop="price"]');
    
          // Rating
          const ratingEl = document.querySelector(
            '[data-at="ratings_count"], [aria-label*="stars"], [class*="star-rating"]'
          );
          const ratingText = ratingEl?.textContent?.trim() ?? "";
          const ratingMatch = ratingText.match(/(\d+\.?\d*)/);
    
          const reviewCountEl = document.querySelector(
            '[data-at="ratings_count_number"], [class*="review-count"]'
          );
          const reviewText = reviewCountEl?.textContent?.trim() ?? "";
          const reviewMatch = reviewText.match(/(\d[\d,]*)/);
    
          // Description
          const descEl =
            document.querySelector('[data-at="product_description"]') ??
            document.querySelector('[class*="description-content"]') ??
            document.querySelector('[itemprop="description"]');
    
          // Ingredients
          const ingredientsEl =
            document.querySelector('[data-at="ingredients"]') ??
            document.querySelector('[class*="ingredients"]');
    
          // How to use
          const howToUseEl =
            document.querySelector('[data-at="how_to_use"]') ??
            document.querySelector('[class*="how-to-use"]');
    
          // Images
          const images: string[] = [];
          const imgEls = document.querySelectorAll(
            '[data-comp="ProductImages"] img, [class*="product-image"] img'
          );
          imgEls.forEach((img) => {
            const src = (img as HTMLImageElement).src;
            if (src && !images.includes(src)) images.push(src);
          });
    
          // Variants (shades/sizes)
          const variants: ProductVariant[] = [];
          const variantEls = document.querySelectorAll(
            '[data-at="sku_item"], [class*="sku-item"], [class*="shade-selector"] button'
          );
          variantEls.forEach((el) => {
            const variantName =
              el.getAttribute("data-shade-name") ??
              el.getAttribute("aria-label") ??
              el.textContent?.trim() ??
              "";
            const variantId =
              el.getAttribute("data-sku-id") ??
              el.getAttribute("data-id") ??
              String(Math.random());
            const variantPrice =
              el.querySelector('[class*="price"]')?.textContent?.trim() ?? "";
            const hex =
              (el as HTMLElement).style?.backgroundColor ??
              el.getAttribute("data-hex") ??
              undefined;
            const inStock =
              !el.classList.contains("out-of-stock") &&
              !el.hasAttribute("disabled");
    
            variants.push({
              id: variantId,
              name: variantName,
              price: variantPrice,
              shade: el.getAttribute("data-shade-name") ?? undefined,
              hex: hex || undefined,
              inStock,
            });
          });
    
          // Breadcrumb categories
          const categories: string[] = [];
          document
            .querySelectorAll('[data-at="breadcrumb"] a, nav[aria-label="breadcrumb"] a')
            .forEach((el) => {
              const text = el.textContent?.trim();
              if (text) categories.push(text);
            });
    
          // Loyalty points
          const loyaltyEl = document.querySelector(
            '[data-at="loyalty_points"], [class*="loyalty-points"]'
          );
          const loyaltyText = loyaltyEl?.textContent?.trim() ?? "";
          const loyaltyMatch = loyaltyText.match(/(\d+)/);
    
          // Stock status
          const outOfStockEl = document.querySelector(
            '[data-at="out_of_stock"], [class*="out-of-stock"]'
          );
    
          // Product ID from URL
          const idMatch = url.match(/-([A-Z0-9]+)(?:\?|$)/);
          const productId = idMatch?.[1] ?? "unknown";
    
          // Reviews
          const reviews: Review[] = [];
          if (includeReviews) {
            const reviewEls = document.querySelectorAll(
              '[data-comp="Review"], [class*="review-item"], [class*="ReviewItem"]'
            );
            const limit = Math.min(reviewEls.length, maxReviews);
            for (let i = 0; i < limit; i++) {
              const rev = reviewEls[i];
              const authorEl = rev.querySelector(
                '[data-at="reviewer_name"], [class*="author"]'
              );
              const ratingEl = rev.querySelector('[aria-label*="stars"]');
              const titleEl = rev.querySelector(
                '[data-at="review_headline"], [class*="review-title"], h3'
              );
              const textEl = rev.querySelector(
                '[data-at="review_body"], [class*="review-text"], p'
              );
              const dateEl = rev.querySelector(
                '[data-at="review_date"], time, [class*="date"]'
              );
              const helpfulEl = rev.querySelector('[class*="helpful-count"]');
    
              const revRatingText = ratingEl?.getAttribute("aria-label") ?? "";
              const revRatingMatch = revRatingText.match(/(\d+\.?\d*)/);
    
              reviews.push({
                author: authorEl?.textContent?.trim() ?? "Anonymous",
                rating: revRatingMatch ? parseFloat(revRatingMatch[1]) : 0,
                title: titleEl?.textContent?.trim() ?? "",
                text: textEl?.textContent?.trim() ?? "",
                date: dateEl?.textContent?.trim() ?? "",
                helpful: helpfulEl
                  ? parseInt(helpfulEl.textContent?.replace(/\D/g, "") ?? "0", 10)
                  : undefined,
              });
            }
          }
    
          return {
            id: productId,
            name: nameEl?.textContent?.trim() ?? "Unknown Product",
            brand: brandEl?.textContent?.trim() ?? "Unknown Brand",
            price: priceEl?.textContent?.trim() ?? "Price unavailable",
            rating: ratingMatch ? parseFloat(ratingMatch[1]) : undefined,
            reviewCount: reviewMatch
              ? parseInt(reviewMatch[1].replace(/,/g, ""), 10)
              : undefined,
            description: descEl?.textContent?.trim() ?? "",
            ingredients: ingredientsEl?.textContent?.trim(),
            howToUse: howToUseEl?.textContent?.trim(),
            variants: variants.slice(0, 20),
            images: images.slice(0, 5),
            categories,
            isAvailable: !outOfStockEl,
            loyaltyPoints: loyaltyMatch ? parseInt(loyaltyMatch[1], 10) : undefined,
            reviews: includeReviews ? reviews : undefined,
            url,
          };
        },
        {
          url: input.product_url,
          maxReviews: input.max_reviews ?? 5,
          includeReviews: input.include_reviews ?? true,
        }
      );
    
      return JSON.stringify({ success: true, product: details });
    }
  • Input validation schema using Zod that defines the parameters for the sephora_get_product tool: product_url (required URL), include_reviews (optional boolean, default true), and max_reviews (optional number 1-10, default 5).
    export const getProductSchema = z.object({
      product_url: z
        .string()
        .url()
        .describe("Full Sephora product URL, e.g. https://www.sephora.com/product/..."),
      include_reviews: z
        .boolean()
        .optional()
        .default(true)
        .describe("Whether to include customer reviews (default: true)"),
      max_reviews: z
        .number()
        .int()
        .min(1)
        .max(10)
        .optional()
        .default(5)
        .describe("Maximum number of reviews to return (1-10)"),
    });
  • src/index.ts:109-113 (registration)
    Tool registration in the TOOLS array that defines the MCP tool with name 'sephora_get_product', description, and input schema converted from Zod to JSON schema format.
      name: "sephora_get_product",
      description:
        "Get detailed information about a specific Sephora product including full description, ingredients, how-to-use instructions, available variants (shades/sizes), and customer reviews.",
      inputSchema: zodToJsonSchema(getProductSchema) as Tool["inputSchema"],
    },
  • Tool dispatcher handler that validates input arguments using getProductSchema.parse() and calls the getProduct function with the validated input.
    sephora_get_product: async (args) => {
      const input = getProductSchema.parse(args);
      return getProduct(input);
    },

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/markswendsen-code/mcp-sephora'

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