UCP Profile for Magento 2 — module reference
UCP Profile publishes a Universal Commerce Protocol business profile at /.well-known/ucp. AI shopping agents — Google AI Mode and Gemini, and other UCP platforms — read it to learn what your store can do and where to call it.
The profile follows UCP spec 2026-08-25 and its official JSON Schemas. On its own, this module only describes the store. The modules that serve the capabilities are UCP Catalog (search and lookup) and UCP Checkout (checkout and orders).
What it does
- Builds the profile: services, capabilities, extensions, payment handlers, supported versions and public keys.
- Publishes signing keys in the top-level **
keys[]JWK Set (RFC 7517), public keys only**. ES256, ES384 and Ed25519 are supported, with RFC 7638 thumbprint key ids and rotation without downtime. - Checks authority binding locally. Since 2026-08-25 a platform must reject an entity whose
schemahost does not match its namespace — and it does so silently.angeo:ucp:validateruns the same algorithm, so you find out first. - Advertises REST by default and, optionally, an MCP binding.
- Verifies RFC 9421 signatures on requests that arrive at your UCP endpoints (2.1.0).
How the endpoint is served
/.well-known/ucp is served by a PHP controller, not a static file. The spec needs Content-Type: application/json and CORS headers, which a static file without an extension cannot send without web server changes.
The stock Magento nginx config ends with try_files $uri $uri/ /index.php$is_args$args, so the request reaches Magento and a custom router answers it. No nginx or Apache change is needed.
If your host added a rule such as location ~ /\. { deny all; }, it blocks all dot-paths. Then add a one-line allow for ^~ /.well-known/. Do not leave a static file at pub/.well-known/ucp: nginx would serve it first, with the wrong type and no CORS.
The controller sends CORS headers, Cache-Control: public, max-age=300, X-Content-Type-Options: nosniff, X-Frame-Options: DENY and Referrer-Policy: no-referrer. It answers OPTIONS with 204. When the module is off, it returns 404, so the store does not advertise UCP.
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 |
| PHP extensions | curl, json, openssl |
Installation
composer require angeo/module-ucp
bin/magento module:enable Angeo_Ucp
bin/magento setup:upgrade
bin/magento setup:di:compile # production mode only
bin/magento cache:flush
Generate a signing key and check the endpoint:
bin/magento angeo:ucp:keys:generate
curl -sI https://yourstore.com/.well-known/ucp
# HTTP/2 200
# content-type: application/json
# access-control-allow-origin: *
# cache-control: public, max-age=300
Capabilities: declared and served
You switch capabilities on in Stores → Configuration → Angeo → UCP (Universal Commerce Protocol). Since 2.2.0, a switch alone does not advertise cart, checkout, order, fulfillment, discount or buyer consent. They appear in the profile only when an installed module serves them:
| Capability | Served by |
|---|---|
dev.ucp.shopping.catalog.search / .lookup | UCP Catalog |
dev.ucp.shopping.checkout + .discount + .fulfillment | UCP Checkout 1.0+ (MCP tools: UCP Checkout over MCP) |
dev.ucp.shopping.order (Get Order and webhooks) | UCP Checkout 1.1+ |
dev.ucp.shopping.permalink | A stock Magento cart URL |
| Cart, buyer consent | Not available yet |
bin/magento angeo:ucp:validate lists switches that are on but have no effect. A module registers what it serves through the implementations argument of Angeo\Ucp\Model\Config in its di.xml.
REST endpoint. Leave Transport → REST Endpoint URL empty to advertise {baseUrl}/ucp/v1, where the catalog and checkout modules answer.
Signing keys
| Command | What it does |
|---|---|
bin/magento angeo:ucp:keys:generate | Generate a UCP signing keypair. The public JWK is saved to config |
bin/magento angeo:ucp:validate | Validate the generated UCP profile against the |
Which key type to choose:
- ES256 (default) — accepted by every counterparty. AP2 mandate signing needs it. Start here.
- Ed25519 — recommended by the spec for Web Bot Auth on HTTP. Needs
ext-sodium. It does not cover AP2, so publish both if you sign mandates. - ES384 — for a stricter curve policy.
Rotate in this order, so there is no gap:
bin/magento angeo:ucp:keys:generate --add # publish the new key next to the old one
# move signing to the new key id, let caches expire
bin/magento angeo:ucp:keys:generate --force # keep only the newest key
The private key never leaves the server. The profile reads public keys only. If a private JWK member (d, p, q, dp, dq, qi, oth, k) is found in stored config, it is removed before serving, with a warning to rotate.
Inbound signature verification (2.1.0)
The profile publishes keys. Since 2.1.0 the module also checks RFC 9421 signatures on requests that arrive at your UCP endpoints. Set the mode under Request Security:
| Mode | Signed and valid | Signed and invalid | Unsigned |
|---|---|---|---|
disabled (default) | served | served | served |
optional | served | 401 | served |
required | served | 401 | 401 |
Start with optional. Invalid signatures are refused, but missing ones are allowed, so you can see in the log which agents sign. Endpoint modules use this through Api\SignatureVerifierInterface. Signing your own responses is not done yet.
Configuration reference
All settings are under Stores → Configuration → Angeo → UCP (Universal Commerce Protocol). The table shows each field, its config path (for bin/magento config:set) and its default value.
General (1)
| Setting | Config path | Default |
|---|---|---|
| Advertise UCP Profile | angeo_ucp/general/enabled | No |
Declared Root Capabilities (9)
| Setting | Config path | Default |
|---|---|---|
| Catalog Search (dev.ucp.shopping.catalog.search) | angeo_ucp/capabilities/catalog_search_enabled | No |
| Catalog Lookup (dev.ucp.shopping.catalog.lookup) | angeo_ucp/capabilities/catalog_lookup_enabled | No |
| Cart (dev.ucp.shopping.cart) | angeo_ucp/capabilities/cart_enabled | No |
| Checkout (dev.ucp.shopping.checkout) | angeo_ucp/capabilities/checkout_enabled | No |
| Order (dev.ucp.shopping.order) | angeo_ucp/capabilities/order_enabled | No |
| Identity Linking (dev.ucp.common.identity_linking) | angeo_ucp/capabilities/identity_linking_enabled | No |
| Identity Linking OAuth Scopes | angeo_ucp/capabilities/identity_linking_scopes | — |
| Permalink (dev.ucp.shopping.permalink) | angeo_ucp/capabilities/permalink_enabled | No |
| Permalink Endpoint URL | angeo_ucp/capabilities/permalink_endpoint | — |
Declared Extensions (3)
| Setting | Config path | Default |
|---|---|---|
| Fulfillment (dev.ucp.shopping.fulfillment) | angeo_ucp/extensions/fulfillment_enabled | No |
| Discount (dev.ucp.shopping.discount) | angeo_ucp/extensions/discount_enabled | No |
| Buyer Consent (dev.ucp.shopping.buyer_consent) | angeo_ucp/extensions/buyer_consent_enabled | No |
Transport (2)
| Setting | Config path | Default |
|---|---|---|
| REST Endpoint URL | angeo_ucp/transport/rest_endpoint | — |
| MCP Endpoint URL | angeo_ucp/transport/mcp_endpoint | — |
Signing Keys (1)
| Setting | Config path | Default |
|---|---|---|
| Public JWK Set (read-only) | angeo_ucp/keys/signing_key_jwk | — |
Advanced Profile Declarations (2)
| Setting | Config path | Default |
|---|---|---|
| Supported Versions (JSON) | angeo_ucp/advanced/supported_versions | — |
| Payment Handlers (JSON) | angeo_ucp/advanced/payment_handlers | — |
Request Security (1)
| Setting | Config path | Default |
|---|---|---|
| Inbound Signature Verification | angeo_ucp/security/inbound_signature_mode | disabled |
Upgrading from 1.x
1.4.0 still works: it publishes a valid 2026-04-08 profile. 2.x adopts 2026-08-25, where the protocol version, the key field and the capability URLs move together.
| 1.x (2026-04-08) | 2.x (2026-08-25) | |
|---|---|---|
| Signing keys | signing_keys[] | **keys[]** (JWK Set) |
Capability schema | optional | required |
schema origin check | prefix on ucp.dev | authority-binding algorithm |
| Key types | P-256 only | P-256, P-384, Ed25519 |
| Key id | random | RFC 7638 thumbprint |
composer require angeo/module-ucp:^2.0
bin/magento setup:upgrade && bin/magento cache:flush
bin/magento angeo:ucp:validate
Existing keys keep working and move to keys[]. If a platform already integrates with your old profile, save a copy and declare it under Advanced → Supported Versions.
FAQ
Is the profile public?
Yes. The UCP spec requires a public, unauthenticated profile. Never put secrets, internal URLs or admin contacts in it.
Why does my profile not list checkout even though I switched it on?
Since 2.2.0 a capability appears only when an installed module serves it. Install UCP Checkout, then run angeo:ucp:validate.
Do I need to change nginx?
Not with the stock Magento config. Only if your host added a rule that blocks paths starting with a dot. Then allow /.well-known/.
Which key type should I use?
Start with ES256. Add Ed25519 if you want Web Bot Auth interop. Publish both if you sign AP2 mandates.
My CDN still shows the old profile. Why?
The profile is cacheable for five minutes. Purge /.well-known/ucp on your CDN or Varnish after rotating keys or changing settings.
Related
- UCP Catalog — catalog.search and catalog.lookup endpoints behind the UCP profile
- UCP Checkout — UCP checkout sessions over REST; the buyer pays on your own checkout
- UCP Checkout over MCP — the same UCP checkout as five MCP tools
- MCP Server — live, read-only catalog tools for AI agents over MCP
- AI Agent Activity — report on AI agent calls, crawler visits, fake bots and agent-built orders
- UCP for Magento 2 — complete guide
- UCP — definition
- All module reference pages · Compatibility matrix
This page was written from the README.md, composer.json and etc/*.xml files of tag 2.2.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.