Shopify and MCP
MCP Example Shopify Store
Integration
Using JSONata Response for the transformation
Shopifiy returns products vs items, use the JSONata Response to transform the output.
{
"items": products
}
Example Shopify Store using a MCP endpoint
Handlebars Template
The carousel-card class is the main container for each product card in the carousel. data-index is used to track the index of each card in the carousel. The card-button class must be used for all the buttons in the card to listen the click events.
{{#each items}}
<div
class="carousel-card bg-white rounded-lg shadow border p-4 mb-6"
data-index="{{@index}}"
data-item="{{{json this}}}"
>
<!-- Image and color selection -->
<div class="flex flex-col items-center">
<img
id="main-image-{{@index}}"
src="{{variants.[0].image_url}}"
alt="{{title}}"
class="object-cover w-44 h-44 mb-3 rounded"
style="background:#eee;"
/>
<!-- Color dots; selection handled in JS -->
<div class="flex flex-row space-x-2 mt-1 mb-2 color-dot-group">
{{#each (uniqueColors variants) as |color colorIndex|}}
<button
type="button"
class="color-btn w-7 h-7 rounded-full border-2 border-gray-300"
style="background:#ddd;"
aria-label="{{color}}"
onclick="selectColorByName(this, '{{color}}')"
data-color="{{color}}"
title="{{color}}"
></button>
{{/each}}
</div>
</div>
<!-- Product details -->
<div class="flex flex-col px-2 mt-2">
<h2 class="text-lg font-bold mb-1">{{title}}</h2>
<p class="font-semibold text-gray-600 mb-1">USD ${{price_range.min}}</p>
<p class="mb-2 text-gray-800 text-sm">{{description}}</p>
<label class="font-semibold text-xs">Select Size:</label>
<div class="flex space-x-4 mt-2 mb-2">
{{#each (uniqueSizes variants) as |size|}}
<button
class="size-btn px-6 py-2 border border-gray-300 rounded-md text-lg font-medium"
data-size="{{size}}"
onclick="selectSize(this, '{{size}}')"
type="button"
>
{{size}}
</button>
{{/each}}
</div>
<button
class="card-button list-btn uppercase flex-grow hover:bg-gradient-to-t md:flex-none ml-2 mt-2 bg-black text-white px-3 py-1 rounded text-sm"
onclick="addToCart(this)"
>
Add to Cart
</button>
</div>
</div>
{{/each}}
JavaScript
// === ai12z-carousel-variants.js ===
// --- Robust Color/Size Extraction ---
function extractColorAndSize(title, productType) {
const parts = title.split("/").map((p) => p.trim())
let color, size
if (parts.length === 2) {
// e.g., "Black / S" -> color: Black, size: S
color = parts[0]
size = parts[1]
} else if (parts.length >= 3) {
// e.g., "All-mountain skiing / Black / 167" -> color: Black, size: 167
color = parts[1]
size = parts[2]
} else if (parts.length === 1) {
color = parts[0]
size = ""
} else {
color = ""
size = ""
}
return { color, size }
}
window.extractColorAndSize = extractColorAndSize // for Handlebars helpers
// --- Handlebars Helpers ---
window.Handlebars.registerHelper("uniqueColors", function (variants) {
const colors = []
const contextProductType = (this && this.product_type) || ""
variants.forEach((v) => {
const productType = v.product_type || contextProductType
const { color } = extractColorAndSize(v.title, productType)
if (color && !colors.includes(color)) colors.push(color)
})
return colors
})
window.Handlebars.registerHelper("uniqueSizes", function (variants) {
const sizes = []
const contextProductType = (this && this.product_type) || ""
variants.forEach((v) => {
const productType = v.product_type || contextProductType
const { size } = extractColorAndSize(v.title, productType)
if (size && !sizes.includes(size)) sizes.push(size)
})
return sizes
})
// --- Deep Shadow DOM Query Utility ---
function findAllInDeepShadow(selector, root = document) {
let results = Array.from(root.querySelectorAll(selector))
Array.from(root.querySelectorAll("*")).forEach((el) => {
if (el.shadowRoot) {
results = results.concat(findAllInDeepShadow(selector, el.shadowRoot))
}
})
return results
}
// --- Apply Color Swatches ---
function applyColorSwatches() {
const colorMap = {
Black: "#222",
Slate: "#a9b3ba",
Sunrise: "#faa61a",
Ironwood: "#ae5249",
Chalk: "#eaeaea",
Red: "#b30000",
Purple: "#b185db",
Gold: "#ecc94b",
}
findAllInDeepShadow(".carousel-card").forEach((card) => {
card.querySelectorAll(".color-btn").forEach((btn) => {
const colorText = btn.title || btn.getAttribute("aria-label") || ""
let colorHex = "#ddd"
Object.keys(colorMap).forEach((key) => {
if (colorText.includes(key)) colorHex = colorMap[key]
})
btn.style.background = colorHex
})
})
}
// --- Color Selection Logic ---
function selectColorByName(btn, color) {
const card = btn.closest(".carousel-card")
if (!card) return
const data = JSON.parse(card.getAttribute("data-item"))
card.setAttribute("data-selected-color", color)
// Update main image to first variant of this color
const variant = data.variants.find((v) => {
const { color: vColor } = extractColorAndSize(v.title, data.product_type)
return vColor === color
})
const img = card.querySelector('img[id^="main-image"]')
if (img && variant?.image_url) img.src = variant.image_url
updateSizeButtons(card, color, data.variants, data.product_type)
// Style color dots (selected, extra-circle if 2 colors)
const colors = [
...new Set(
data.variants.map(
(v) => extractColorAndSize(v.title, data.product_type).color
)
),
]
card.querySelectorAll(".color-btn").forEach((dot) => {
dot.classList.toggle(
"selected-color",
dot.getAttribute("data-color") === color
)
dot.classList.toggle(
"extra-circle",
dot.getAttribute("data-color") === color && colors.length === 2
)
})
}
Note:: root = carousel?.shadowRoot; This is how we reference the shadowRoot, if carousel
Ecom CSS
.color-btn.selected-color {
border: 2.5px solid #2D7BE5;
}
.color-btn.extra-circle {
box-shadow: 0 0 0 4px #f3f4f6, 0 0 0 7px #2D7BE5;
}
.size-btn.unavailable {
position: relative;
pointer-events: none;
}
.size-btn.unavailable .slash {
position: absolute;
left: 12px; top: 50%;
width: 36px; height: 2px;
background: #999;
transform: rotate(-24deg) translateY(-50%);
content: "";
display: inline-block;
z-index: 2;
border-radius: 2px;
}
JSON from endpoint, simplified...
{
"items": [
{
"description": "Don't leave home without the Smith Level Mips snow helmet. It's built with a hybrid shell construction and Mips technology, which helps reduce rotational forces if the helmet gets hit at an angle.",
"image_url": "https://cdn.shopify.com/s/files/1/0920/9112/1967/files/1.png?v=1749094711",
"price_range": {
"currency": "USD",
"max": "225.0",
"min": "225.0"
},
"product_id": "gid://shopify/Product/10607997387055",
"product_type": "Helmet",
"tags": ["Men"],
"title": "Men's Level Mips Snow Helmet",
"variants": [
{
"available": true,
"currency": "USD",
"image_url": "https://cdn.shopify.com/s/files/1/0920/9112/1967/files/1.png?v=1749094711",
"price": "225.0",
"title": "Black / S",
"variant_id": "gid://shopify/ProductVariant/51390165909807"
},
{
"available": true,
"currency": "USD",
"image_url": "https://cdn.shopify.com/s/files/1/0920/9112/1967/files/1.png?v=1749094711",
"price": "225.0",
"title": "Black / M",
"variant_id": "gid://shopify/ProductVariant/51390165975343"
},
{
"available": true,
"currency": "USD",
"image_url": "https://cdn.shopify.com/s/files/1/0920/9112/1967/files/1.png?v=1749094711",
"price": "225.0",
"title": "Black / L",
"variant_id": "gid://shopify/ProductVariant/51390166040879"
},
{
"available": true,
"currency": "USD",
"image_url": "https://cdn.shopify.com/s/files/1/0920/9112/1967/files/1.png?v=1749094711",
"price": "225.0",
"title": "Black / XL",
"variant_id": "gid://shopify/ProductVariant/51390166106415"
},
{
"available": true,
"currency": "USD",
"image_url": "https://cdn.shopify.com/s/files/1/0920/9112/1967/files/E0062800TB00_P00_ec62e1a4-e582-443f-80d2-8abe8f76add4.webp?v=1750297479",
"price": "225.0",
"title": "Matte Slate / S",
"variant_id": "gid://shopify/ProductVariant/51390165942575"
},
{
"available": true,
"currency": "USD",
"image_url": "https://cdn.shopify.com/s/files/1/0920/9112/1967/files/E0062800TB00_P00_ec62e1a4-e582-443f-80d2-8abe8f76add4.webp?v=1750297479",
"price": "225.0",
"title": "Matte Slate / M",
"variant_id": "gid://shopify/ProductVariant/51390166008111"
},
{
"available": true,
"currency": "USD",
"image_url": "https://cdn.shopify.com/s/files/1/0920/9112/1967/files/E0062800TB00_P00_ec62e1a4-e582-443f-80d2-8abe8f76add4.webp?v=1750297479",
"price": "225.0",
"title": "Matte Slate / L",
"variant_id": "gid://shopify/ProductVariant/51390166073647"
},
{
"available": true,
"currency": "USD",
"image_url": "https://cdn.shopify.com/s/files/1/0920/9112/1967/files/E0062800TB00_P00_ec62e1a4-e582-443f-80d2-8abe8f76add4.webp?v=1750297479",
"price": "225.0",
"title": "Matte Slate / XL",
"variant_id": "gid://shopify/ProductVariant/51390166139183"
}
]
},
{
"description": "Like a good day on the mountain, the Smith Level ski and snowboard helmet is at your service for ranging far and wide. It brings the added energy absorption of Zonal KOROYD® and the advanced angled impact protection of Mips® to let you focus on ripping high-speed arcs in the alpine and chasing untracked lines in the trees. The hybrid shell design adds durability without weighing you down for a helmet that you will actually take with you when the backcountry beckons. And because you love to ski all day, the fit and vents are adjustable on the fly, so you never need to slow down.",
"image_url": "https://cdn.shopify.com/s/files/1/0920/9112/1967/files/level-mips-helmet_matteIronwood_3Q.png?v=1749177564",
"price_range": {
"currency": "USD",
"max": "225.0",
"min": "195.0"
},
"product_id": "gid://shopify/Product/10615507124527",
"product_type": "Helmet",
"tags": ["Men"],
"title": "Men's Smith Level Snow Helmet",
"variants": [
{
"available": true,
"currency": "USD",
"image_url": "https://cdn.shopify.com/s/files/1/0920/9112/1967/files/level-mips-helmet_matteIronwood_3Q.png?v=1749177564",
"price": "225.0",
"title": "Matte Ironwood / MEDIUM-MIPS",
"variant_id": "gid://shopify/ProductVariant/51399728595247"
},
{
"available": true,
"currency": "USD",
"image_url": "https://cdn.shopify.com/s/files/1/0920/9112/1967/files/level-mips-helmet_matteIronwood_3Q.png?v=1749177564",
"price": "195.0",
"title": "Matte Ironwood / S",
"variant_id": "gid://shopify/ProductVariant/51399728628015"
},
{
"available": true,
"currency": "USD",
"image_url": "https://cdn.shopify.com/s/files/1/0920/9112/1967/files/level-mips-helmet_matteIronwood_3Q.png?v=1749177564",
"price": "195.0",
"title": "Matte Ironwood / M",
"variant_id": "gid://shopify/ProductVariant/51399728660783"
},
{
"available": true,
"currency": "USD",
"image_url": "https://cdn.shopify.com/s/files/1/0920/9112/1967/files/level-mips-helmet_matteIronwood_3Q.png?v=1749177564",
"price": "195.0",
"title": "Matte Ironwood / L",
"variant_id": "gid://shopify/ProductVariant/51399728693551"
},
{
"available": true,
"currency": "USD",
"image_url": "https://cdn.shopify.com/s/files/1/0920/9112/1967/files/level-mips-helmet_matteSunrise_3Q.png?v=1750298981",
"price": "225.0",
"title": "Matte Sunrise / MEDIUM-MIPS",
"variant_id": "gid://shopify/ProductVariant/51399728824623"
},
{
"available": true,
"currency": "USD",
"image_url": "https://cdn.shopify.com/s/files/1/0920/9112/1967/files/level-mips-helmet_matteSunrise_3Q.png?v=1750298981",
"price": "195.0",
"title": "Matte Sunrise / S",
"variant_id": "gid://shopify/ProductVariant/51399728857391"
},
{
"available": true,
"currency": "USD",
"image_url": "https://cdn.shopify.com/s/files/1/0920/9112/1967/files/level-mips-helmet_matteSunrise_3Q.png?v=1750298981",
"price": "195.0",
"title": "Matte Sunrise / M",
"variant_id": "gid://shopify/ProductVariant/51399728890159"
},
{
"available": true,
"currency": "USD",
"image_url": "https://cdn.shopify.com/s/files/1/0920/9112/1967/files/level-mips-helmet_matteSunrise_3Q.png?v=1750298981",
"price": "195.0",
"title": "Matte Sunrise / L",
"variant_id": "gid://shopify/ProductVariant/51399728922927"
}
]
}
]
}