Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 22 additions & 9 deletions docs/specification/catalog/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,21 @@ This enables product discovery before checkout, supporting use cases like:
* Free-text product search
* Category and filter-based browsing
* Batch product/variant retrieval by identifier
* Single-product detail for purchase decisions
* Price comparison across variants

## Capabilities

| Capability | Description |
| :--- | :--- |
| [`dev.ucp.shopping.catalog.search`](search.md) | Search for products using query text and filters. |
| [`dev.ucp.shopping.catalog.lookup`](lookup.md) | Retrieve products or variants by identifier. |
| [`dev.ucp.shopping.catalog.lookup`](lookup.md) | Retrieve products or variants by identifier. Supports batch lookup and single-product detail. |

## Key Concepts

* **Product**: A catalog item with title, description, media, and one or more
variants.
* **Variant**: A purchasable item with specific option selections (e.g., "Blue /
* **Variant**: A purchasable item with specific option values (e.g., "Blue /
Large"), price, and availability.
* **Price**: Price values include both amount (in minor currency units) and
currency code, enabling multi-currency catalogs.
Expand Down Expand Up @@ -74,19 +75,31 @@ currency; response prices include explicit currency codes.
A catalog item representing a sellable item with one or more purchasable variants.

`media` and `variants` are ordered arrays. Businesses SHOULD return the most
relevant variant and image first—default for lookups, best match based on query
and context for search. Platforms SHOULD treat the first element as featured.
relevant variant and image first. Platforms SHOULD treat the first element as
featured.

Variant cardinality varies by operation:

* **Search**: Multiple products with one featured variant each.
* **Batch Lookup**: Multiple products with variants matched by input
identifiers — see [Client Correlation](lookup.md#client-correlation).
* **Get Product**: Single product with featured variant and a relevant
subset, filtered by option selections when provided.

{{ schema_fields('types/product', 'catalog') }}

Operation-specific extensions to the base product are defined in the
[Lookup capability schema](lookup.md): `get_product` responses add
`selected` (effective option selections after relaxation); `lookup_catalog`
responses extend variants with `input` correlation.

### Variant

A purchasable item with specific option selections, price, and availability.
A purchasable item with specific option values, price, and availability.

In lookup responses, each variant carries an `input` array for correlation:
which request identifiers resolved to this variant, and whether the match
was `exact` or `featured` (server-selected). See
[Client Correlation](lookup.md#client-correlation) for details.
Each variant carries an `options` array describing the option values that define
it (e.g., Color: Blue, Size: Large). These are intrinsic to the variant—they
describe what the variant IS, independent of user selections.

`media` is an ordered array. Businesses SHOULD return the featured variant image
as the first element. Platforms SHOULD treat the first element as featured.
Expand Down
129 changes: 122 additions & 7 deletions docs/specification/catalog/lookup.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,32 @@
* **Capability Name:** `dev.ucp.shopping.catalog.lookup`

Retrieves products or variants by identifier. Use this when you already have
identifiers (e.g., from a saved list, deep links, or cart validation).
identifiers (e.g., from a saved list, deep links, cart validation, or a selected
product for detail rendering).

## Operation
## Operations

| Operation | Description |
| :--- | :--- |
| **Lookup Catalog** | Retrieve products or variants by identifier. |
| Operation | Tool / Endpoint | Description |
| :--- | :--- | :--- |
| **Batch Lookup** | `lookup_catalog` / `POST /catalog/lookup` | Retrieve multiple products by identifier. |
| **Get Product** | `get_product` / `POST /catalog/product` | Retrieve full detail for a single product. |

Both operations accept product and variant identifiers. They differ in cardinality

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The “Both operations accept product and variant identifiers” table implies get_product supports the same identifier surface as lookup_catalog (SKU, URL, handle, etc.), but the later Get Product → Supported Identifiers section says “The id parameter accepts a single product ID or variant ID.” These are materially different guarantees.

Suggested fix: explicitly state:
“MUST support product ID + variant ID”
“MAY support SKU/URL/handle/etc.” (list optional identifiers)
and add a normative note: identifiers are opaque strings and MUST NOT be dereferenced or fetched as URLs.

and response shape:

| Concern | `lookup_catalog` | `get_product` |
| :--- | :--- | :--- |
| **Input** | `ids[]` — product or variant ID, SKU, URL, etc. | `id` — single identifier |
| **Purpose** | Resolve identifiers to products | Detailed product information for purchase decisions, optionally filtered by option selection |
| **Variants** | One featured variant per product | Featured variant and relevant subset, filtered by option selections |

Use `lookup_catalog` when you have identifiers to resolve or display in a list.
Use `get_product` when a user has selected a specific product and needs full
detail, including interactive variant selection, for a purchase decision.

---

## Batch Lookup (`lookup_catalog`)

### Supported Identifiers

Expand Down Expand Up @@ -68,6 +87,9 @@ with an appropriate error (HTTP 400 `request_too_large` for REST, JSON-RPC
* **`featured`**: Identifier resolved to the parent product; server
selected this variant as representative (e.g., product ID, handle).

Use [`get_product`](#get-product-get_product) for variant selection, option
availability, and complete product context.

### Request

{{ extension_schema_fields('catalog_lookup.json#/$defs/lookup_request', 'catalog') }}
Expand All @@ -76,7 +98,100 @@ with an appropriate error (HTTP 400 `request_too_large` for REST, JSON-RPC

{{ extension_schema_fields('catalog_lookup.json#/$defs/lookup_response', 'catalog') }}

---

## Get Product (`get_product`)

Retrieves current product state for a single identifier, with support for
interactive variant selection and real-time availability signals. This is the
authoritative source for purchase decisions.

### Supported Identifiers

The `id` parameter accepts a single product ID or variant ID.

### Resolution Behavior

The response returns the product with complete context (title, description,
media, options) and a **subset of variants matching
[`product.selected`](#option-selection)**:

* **Product ID**: `variants` SHOULD contain the featured variant and other
variants matching `product.selected`. When the request includes `selected`
options, this narrows the subset to variants matching the client's choices.
* **Variant ID**: The requested variant MUST be the first element (featured).
`product.selected` reflects that variant's options. Remaining variants
match the same effective selections.

### Response Shape

The response contains a singular `product` object (not an array). This reflects
the single-resource semantics of the operation. When the identifier is not found,
the response MUST return an error (HTTP 404 for REST, JSON-RPC `-32602` for MCP)
rather than an empty result.

### Option Selection

The `selected` and `preferences` parameters enable interactive variant
narrowing: the core product detail page interaction where a user progressively selects options
(Color, Size, etc.) and the UI updates availability in real time.

#### Input

* **`selected`**: Array of option selections (e.g., `[{"name": "Color", "label": "Red"}]`).
Partial selections are valid; the client sends whatever the user has chosen so far.
Each option name MUST appear at most once.
* **`preferences`**: Option names in relaxation priority order (e.g.,
`["Color", "Size"]`). When no variant matches all selections, the server drops
options from the **end** of this list first, keeping higher-priority selections
intact. Optional; if omitted, the server uses its own relaxation heuristic.

#### Output: Effective Selections

The response MUST include `product.selected`: the effective option
selections that determine the featured variant, the variant subset, and
all availability signals. When the request omits `selected`, the server
determines the initial selections (typically the featured variant's own
options). When the request includes `selected`, effective selections
reflect the server's resolution after any relaxation.

Clients that send `selected` detect relaxation by diffing their request
against `product.selected`:

* **No relaxation**: Response `selected` matches the request — all
selections resolved to at least one variant.
* **Relaxation occurred**: Response `selected` is a subset of the
request — the server dropped unresolvable options per `preferences`
priority.

#### Output: Availability Signals

Option values in the response SHOULD include availability signals
relative to `product.selected`:

| `available` | `exists` | Meaning | UI Treatment |
| :--- | :--- | :--- | :--- |
| `true` | `true` | In stock — purchasable | Selectable |
| `false` | `true` | Out of stock — variant exists but unavailable | Disabled / strikethrough |
| `false` | `false` | No variant for this combination | Hidden or visually distinct |

These fields appear on each option value in `product.options[].values[]`. They
reflect availability **relative to the effective selections**. Changing a
selection changes the availability map.

### Request

{{ extension_schema_fields('catalog_lookup.json#/$defs/get_product_request', 'catalog') }}

### Response

{{ extension_schema_fields('catalog_lookup.json#/$defs/get_product_response', 'catalog') }}

---

## Transport Bindings

* [REST Binding](rest.md#post-cataloglookup): `POST /catalog/lookup`
* [MCP Binding](mcp.md#lookup_catalog): `lookup_catalog` tool
* [REST Binding](rest.md#post-cataloglookup): `POST /catalog/lookup` (batch)
* [REST Binding](rest.md#post-catalogproduct): `POST /catalog/product` (single)
* [MCP Binding](mcp.md#lookup_catalog): `lookup_catalog` tool (batch)
* [MCP Binding](mcp.md#get_product): `get_product` tool (single)
Loading
Loading