# UCP Catalog for Magento 2  -  module reference

> UCP catalog.search, catalog.lookup and get_product for Magento 2 (spec 2026-08-25): variants, option narrowing, price and category filters.

Source: https://angeo.dev/docs/modules/ucp-catalog/
Updated: 2026-10-17

Module reference

# UCP Catalog for Magento 2  -  module reference

Covers `angeo/module-ucp-catalog` 2.1.1 - Checked 16 October 2026 - MIT licensed

**UCP Catalog** is the part that answers. [UCP Profile](https://angeo.dev/docs/modules/ucp/) 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.

Package`angeo/module-ucp-catalog`Latest version2.1.1 (released 2026-09-15)Magento2.4.6+PHP8.1 - 8.5LicenseMIT, freeLinks[GitHub](https://github.com/angeo-dev/module-ucp-catalog) - [Packagist](https://packagist.org/packages/angeo/module-ucp-catalog) - [Changelog](https://github.com/angeo-dev/module-ucp-catalog/blob/main/CHANGELOG.md) - [Module page](https://angeo.dev/modules/ucp-catalog/)

**On this page**

1. Endpoints
2. Requirements
3. Installation
4. Variants
5. Narrowing options with get_product
6. Filters
7. Smoke test
8. Request signatures (2.1.0)
9. Not done yet
10. FAQ
11. Related

## 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](https://angeo.dev/docs/modules/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)**:

1. Turn on the profile and declare **Catalog Search** and **Catalog Lookup**.
2. Leave *Transport -> REST Endpoint URL* empty, or set it to `https://yourstore.com/ucp/v1`, so the profile advertises the paths this module serves.
3. 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`**  -  `min` and `max` in ISO 4217 **minor units**, in `context.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 in `product.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](https://angeo.dev/docs/modules/ucp/)  -  a signed /.well-known/ucp profile for UCP agents
- [UCP Checkout](https://angeo.dev/docs/modules/ucp-checkout/)  -  UCP checkout sessions over REST; the buyer pays on your own checkout
- [MCP Server](https://angeo.dev/docs/modules/mcp-server/)  -  live, read-only catalog tools for AI agents over MCP
- [UCP for Magento 2  -  complete guide](https://angeo.dev/magento-ucp/)
- [All module reference pages](https://angeo.dev/docs/modules/) - [Compatibility matrix](https://angeo.dev/docs/compatibility/)

This page was written from the `README.md`, `composer.json` and `etc/*.xml` files of [tag 2.1.1](https://github.com/angeo-dev/module-ucp-catalog/tree/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.
