ChanlChanl
Tools & MCP

From Keyword Search to Shopping Memory

Build the intelligence layer for an AI shopping assistant: semantic product search with Commerce MCP, customer memory that persists across visits, and MCP tool registration for multi-channel deployment.

DGDean GroverCo-founderFollow
March 21, 2026
13 min read
Warm watercolor illustration of interconnected data streams flowing through a library-like space

In Part 1, we built a rendering layer that turns product data into beautiful cards inside a chat. Images, prices, star ratings, "Add to Cart" buttons. It looks great. One problem: the products come from a hardcoded array.

Try asking for "something warm for a weekend hike." Nothing. Try "what's similar to what I bought last month?" Nothing again. The rendering layer works, but the data behind it is fake. This article builds the intelligence layer: real product data, semantic search that understands intent, and customer memory that persists across visits. By the end, all three are wired into a single MCP tool that any agent can call.

Commerce Protocols in 2026

Two protocols standardize how AI agents talk to commerce platforms: Commercetools MCP and Google's Universal Commerce Protocol. Six months ago, connecting an agent to a product catalog meant writing a custom API wrapper. Now you pick a protocol and get structured data out of the box.

Commercetools Commerce MCP turns product catalogs, carts, orders, and pricing into MCP tools that any AI agent can call. It ships as an npm package (@commercetools/mcp-essentials) and works with the Vercel AI SDK, OpenAI Agent SDK, and LangChain.

Google's Universal Commerce Protocol (UCP) takes a broader approach. Co-developed with Shopify and endorsed by over 20 companies (Target, Walmart, Visa, Stripe), UCP defines three primitives: Checkout, Identity Linking via OAuth 2.0, and Order Management. It has an explicit MCP binding, so MCP agents speak UCP natively. Shopify layers on top with agentic storefronts: UCP-compliant tools and a Checkout Kit already integrated with ChatGPT, Perplexity, and Copilot.

Let's connect to a real catalog. Here's a Commercetools MCP server configuration that gives your agent access to product search, cart management, and order history:

json
{
  "mcpServers": {
    "commercetools": {
      "command": "npx",
      "args": ["-y", "@commercetools/mcp-essentials"],  // Auto-installs on first run
      "env": {
        "CTP_PROJECT_KEY": "your-project",
        "CTP_CLIENT_ID": "your-client-id",
        "CTP_CLIENT_SECRET": "your-client-secret",      // Store in env vars, never commit
        "CTP_AUTH_URL": "https://auth.us-central1.gcp.commercetools.com",
        "CTP_API_URL": "https://api.us-central1.gcp.commercetools.com"
      }
    }
  }
}

With that running, your agent can call product_search, cart_create, and order_get with no custom code:

AI Agent MCP Client Commerce MCP Server Product Catalog Shopping Cart Order History
Commerce MCP connects AI agents to real product catalogs, carts, and orders

Now your agent has real products. But there's a gap: Commerce MCP gives you structured data (SKUs, prices, inventory counts), not understanding. A customer who asks for "something warm for hiking" gets zero results unless a product literally contains those words. We need a semantic layer on top of the catalog.

Product Catalog as Knowledge Base

Semantic search understands intent, not just keywords. "Warm waterproof jacket under $100" finds insulated rain shells at $89 even though "warm" never appears in the product description. That's the difference between a search box and a shopping assistant.

You can build this yourself. Embed every product description with OpenAI's embedding model, store vectors in Pinecone or Weaviate, build a query pipeline with similarity thresholds, handle reindexing when products change. We covered the full approach in RAG from Scratch. It works, and you'll learn a lot building it.

But for a shopping assistant that needs to stay current with inventory, there's a simpler path. One API call turns a product feed into a searchable knowledge base, no embedding pipeline or vector database to maintain. Here's how that looks with the Chanl SDK:

typescript
import Chanl from '@chanl/sdk';
 
const sdk = new Chanl({ apiKey: process.env.CHANL_API_KEY });
 
// Ingest from a JSON feed - each product becomes a searchable document
const kb = await sdk.knowledge.create({
  title: 'Product Catalog',
  source: 'url',
  url: 'https://store.example.com/products.json',
});
 
// Or crawl rendered pages - picks up descriptions, specs, reviews
await sdk.knowledge.create({
  title: 'Product Pages',
  source: 'url',
  url: 'https://store.example.com/products',
  crawlOptions: { depth: 2 },  // Follow links 2 levels deep
});
 
// Hybrid = vector similarity for "warm" + keyword filter for "$100"
const results = await sdk.knowledge.search({
  query: 'warm waterproof jacket under $100',
  mode: 'hybrid',
});

That hardcoded array from Part 1 just became a semantic knowledge base. Hybrid mode is the key here: it combines vector similarity (understanding that "hiking boots" relates to trail footwear) with keyword filtering (the $100 price cap). Pure vector search can't filter on price. Pure keyword search misses intent. Hybrid handles both as naturally as a human store clerk would.

Products change? Re-crawl the URL and the index updates. No manual embedding jobs, no vector database tuning. But the search still treats every customer the same. When someone asks for "a new shirt," it doesn't know this customer prefers organic cotton in size M. That takes memory.

Customer Memory Across Sessions

Memory turns a search box into a personal shopper. Without it, a customer mentions they prefer organic cotton, wear size M, and usually shop under $80. Your assistant finds great options. They leave, come back next week, and the agent greets them like a stranger: "What are you looking for today?" All those preferences, gone.

Three operations fix this: store explicit facts, auto-extract implicit ones from conversations, and recall everything on return visits. Here's all three together:

typescript
// --- 1. Store explicit facts ---
await sdk.memory.create({
  entityType: 'customer',  // Scoped to this customer, not the whole workspace
  entityId: 'cust_123',
  content: 'Prefers organic cotton, wears size M, budget under $80',
});
 
// --- 2. Auto-extract facts from conversation transcripts ---
// "I'm looking for something like that linen shirt I got last summer"
// contains implicit info about material preference and purchase history
const { data } = await sdk.memory.extract({
  text: conversationTranscript,  // Full transcript - AI finds the facts
  entityType: 'customer',
  entityId: 'cust_123',
  save: true,  // Persist immediately; false = preview only
});
// Returns facts with confidence scores:
// [{ content: "Prefers linen and natural fabrics", confidence: 0.92 },
//  { content: "Bought a linen shirt (summer 2025)", confidence: 0.88 }]
 
// --- 3. Recall on return visits ---
// Pull memories BEFORE the agent's first response
const { data: memories } = await sdk.memory.search({
  entityType: 'customer',
  entityId: 'cust_123',
  query: 'preferences',  // Semantic search over stored facts
});
 
// Flatten into a string the LLM can read
const context = memories.memories
  .map((m) => m.content)
  .join('\n');
 
const systemPrompt = `You are a shopping assistant.
 
Customer context (from previous visits):
${context}
 
Use these preferences to personalize recommendations
without asking the customer to repeat themselves.`;

Instead of "What are you looking for today?" the agent says "Welcome back! I remember you prefer organic cotton in size M. We just got some new arrivals in your price range." That continuity turns a one-time shopper into a repeat customer.

For deeper coverage on memory patterns, see AI Agent Memory: From Session Context to Long-Term Knowledge.

Customer service representative

Customer Memory

4 memories recalled

Sarah Chen
Premium
Last call
2 days ago
Prefers
Email follow-up
Session Memory

“Discussed upgrading to Business plan. Budget approved at $50k. Follow up next Tuesday.”

85% relevance

Registering Search as an MCP Tool

One MCP tool that combines catalog search, semantic understanding, and customer memory. Register it once, and any agent on any channel can call it.

First, define the tool so your agent knows how to invoke it:

typescript
await sdk.tools.create({
  name: 'search_products',
  description:
    'Search the product catalog using natural language. ' +
    'Considers customer preferences from memory and returns ' +
    'products with images, prices, and availability.',
  type: 'http',  // Chanl calls your endpoint when the agent invokes this tool
  inputSchema: {
    type: 'object',
    properties: {
      query: {
        type: 'string',
        description: 'Natural language product search query',
      },
      maxPrice: {
        type: 'number',
        description: 'Maximum price filter (optional)',
      },
      category: {
        type: 'string',
        description: 'Product category filter (optional)',
      },
      customerId: {
        type: 'string',
        description: 'Customer ID for personalized results',
      },
    },
    required: ['query'],  // Only query is mandatory, filters are optional
  },
  configuration: {
    url: 'https://your-api.com/search',  // Your backend handles the 3-layer logic below
    method: 'POST',
  },
});

Behind that endpoint, the handler combines all three layers. It recalls customer preferences, enriches the query, runs hybrid search, and shapes the results to match Part 1's ProductCardSchema:

typescript
async function handleProductSearch({
  query,
  maxPrice,
  category,
  customerId,
}: SearchParams) {
  // 1. Recall customer preferences (skip for anonymous users)
  let preferences = '';
  if (customerId) {
    const { data: memoryData } = await sdk.memory.search({
      entityType: 'customer',
      entityId: customerId,
      query: 'product preferences',
    });
    preferences = memoryData.memories.map((m) => m.content).join('. ');
  }
 
  // 2. Prepend preferences so vector search weighs them
  const enrichedQuery = preferences
    ? `${query} (customer prefers: ${preferences})`
    : query;
 
  // 3. Hybrid = semantic similarity + keyword filtering
  const { data: searchData } = await sdk.knowledge.search({
    query: enrichedQuery,
    mode: 'hybrid',
    limit: 10,  // Over-fetch, then filter below
  });
 
  // 4. Post-filter on structured fields the vector search can't handle
  const filtered = searchData.results.filter((product) => {
    if (maxPrice && product.metadata?.price > maxPrice) return false;
    if (category && product.metadata?.category !== category) return false;
    return true;
  });
 
  // 5. Shape matches Part 1's ProductCardSchema
  return filtered.map((product) => ({
    name: product.metadata?.name,
    price: product.metadata?.price,
    image: product.metadata?.imageUrl,
    rating: product.metadata?.rating,
    url: product.metadata?.productUrl,
  }));
}

Same tool, every channel. A customer asks for hiking boots through your webchat widget. Two days later they call your voice line, and the same search_products tool handles it, this time remembering they prefer waterproof, size 10. The rendering layer from Part 1 turns the response into visual product cards. The intelligence layer here makes sure those cards show the right products.

For more on tool architecture, see AI Agent Tools: MCP, OpenAPI, and Tool Management.

The Full Stack

Here's everything we've built across both articles:

Customer Chat UI Voice Agent REST API AI Backend search_productsMCP Tool Commerce MCP(Live Catalog) Knowledge Base(Semantic Search) Customer Memory(Preferences) Widget Renderer(Part 1) Product Cards+ Add to Cart
Complete shopping assistant: rendering (Part 1) + intelligence (Part 2)

Part 1 gave us the rendering layer. This article added the intelligence layer: real catalog data through Commerce MCP, semantic search through a knowledge base, and customer memory that persists across sessions. All three combine in a single MCP tool that works on every channel.

What's missing is confidence. How do you know the search returns the right products before real customers hit edge cases? What happens when the knowledge base returns stale prices, or customer memory recalls an outdated preference? Part 3 covers AI persona testing, quality scorecards, and production monitoring. The intelligence layer works. Now we need to prove it.

DG

Co-founder

Building the platform for AI agents at Chanl — tools, testing, and observability for customer experience.

Learn Agentic AI

One lesson a week — practical techniques for building, testing, and shipping AI agents. From prompt engineering to production monitoring. Learn by doing.

500+ engineers subscribed

Frequently Asked Questions