-
Notifications
You must be signed in to change notification settings - Fork 267
feat(catalog): Catalog capability for product discovery #55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
igrigorik
wants to merge
20
commits into
main
Choose a base branch
from
feat/catalog-capability
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
a483623
feat(catalog): Catalog capability for product discovery
igrigorik d06a5ae
Extract search filters to standalone schemas
igrigorik 1a2e634
add custom words for spellcheck
igrigorik b5a42dd
rebase on main
igrigorik f471167
split into search and lookup capabilities
igrigorik eae79de
clarify context & market assignment
igrigorik 6878142
feat(context): add language field for content localization
igrigorik e606d3d
Add POST /catalog/lookup endpoint + context query params for GET
igrigorik f92b9db
security hardening, currency support, schema refinements
igrigorik 2d2f096
fix linter issues
igrigorik 7894a60
rename `price` fields on Product for clarity
igrigorik 8bbe148
extensible category schema with taxonomy support
igrigorik 99615b9
Merge branch 'main' into feat/catalog-capability
igrigorik 46d1e48
refactor to POST-only batch lookup API
igrigorik a816a7b
Merge branch 'main' into feat/catalog-capability
igrigorik 828ed00
Remove stale spec/ directory (schemas live in source/)
igrigorik 40369ff
update category examples with multi-taxonomy response
igrigorik 269eefa
fix examples to show price_range on Product
igrigorik 90ecd9d
extract description into reusable type
igrigorik 1db9268
feat: category search filter as an array
igrigorik File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,232 @@ | ||
| <!-- | ||
| Copyright 2026 UCP Authors | ||
|
|
||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||
| you may not use this file except in compliance with the License. | ||
| You may obtain a copy of the License at | ||
|
|
||
| http://www.apache.org/licenses/LICENSE-2.0 | ||
|
|
||
| Unless required by applicable law or agreed to in writing, software | ||
| distributed under the License is distributed on an "AS IS" BASIS, | ||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| See the License for the specific language governing permissions and | ||
| limitations under the License. | ||
| --> | ||
|
|
||
| # Catalog Capability | ||
|
|
||
| ## Overview | ||
|
|
||
| The Catalog capability allows platforms to search and browse business product catalogs. | ||
| 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 | ||
| * 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. | | ||
|
|
||
| ## 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 / | ||
| Large"), price, and availability. | ||
| * **Price**: Price values include both amount (in minor currency units) and | ||
| currency code, enabling multi-currency catalogs. | ||
|
|
||
| ### Relationship to Checkout | ||
|
|
||
| Catalog operations return product and variant IDs that can be used directly in | ||
| checkout `line_items[].item.id`. The variant ID from catalog retrieval should match | ||
| the item ID expected by checkout. | ||
|
|
||
| ## Shared Entities | ||
|
|
||
| ### Context | ||
|
|
||
| Location and market context for catalog operations. All fields are optional | ||
| hints for relevance and localization. Platforms MAY geo-detect context from | ||
| request headers. | ||
|
|
||
| Context signals are provisional—not authorization. Businesses SHOULD use these | ||
| values when authoritative data (e.g., shipping address) is absent, and MAY | ||
| ignore or down-rank them if inconsistent with stronger signals (authenticated | ||
| account, fraud rules, export controls). Eligibility and policy enforcement | ||
| MUST occur at checkout/order time with authoritative data. | ||
|
|
||
| Businesses determine market assignment—including currency—based on context | ||
| signals. Price filter values are interpreted in the business's assigned | ||
| currency; response prices include explicit currency codes. | ||
|
|
||
| {{ schema_fields('types/context', 'catalog') }} | ||
|
|
||
| ### Product | ||
|
|
||
| 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. | ||
|
|
||
| {{ schema_fields('types/product', 'catalog') }} | ||
|
|
||
| ### Variant | ||
|
|
||
| A purchasable item with specific option selections, price, and availability. | ||
|
|
||
| `media` is an ordered array. Businesses SHOULD return the featured variant image | ||
| as the first element. Platforms SHOULD treat the first element as featured. | ||
|
|
||
| {{ schema_fields('types/variant', 'catalog') }} | ||
|
|
||
| ### Price | ||
|
|
||
| {{ schema_fields('types/price', 'catalog') }} | ||
|
|
||
| ### Price Range | ||
|
|
||
| {{ schema_fields('types/price_range', 'catalog') }} | ||
|
|
||
| ### Media | ||
|
|
||
| {{ schema_fields('types/media', 'catalog') }} | ||
|
|
||
| ### Product Option | ||
|
|
||
| {{ schema_fields('types/product_option', 'catalog') }} | ||
|
|
||
| ### Option Value | ||
|
|
||
| {{ schema_fields('types/option_value', 'catalog') }} | ||
|
|
||
| ### Selected Option | ||
|
|
||
| {{ schema_fields('types/selected_option', 'catalog') }} | ||
|
|
||
| ### Rating | ||
|
|
||
| {{ schema_fields('types/rating', 'catalog') }} | ||
|
|
||
| ## Messages and Error Handling | ||
|
|
||
| All catalog responses include an optional `messages` array that allows businesses | ||
| to provide context about errors, warnings, or informational notices. | ||
|
|
||
| ### Message Types | ||
|
|
||
| Messages communicate business outcomes and provide context: | ||
|
|
||
| | Type | When to Use | Example Codes | | ||
| | :--- | :--- | :--- | | ||
| | `error` | Business-level errors | `NOT_FOUND`, `OUT_OF_STOCK`, `REGION_RESTRICTED` | | ||
| | `warning` | Important conditions affecting purchase | `DELAYED_FULFILLMENT`, `FINAL_SALE`, `AGE_RESTRICTED` | | ||
| | `info` | Additional context without issues | `PROMOTIONAL_PRICING`, `LIMITED_AVAILABILITY` | | ||
|
|
||
| **Note**: All catalog errors use `severity: "recoverable"` - agents handle them programmatically (retry, inform user, show alternatives). | ||
|
|
||
| #### Message (Error) | ||
|
|
||
| {{ schema_fields('types/message_error', 'catalog') }} | ||
|
|
||
| #### Message (Warning) | ||
|
|
||
| {{ schema_fields('types/message_warning', 'catalog') }} | ||
|
|
||
| #### Message (Info) | ||
|
|
||
| {{ schema_fields('types/message_info', 'catalog') }} | ||
|
|
||
| ### Common Scenarios | ||
|
|
||
| #### Empty Search | ||
|
|
||
| When search finds no matches, return an empty array without messages. | ||
igrigorik marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| ```json | ||
| { | ||
| "ucp": {...}, | ||
| "products": [] | ||
| } | ||
| ``` | ||
|
|
||
| This is not an error - the query was valid but returned no results. | ||
|
|
||
| #### Backorder Warning | ||
|
|
||
| When a product is available but has delayed fulfillment, return the product with | ||
| a warning message. Use the `path` field to target specific variants. | ||
|
|
||
| ```json | ||
| { | ||
| "ucp": {...}, | ||
| "products": [ | ||
| { | ||
| "id": "prod_xyz789", | ||
| "title": "Professional Chef Knife Set", | ||
| "description": { "plain": "Complete professional knife collection." }, | ||
| "price_range": { | ||
| "min": { "amount": 29900, "currency": "USD" }, | ||
| "max": { "amount": 29900, "currency": "USD" } | ||
| }, | ||
| "variants": [ | ||
| { | ||
| "id": "var_abc", | ||
| "title": "12-piece Set", | ||
| "description": { "plain": "Complete professional knife collection." }, | ||
| "price": { "amount": 29900, "currency": "USD" }, | ||
| "availability": { "available": true } | ||
| } | ||
| ] | ||
| } | ||
| ], | ||
| "messages": [ | ||
| { | ||
| "type": "warning", | ||
| "code": "delayed_fulfillment", | ||
| "path": "$.products[0].variants[0]", | ||
| "content": "12-piece set on backorder, ships in 2-3 weeks" | ||
| } | ||
| ] | ||
| } | ||
| ``` | ||
|
|
||
| Agents can present the option and inform the user about the delay. The `path` | ||
| field uses RFC 9535 JSONPath to target specific components. | ||
|
|
||
| #### Identifiers Not Found | ||
|
|
||
| When requested identifiers don't exist, return success with the found products | ||
| (if any). The response MAY include informational messages indicating which | ||
| identifiers were not found. | ||
|
|
||
| ```json | ||
| { | ||
| "ucp": {...}, | ||
| "products": [], | ||
| "messages": [ | ||
| { | ||
| "type": "info", | ||
| "code": "not_found", | ||
| "content": "prod_invalid" | ||
| } | ||
| ] | ||
| } | ||
| ``` | ||
|
|
||
| Agents correlate results by matching fields in returned products against | ||
| requested identifiers. | ||
|
|
||
| ## Transport Bindings | ||
|
|
||
| The capabilities above are bound to specific transport protocols: | ||
|
|
||
| * [REST Binding](rest.md): RESTful API mapping. | ||
| * [MCP Binding](mcp.md): Model Context Protocol mapping via JSON-RPC. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| <!-- | ||
| Copyright 2026 UCP Authors | ||
|
|
||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||
| you may not use this file except in compliance with the License. | ||
| You may obtain a copy of the License at | ||
|
|
||
| http://www.apache.org/licenses/LICENSE-2.0 | ||
|
|
||
| Unless required by applicable law or agreed to in writing, software | ||
| distributed under the License is distributed on an "AS IS" BASIS, | ||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| See the License for the specific language governing permissions and | ||
| limitations under the License. | ||
| --> | ||
|
|
||
| # Catalog Lookup Capability | ||
|
|
||
| * **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). | ||
|
|
||
| ## Operation | ||
|
|
||
| | Operation | Description | | ||
| | :--- | :--- | | ||
| | **Lookup Catalog** | Retrieve products or variants by identifier. | | ||
|
|
||
| ### Supported Identifiers | ||
|
|
||
| The `ids` parameter accepts an array of identifiers. Implementations MUST support | ||
| lookup by product ID and variant ID. Implementations MAY additionally support | ||
| secondary identifiers such as SKU or handle, provided these are also fields on | ||
| the returned product object. | ||
|
|
||
| Duplicate identifiers in the request MUST be deduplicated. When an identifier | ||
| matches multiple products (e.g., a SKU shared across variants), all matching | ||
| products MUST be returned. When multiple identifiers resolve to the same product, | ||
| it MUST be returned once. The `products` array may contain fewer or more items | ||
| than requested identifiers. | ||
|
|
||
| ### Client Correlation | ||
|
|
||
| The response does not guarantee order or provide explicit identifier-to-product | ||
| mapping. Clients correlate results by matching fields in the returned products | ||
| (e.g., `id`, `sku`, `handle`) against the requested identifiers. | ||
|
|
||
| ### Batch Size | ||
|
|
||
| Implementations SHOULD accept at least 10 identifiers per request. Implementations | ||
| MAY enforce a maximum batch size and MUST reject requests exceeding their limit | ||
| with an appropriate error (HTTP 400 `request_too_large` for REST, JSON-RPC | ||
| `-32602` for MCP). | ||
|
|
||
| ### Resolution Behavior | ||
|
|
||
| For each identifier, the response returns the parent product with full context | ||
| (title, description, media, options): | ||
|
|
||
| * **Product ID lookup**: `variants` MAY contain a representative set. | ||
| * **Variant ID lookup**: `variants` MUST contain only the requested variant. | ||
|
|
||
| When the variant set is large, a representative set MAY be returned based on | ||
| buyer context or other criteria. This ensures agents always have product context | ||
| for display while getting exactly what they requested. | ||
|
|
||
| ### Request | ||
|
|
||
| {{ extension_schema_fields('catalog_lookup.json#/$defs/lookup_request', 'catalog') }} | ||
|
|
||
| ### Response | ||
|
|
||
| {{ extension_schema_fields('catalog_lookup.json#/$defs/lookup_response', 'catalog') }} | ||
|
|
||
| ## Transport Bindings | ||
|
|
||
| * [REST Binding](rest.md#post-cataloglookup): `POST /catalog/lookup` | ||
| * [MCP Binding](mcp.md#lookup_catalog): `lookup_catalog` tool |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.