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).
Tools
| Bridge tool (Path A) | Raw UCP tool (Path B) | Scope |
|---|---|---|
search_products | search_catalog @ /mcp | Global search |
lookup_products | lookup_catalog @ /mcp | Resolve by id/handle/SKU/barcode (global) |
get_product_global | get_product @ /mcp | Full detail by id, any store |
view_product | get_product @ /s/:slug/mcp | Full detail within a store |
Two shapes, one catalog
The bridge flattens arguments; raw UCP wraps them. Money differs too — read this before copying filters.
Path A (bridge search_products) — flat args, price in major units (dollars):
{
"query": "running shoes",
"filters": { "price": { "max": 100, "currency": "USD" } },
"sort": "price_asc"
}Path B (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 Path A) 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
Map the buyer’s request onto structured filters. A search needs a query and/or
filters — calling with only pagination returns nothing.
| Buyer says | Filter |
|---|---|
| ”under $100” | filters.price.max (dollars on Path A, cents on Path B) |
| “red ones” | filters.attributes: [{ "name": "Color", "value": "Red" }] |
| ”on sale”, “eco-friendly” | filters.tags: ["sale"] |
| ”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" (price_desc, or newest default) |
| “include sold out” | filters.available: false (out-of-stock hidden by default) |
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 Path A, search_products and view_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.