Skip to main content

Search the Shopify Catalog

Early access

Agentic commerce is rolling out to Dev Dashboard. Sign up to be notified.

Shopify Catalog contains all eligible products sold across merchants on the Shopify platform. You can use the Catalog MCP server to search for products and retrieve detailed product information for your AI agent.

This tutorial shows you how to authenticate with the Catalog MCP server, search for products, and apply filters to refine results.


In this tutorial, you'll learn how to do the following tasks:

  • Generate API credentials from Shopify to authenticate with the Catalog MCP server
  • Search for products across the global Shopify Catalog using natural language queries
  • Apply filters and parameters to refine product search results
  • Extract product variant IDs and shop domains for checkout


Anchor to Step 1: Generate API credentialsStep 1: Generate API credentials

Get your client credentials and generate API keys to authenticate with the Catalog MCP server.

  1. Obtain your client credentials (client ID and secret) from the Catalogs section of Dev Dashboard.

    Diagram showing cart groups flowing to Checkout Kit presentations
  2. Make a POST request to the token endpoint to generate a bearer token you'll use for subsequent requests:

    Terminal

    curl --request POST \
    --url https://api.shopify.com/auth/access_token \
    --header 'Content-Type: application/json' \
    --data '{
    "client_id": "{your_client_id}",
    "client_secret": "{your_client_secret}",
    "grant_type": "client_credentials"
    }'
  3. Create a .env file and add your token:

    .env

    BEARER_TOKEN={your_token}

Anchor to Step 2: Search the CatalogStep 2: Search the Catalog

Create a script that sends a query to the Catalog MCP server to search for products using the search_global_products tool.

  1. Create a search-catalog.js file that sends a query to the Catalog MCP server:

    import 'dotenv/config';

    const bearerToken = process.env.BEARER_TOKEN;

    fetch('https://discover.shopifyapps.com/global/mcp', {
    method: 'POST',
    headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${bearerToken}`
    },
    body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'tools/call',
    id: 1,
    params: {
    name: 'search_global_products',
    arguments: {
    query: 'I need a crewneck sweater',
    context: 'buyer looking for sustainable fashion',
    limit: 3
    }
    }
    })
    })
    .then(res => res.json())
    .then(data => {
    // Parse the text field to get the actual offers object
    if (data.result && data.result.content && data.result.content[0]) {
    const offersData = JSON.parse(data.result.content[0].text);
    console.log(JSON.stringify(offersData, null, 2));
    }
    });
    {
    "offers": [
    {
    "id": "gid://shopify/p/abc123XYZ789defGHI456jk",
    "title": "Classic Crewneck Sweatshirt",
    "description": "A comfortable everyday sweatshirt with a classic fit and soft cotton-polyester blend.",
    "images": [
    {
    "url": "https://cdn.shopify.com/s/files/1/0000/0001/0002/files/classic-crewneck.webp?v=1700000001",
    "altText": "Classic Crewneck Sweatshirt",
    "product": {
    "id": "gid://shopify/Product/1000000000001",
    "title": "Classic Crewneck Sweatshirt",
    "onlineStoreUrl": "https://mock.shop/products/classic-crewneck-sweatshirt?variant=2000000000001&_gsid=abc123def456",
    "shop": {
    "name": "Mock Shop",
    "onlineStoreUrl": "https://mock.shop"
    }
    }
    }
    ],
    "options": [
    {
    "name": "Color",
    "values": [
    {
    "value": "Heather Gray",
    "availableForSale": true,
    "exists": true
    }
    ]
    },
    {
    "name": "Size",
    "values": [
    {
  2. Execute the script from your terminal:

    Terminal

    node search-catalog.js

    The response includes product titles, descriptions, pricing, images, and detailed variant information for each matching product. You can use data from the returned UniversalProduct resource to display custom components, so that buyers can interact with individual products. For example:

    let response = JSON.parse(data.result.content[0].text);
    let offer = response.offers[buyerSelectedIndex];

    // Extract product data
    let product = {
    title: offer.title,
    price: offer.priceRange.min.amount,
    currency: offer.priceRange.min.currencyCode,
    image: offer.images[0]?.url,
    url: offer.url
    };

    let card = `
    <div class="product-card">
    <img src="${product.image}" alt="${product.title}" />
    <h3>${product.title}</h3>
    <p>${product.currency} $${parseFloat(product.price).toFixed(2)}</p>
    <a href="${product.url}">View Product</a>
    </div>
    `;
    Info

    You can create custom catalogs in Dev Dashboard that allow you to narrow results down to those most relevant to the goals of your agent, which will change the endpoint URLs needed to connect to the MCP server. Learn more about saved catalogs in Dev Dashboard.


Anchor to Step 3: Apply query parametersStep 3: Apply query parameters

Buyers can supply additional context that can be used to refine your catalog search by adding filters for price, shipping location, availability, and other criteria.

  1. Limit results to products within a specific price range, that ships to a specific country, and that can be second hand items:

    import 'dotenv/config';

    const bearerToken = process.env.BEARER_TOKEN;

    fetch('https://discover.shopifyapps.com/global/mcp', {
    method: 'POST',
    headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${bearerToken}`
    },
    body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'tools/call',
    id: 1,
    params: {
    name: 'search_global_products',
    arguments: {
    query: 'I need a crewneck sweater',
    context: 'buyer looking for sustainable fashion',
    include_secondhand: true,
    min_price: 50,
    max_price: 200,
    ships_to: 'US',
    limit: 3
    }
    }
    })
    })
    .then(res => res.json())
    .then(data => {
    // Parse the text field to get the actual offers object
    if (data.result && data.result.content && data.result.content[0]) {
    const offersData = JSON.parse(data.result.content[0].text);
    console.log(JSON.stringify(offersData, null, 2));
    }
    });
    {
    "offers": [
    {
    "id": "gid://shopify/p/abc123XYZ789defGHI456jk",
    "title": "Classic Crewneck Sweatshirt",
    "description": "A comfortable everyday sweatshirt with a classic fit and soft cotton-polyester blend.",
    "images": [
    {
    "url": "https://cdn.shopify.com/s/files/1/0000/0001/0002/files/classic-crewneck.webp?v=1700000001",
    "altText": "Classic Crewneck Sweatshirt",
    "product": {
    "id": "gid://shopify/Product/1000000000001",
    "title": "Classic Crewneck Sweatshirt",
    "onlineStoreUrl": "https://mock.shop/products/classic-crewneck-sweatshirt?variant=2000000000001&_gsid=abc123def456",
    "shop": {
    "name": "Mock Shop",
    "onlineStoreUrl": "https://mock.shop"
    }
    }
    }
    ],
    "options": [
    {
    "name": "Color",
    "values": [
    {
    "value": "Heather Gray",
    "availableForSale": true,
    "exists": true
    }
    ]
    },
    {
    "name": "Size",
    "values": [
    {

Anchor to Step 4: Lookup detailed product informationStep 4: Lookup detailed product information

At this point your search has resulted in three products, and the buyer may be interested in investigating additional details about them. Lookup within Catalog allows you to render product details not available already in Search (for example, all variants, detailed pricing, availability, and shop details) so buyers can explore available options and offers.

If the buyer chooses an unavailable option, the API will relax those options to find an available variant. See the get_global_product_details tool documentation for more information about options preferences.

In this example, you'll modify the script to arbitrarily select a product variant result, then leverage Lookup to get additional information about that product.

  1. The Catalog returns products grouped by Universal Product ID (UPID). Each product includes shop information and variant details:

    import 'dotenv/config';

    const bearerToken = process.env.BEARER_TOKEN;

    fetch('https://discover.shopifyapps.com/global/mcp', {
    method: 'POST',
    headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${bearerToken}`
    },
    body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'tools/call',
    id: 1,
    params: {
    name: 'search_global_products',
    arguments: {
    query: 'I need a crewneck sweater',
    context: 'buyer looking for sustainable fashion',
    include_secondhand: true,
    min_price: 50,
    max_price: 200,
    ships_to: 'US',
    limit: 3
    }
    }
    })
    })
    .then(res => res.json())
    .then(data => {
    // Parse the text field to get the actual offers object
    if (data.result && data.result.content && data.result.content[0]) {
    const offersData = JSON.parse(data.result.content[0].text);
    console.log(JSON.stringify(offersData, null, 2));
    }
    // Extract first offer ID and call get_global_product_details
    if (data.result && data.result.content && data.result.content[0]) {
    const textContent = JSON.parse(data.result.content[0].text);
    if (textContent.offers && textContent.offers.length > 0) {
    const fullId = textContent.offers[0].id;
    // Extract UPID from gid://shopify/p/{UPID}
    const upid = fullId.split('/p/')[1];
    console.log('\n✓ Using first offer UPID:', upid);
    // Call get_global_product_details with the extracted ID
    return fetch('https://discover.shopifyapps.com/global/mcp', {
    method: 'POST',
    headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${bearerToken}`
    },
    body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'tools/call',
    id: 2,
    params: {
    name: 'get_global_product_details',
    arguments: {
    upid: upid
    }
    }
    })
    });
    }
    }
    })
    .then(res => res ? res.json() : null)
    .then(data => {
    if (data) {
    console.log('\nProduct Details:');
    console.log(JSON.stringify(data, null, 2));
    }
    })
    .catch(err => console.error('Request failed:', err));
    {
    "id": "gid://shopify/p/abc123XYZ789defGHI456jk",
    "title": "Classic Crewneck Sweatshirt",
    "description": "A comfortable everyday sweatshirt with a classic fit and soft cotton-polyester blend.",
    "images": [
    {
    "url": "https://cdn.shopify.com/s/files/1/0000/0001/0002/files/classic-crewneck-front.webp?v=1700000001",
    "altText": "Classic Crewneck Sweatshirt - Front",
    "product": {
    "id": "gid://shopify/Product/1000000000001",
    "title": "Classic Crewneck Sweatshirt",
    "onlineStoreUrl": "https://mock.shop/products/classic-crewneck-sweatshirt?variant=2000000000001&_gsid=abc123def456",
    "shop": {
    "name": "Mock Shop",
    "onlineStoreUrl": "https://mock.shop"
    }
    }
    },
    {
    "url": "https://cdn.shopify.com/s/files/1/0000/0001/0002/files/classic-crewneck-back.webp?v=1700000002",
    "altText": "Classic Crewneck Sweatshirt - Back",
    "product": {
    "id": "gid://shopify/Product/1000000000001",
    "title": "Classic Crewneck Sweatshirt",
    "onlineStoreUrl": "https://mock.shop/products/classic-crewneck-sweatshirt?variant=2000000000001&_gsid=abc123def456",
    "shop": {
    "name": "Mock Shop",
    "onlineStoreUrl": "https://mock.shop"
    }
    }
    },
    {
    "url": "https://cdn.shopify.com/s/files/1/0000/0001/0002/files/classic-crewneck-detail.webp?v=1700000003",
    "altText": "Classic Crewneck Sweatshirt - Detail",
    "product": {
    "id": "gid://shopify/Product/1000000000001",

    Inspect the response related to product variant. You'll see from that response (at .result.content[0].text.product.products[0].selectedProductVariant) the default selection for the result.

    Info

    It's at this point that your agent would respond to a buyer's behavior to identify which products, variants, and options to investigate with the get_global_product_details tool. In this demo, the first result in the array of resulting products is chosen to stand-in for this behavior.

  2. Update the script to get updated details for the size the buyer actually wants to order using the product_options argument.

    import 'dotenv/config';

    const bearerToken = process.env.BEARER_TOKEN;

    fetch('https://discover.shopifyapps.com/global/mcp', {
    method: 'POST',
    headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${bearerToken}`
    },
    body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'tools/call',
    id: 1,
    params: {
    name: 'search_global_products',
    arguments: {
    query: 'vintage leather jacket',
    include_secondhand: true,
    max_price: 150,
    limit: 3
    }
    }
    })
    })
    .then(res => res.json())
    .then(data => {
    // Extract first offer ID and call get_global_product_details
    if (data.result && data.result.content && data.result.content[0]) {
    const textContent = JSON.parse(data.result.content[0].text);
    if (textContent.offers && textContent.offers.length > 0) {
    const fullId = textContent.offers[0].id;
    // Extract UPID from gid://shopify/p/{UPID}
    const upid = fullId.split('/p/')[1];
    // Call get_global_product_details with the extracted ID
    return fetch('https://discover.shopifyapps.com/global/mcp', {
    method: 'POST',
    headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${bearerToken}`
    },
    body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'tools/call',
    id: 2,
    params: {
    name: 'get_global_product_details',
    arguments: {
    upid: upid,
    product_options: [{
    key: 'Size',
    values: ['Large (L)']
    }]
    }
    }
    })
    });
    }
    }
    })
    .then(res => res ? res.json() : null)
    .then(data => {
    if (data) {
    console.log(JSON.stringify(data, null, 2));
    }
    })
    .catch(err => console.error('Request failed:', err));


Was this page helpful?