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
| Name | Required | Description | Default |
|---|---|---|---|
| product_url | Yes | Full Sephora product URL, e.g. https://www.sephora.com/product/... | |
| include_reviews | No | Whether to include customer reviews (default: true) | |
| max_reviews | No | Maximum number of reviews to return (1-10) |
Implementation Reference
- src/tools/getProduct.ts:68-278 (handler)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 }); } - src/tools/getProduct.ts:4-22 (schema)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"], }, - src/index.ts:151-154 (handler)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); },