Skip to Content
Catalog

Catalog

Agents discover products two ways:

  • Global catalog — search across every Artos store in one call. Each result is annotated with the store that sells it, so you can route follow-up cart/checkout calls to the right store.
  • Per-store catalog — browse or look up within a single store once you know its slug.

The global catalog is read-only: there is no cross-store cart. Discover globally, then run a per-store checkout (see Checkout).

Building a price tracker, search index, or product feed? The Catalog crawler guide walks through read-only ingestion with the CLI and SDK (including pagination).

Tools

Bridge tool (Connect)Raw UCP tool (Build)Scope
search_productsPOST /catalog/search (REST)Global search
lookup_productslookup_catalog @ /mcpResolve by id/handle/SKU/barcode (global)
get_productget_product @ /s/:slug/mcp or /mcpFull detail by id — within a store with store_slug, across every store without it

Two shapes, one catalog

The bridge flattens arguments; raw UCP wraps them. Money differs too — read this before copying filters.

Connect (bridge search_products) — flat args, price in major units (dollars):

{ "query": "running shoes", "filters": { "price": { "max": 100, "currency": "USD" } }, "sort": "price_asc" }

Build (raw search_catalog) — args wrapped under catalog, price in minor units (integer cents), and meta["ucp-agent"].profile is required on every call:

{ "name": "search_catalog", "arguments": { "catalog": { "query": "running shoes", "filters": { "price": { "max": 10000 } }, "sort": "price_asc" }, "meta": { "ucp-agent": { "profile": "https://your-app.example/.well-known/ucp" } } } }

With the @artos-commerce/ucp-client SDK you skip the envelope and unit conversion entirely — shop.searchProducts({ query, filters }) takes filters in major units (like Connect) and client.callGlobalTool('search_catalog', …) exposes the raw tool when you need it:

const results = await shop.searchProducts({ query: 'running shoes', filters: { price: { max: 100, currency: 'USD' } }, });

Natural language → filters

The free-text query is full-text search: word order doesn’t matter, plurals/word forms match (“shoes” finds “Shoe”), small typos fall back to fuzzy matching, and results come back most-relevant first — pass the buyer’s product words as-is. Map explicit constraints onto structured filters. A search needs a query and/or filters — a request with neither (e.g. only pagination) is rejected as invalid_request.

Buyer saysFilter
”under $100”filters.price.max (dollars on Connect, cents on Build)
“red ones”filters.attributes: [{ "name": "Color", "value": "Red" }]
”on sale”, “eco-friendly”filters.tags: ["sale"] (exact tag, case-insensitive)
“running shoes” (a category)filters.categories: ["shoes"] (fuzzy; partial matches the full taxonomy path)
“from Energy Sport”filters.seller: "energy-sport" (fuzzy: slug, name, or description)
“cheapest first”sort: "price_asc" (or price_desc / newest; omitted, a query sorts by relevance, a filter-only browse by newest)
“include sold out”filters.available: false (out-of-stock hidden by default; valid on its own as a whole-catalog browse)

Responses echo facets.categories and facets.sellers. Feed a facet back as a filter to refine (“what categories does this shop carry?” → search with filters.seller, read facets.categories).

Multi-store routing

Global search annotates each product with its seller under the sanctioned UCP extension point metadata.artos_seller:

{ "metadata": { "artos_seller": { "slug": "energy-sport", "name": "Energy Sport", "endpoint": "https://api.artos.sh/s/energy-sport" } } }

Use metadata.artos_seller.slug as the store_slug for create_cart / create_checkout. To buy from multiple shops, run a separate checkout per store.

MCP Apps widgets

On Connect, search_products and get_product carry an MCP App UI resource (_meta.ui.resourceUri) so hosts like Claude render an inline product grid / detail card instead of raw JSON.

Last updated on