UCP Catalog for Magento 2 — module reference
UCP Catalog is the part that answers. UCP Profile tells agents that your store supports catalog search and lookup. This module serves those calls, following UCP spec 2026-08-25, so an agent that finds your store through /.well-known/ucp can actually shop it.
Endpoints
| Method and path | Operation | Capability |
|---|---|---|
POST /ucp/v1/catalog/search | search_catalog | dev.ucp.shopping.catalog.search |
POST /ucp/v1/catalog/lookup | lookup_catalog | dev.ucp.shopping.catalog.lookup |
POST /ucp/v1/catalog/product | get_product | dev.ucp.shopping.catalog.lookup |
Responses are checked in CI against the official UCP JSON Schemas.
Requirements
| Magento | 2.4.6+ (Open Source, Mage-OS or Adobe Commerce) — magento/framework >=103.0.6 <104 |
|---|---|
| PHP | 8.1 – 8.5 — ~8.1.0||~8.2.0||~8.3.0||~8.4.0||~8.5.0 |
| Other angeo modules | angeo/module-ucp ^2.1 |
| PHP extensions | json, mbstring |
Magento_ConfigurableProduct is suggested, not required. Without it, products fall back to one variant each.
Installation
composer require angeo/module-ucp-catalog
bin/magento module:enable Angeo_UcpCatalog
bin/magento setup:upgrade
bin/magento setup:di:compile # production mode only
bin/magento cache:flush
Then, in Stores → Configuration → Angeo → UCP (Universal Commerce Protocol):
- Turn on the profile and declare Catalog Search and Catalog Lookup.
- Leave Transport → REST Endpoint URL empty, or set it to
https://yourstore.com/ucp/v1, so the profile advertises the paths this module serves. - Run
bin/magento angeo:ucp:validate.
This module has no settings of its own.
Variants
In UCP a product has variants, and the variant is what you buy: it has its own price, stock and options. A Magento configurable product has the same shape, and the module maps it that way:
Classic Shirt price_range 29.00 – 32.50 EUR
├─ SHIRT-BLUE-S 29.00 in_stock Color: Blue, Size: Small
├─ SHIRT-BLUE-L 32.50 in_stock Color: Blue, Size: Large
└─ SHIRT-RED-S 29.00 out_of_stock Color: Red, Size: Small
Narrowing options with get_product
POST /catalog/product is where option selection happens. preferences lists option names in the order they may be relaxed — the server drops options from the end of the list first.
curl -s -X POST https://yourstore.com/ucp/v1/catalog/product \
-H 'Content-Type: application/json' \
-d '{
"id": "gid://magento/Product/10",
"selected": [{"name":"Color","label":"Red"},{"name":"Size","label":"Large"}],
"preferences": ["Color", "Size"]
}'
Red/Large does not exist above, so Size is relaxed and SHIRT-RED-S comes back, with a message that says what changed. Send ["Size","Color"] and you get SHIRT-BLUE-L.
Each option value says if it exists and if it is available:
exists | available | |
|---|---|---|
| Red / Small | true | false — sold, but out of stock |
| Red / Large | false | false — never made |
These are different facts. Mixing them up is how an agent tells a shopper something is “unavailable” when it never existed.
Filters
- **
price** —minandmaxin ISO 4217 minor units, incontext.currency. If that currency differs from the store’s and no conversion exists, the filter is skipped and the response says so. - **
categories** — OR logic, matched without case against the labels inproduct.categories.
signals and context.intent are accepted and ignored. Acting on them means relevance ranking, and a LIKE query dressed up as ranking would be worse than an honest search.
Smoke test
curl -s -X POST https://yourstore.com/ucp/v1/catalog/search \
-H 'Content-Type: application/json' \
-d '{"query":"shirt","pagination":{"limit":5},"filters":{"price":{"max":15000}}}' | python3 -m json.tool
curl -s -X POST https://yourstore.com/ucp/v1/catalog/lookup \
-H 'Content-Type: application/json' \
-d '{"ids":["YOUR-SKU"]}' | python3 -m json.tool
Expect ucp.version = 2026-08-25, ucp.status = success, products[] with price_range in minor units, and pagination.total_count. With the module or capability off you get a spec-shaped 404 with ucp.status = error.
Request signatures (2.1.0)
All three endpoints check RFC 9421 signatures, controlled by Request Security → Inbound Signature Verification in the UCP Profile settings. The check runs before any catalog work. Rejections return 401 with a spec-shaped error. The default is disabled, so an upgrade changes nothing until you turn it on.
Not done yet
Signing responses (Signature, Signature-Input, Content-Digest). The optional policies and actions response members of 2026-08-25 are not sent either.
FAQ
Do I need UCP Profile as well?
Yes. The profile tells agents the endpoints exist, and this module requires it. Both move together, because the endpoints read the protocol version from the profile module.
Are configurable products supported?
Yes. Each child becomes a variant with its own price, stock and option values, and the parent shows the real price range.
Why is my price filter ignored?
The filter uses minor units in the request currency. If that currency is not one the store can convert to, the filter is skipped and the response includes a message saying so.
Does it rank results by relevance?
No. It runs Magento’s catalog search and is honest about it. Signals and intent are accepted but not used for ranking.
Is any setup needed in this module?
No. Turn on Catalog Search and Catalog Lookup in the UCP Profile settings and check with angeo:ucp:validate.
Related
- UCP Profile — a signed /.well-known/ucp profile for UCP agents
- UCP Checkout — UCP checkout sessions over REST; the buyer pays on your own checkout
- MCP Server — live, read-only catalog tools for AI agents over MCP
- UCP for Magento 2 — complete guide
- All module reference pages · Compatibility matrix
This page was written from the README.md, composer.json and etc/*.xml files of tag 2.1.1, read on 16 October 2026. Settings, commands and defaults come straight from that code. If a newer release is out, the README on GitHub wins.