Как опубликовать /.well-known/ucp: профиль продавца UCP
Опубликуйте /.well-known/ucp, чтобы ИИ-агенты видели, что умеет ваш магазин: 3 обязательных поля, правила хостинга, согласование и breaking changes v2026-08-25.

Чтобы опубликовать профиль продавца Universal Commerce Protocol (UCP), отдайте по адресу /.well-known/ucp один JSON-документ: в нём версия протокола, сервисы с эндпоинтами, возможности (capabilities) и платёжные обработчики. ИИ-агент для покупок сначала забирает этот файл, сверяет его со своим профилем и использует только то, что поддерживают обе стороны (спецификация UCP).
Профиль — точка входа в коммерческий слой стека готовности: прежде чем что-то купить, агент должен узнать, что умеет ваш магазин. Этот гайд — подробный разбор UCP к чек-листу агентной коммерции для магазинов. Сами данные о товарах — задача структурированных данных, а общая картина описана в материале о том, что значит готовность к ИИ-агентам.
Оговорка в самом начале: UCP молод. Shopify и Google опубликовали его 11 января 2026 года (Shopify Engineering, Google Developers Blog), и с тех пор вышло четыре датированных релиза, последний — 25 августа 2026 года (релизы). Поддержка версий неравномерна: 2 октября 2026 года руководство Google для продавцов всё ещё описывало версии профиля 2026-04-08 и 2026-01-23 (Google), тогда как витрины Shopify уже объявляют 2026-08-25 (changelog Shopify).
Ключевые выводы
- Профиль продавца — это JSON-файл на
/.well-known/ucp. Обязательны толькоucp.version,ucp.servicesиucp.payment_handlers, причём последние два должны присутствовать, даже если пусты (спецификация UCP).- Отдавайте его по HTTPS, без редиректов и с
Cache-Control: publicиmax-ageне меньше 60 секунд (спецификация UCP, Hosting). Правил про CORS в спецификации нет; проверка этого заголовка — собственное решение aiSiteReady.- Активные возможности выбирает бизнес: он пересекает свой профиль с профилем агента по точному имени и точной датированной версии, а затем отбрасывает «осиротевшие» расширения.
- Релиз v2026-08-25 сломал профили четырьмя способами:
keys[]заменилsigning_keys[], переименованы флаги конфигурации fulfillment, перестроено согласие покупателя, а платёжные расширения переехали вdev.ucp.common.payment.*(заметки к релизу).- Shopify сам генерирует профиль для своих витрин, и, по словам сотрудников, разработчики не могут его править (форум Shopify); кастомные магазины публикуют свой.
Что такое профиль продавца /.well-known/ucp?
Это документ обнаружения бизнеса в UCP: публичный JSON-файл, который ещё до первого запроса checkout сообщает агенту, что поддерживает ваш магазин. Бизнес публикует его на /.well-known/ucp; платформы — то есть агенты и приложения — публикуют свой профиль по адресу, который передают с каждым запросом, поэтому любая платформа с доступным профилем может работать с любым бизнесом без предварительной регистрации (спецификация UCP).
Профиль — это карта, а не сам API. Все эндпоинты, к которым обратится агент, объявлены внутри него; описание shopping REST прямо требует брать базовый URL из блока services профиля (OpenAPI UCP). Каждый блок отвечает на один вопрос.
Два факта стоит знать, прежде чем вкладываться. Во-первых, в реестре well-known URI у IANA ucp на момент его обновления 16 сентября 2026 года не значился, в отличие от api-catalog и agent-card.json (IANA); путь держится на спецификации, а не на регистрации. Во-вторых, участвовать в разработке может любой, но голоса распределены неравно: в Shopping Tech Council 16 мест, из них по четыре постоянных места у Google и Shopify — восемь голосов из шестнадцати (управление UCP).
UCP — не единственный коммерческий протокол. Он заявляет совместимость с AP2 и включает расширение ap2_mandate для подписанных платежей (UCP и AP2). У Agentic Commerce Protocol от OpenAI и Stripe есть собственный /.well-known/acp.json, но его RFC по обнаружению всё ещё помечен как предложение и не выпущен (ACP RFC). В остальной части гайда я его не касаюсь.
Что внутри профиля UCP: от минимального до полного?
Важны шесть полей объекта, и лишь три из них обязательны. Таблица следует определению business_schema в JSON Schema спецификации на релизе v2026-08-25 (profile.json).
| Поле | Что вы задаёте | Что важно знать |
|---|---|---|
ucp.version | Дата YYYY-MM-DD | Обязательное. Только дата релиза, никаких строк вроде draft |
ucp.supported_versions | Старая дата → URL профиля | Необязательное. Каждый URL отдаёт полный профиль для этой даты |
ucp.services | Ключ dev.ucp.shopping, по записи на транспорт | Обязательное, может быть пустым. Запись: version, transport, endpoint, schema, spec |
ucp.capabilities | Ключ на возможность, массив версионных записей | Необязательное. Запись: version, schema (для бизнеса обязательно), spec, extends, config |
ucp.payment_handlers | Ключ на обработчик, массив записей | Обязательное, может быть пустым. Запись: id, version, spec, schema, config |
keys | Публичные JWK: kid, kty, crv, x, y, alg | Необязательное. Только публичные ключи |
Транспорты — rest, mcp, a2a и embedded. Записям REST, MCP и A2A нужен endpoint; у A2A он указывает на agent card (сделайте сайт вызываемым агентами). Транспорт mcp в UCP — это коммерческий API, описанный файлом OpenRPC, и он не то же самое, что MCP Server Card (MCP Server Card: разбор).
Минимальный документ, проходящий схему, состоит из трёх полей, причём services и payment_handlers присутствуют, даже если пусты (спецификация UCP).
{
"ucp": {
"version": "2026-08-25",
"services": {},
"payment_handlers": {}
}
}
Такой файл проходит валидацию, но ничего не сообщает: в нём нет ни эндпоинта, ни возможностей, а спецификация отмечает, что полезный профиль обычно объявляет хотя бы одну возможность. Минимальный полезный профиль — только для чтения: один REST-сервис и две каталожные возможности, чтобы агент мог искать товары и получать их по идентификатору. Платёжных обработчиков в нём нет, потому что пока ничего не покупают.
{
"ucp": {
"version": "2026-08-25",
"services": {
"dev.ucp.shopping": [
{
"version": "2026-08-25",
"spec": "https://ucp.dev/2026-08-25/specification/overview/",
"transport": "rest",
"endpoint": "https://shop.example.com/ucp/v1",
"schema": "https://ucp.dev/2026-08-25/services/shopping/rest.openapi.json"
}
]
},
"capabilities": {
"dev.ucp.shopping.catalog.search": [
{
"version": "2026-08-25",
"spec": "https://ucp.dev/2026-08-25/specification/shopping/catalog/search",
"schema": "https://ucp.dev/2026-08-25/schemas/shopping/catalog_search.json"
}
],
"dev.ucp.shopping.catalog.lookup": [
{
"version": "2026-08-25",
"spec": "https://ucp.dev/2026-08-25/specification/shopping/catalog/lookup",
"schema": "https://ucp.dev/2026-08-25/schemas/shopping/catalog_lookup.json"
}
]
},
"payment_handlers": {}
}
}
С таким профилем агент превращает поиск товара в POST https://shop.example.com/ucp/v1/catalog/search, потому что пути из OpenAPI дописываются к объявленному эндпоинту. Магазин, который принимает заказы через агента, добавляет checkout, его расширения, жизненный цикл заказа, платёжный обработчик и ключ подписи.
{
"ucp": {
"version": "2026-08-25",
"supported_versions": {
"2026-04-08": "https://shop.example.com/.well-known/ucp/2026-04-08"
},
"services": {
"dev.ucp.shopping": [
{
"version": "2026-08-25",
"spec": "https://ucp.dev/2026-08-25/specification/overview/",
"transport": "rest",
"endpoint": "https://shop.example.com/ucp/v1",
"schema": "https://ucp.dev/2026-08-25/services/shopping/rest.openapi.json"
},
{
"version": "2026-08-25",
"spec": "https://ucp.dev/2026-08-25/specification/overview/",
"transport": "mcp",
"endpoint": "https://shop.example.com/ucp/mcp",
"schema": "https://ucp.dev/2026-08-25/services/shopping/mcp.openrpc.json"
}
]
},
"capabilities": {
"dev.ucp.shopping.checkout": [
{
"version": "2026-08-25",
"spec": "https://ucp.dev/2026-08-25/specification/shopping/checkout",
"schema": "https://ucp.dev/2026-08-25/schemas/shopping/checkout.json"
}
],
"dev.ucp.shopping.fulfillment": [
{
"version": "2026-08-25",
"spec": "https://ucp.dev/2026-08-25/specification/shopping/extensions/fulfillment",
"schema": "https://ucp.dev/2026-08-25/schemas/shopping/fulfillment.json",
"extends": "dev.ucp.shopping.checkout",
"config": {
"multi_destination": [{ "method": "shipping" }],
"method_combinations": [["shipping", "pickup"]]
}
}
],
"dev.ucp.shopping.discount": [
{
"version": "2026-08-25",
"spec": "https://ucp.dev/2026-08-25/specification/shopping/extensions/discount",
"schema": "https://ucp.dev/2026-08-25/schemas/shopping/discount.json",
"extends": "dev.ucp.shopping.checkout"
}
],
"dev.ucp.shopping.order": [
{
"version": "2026-08-25",
"spec": "https://ucp.dev/2026-08-25/specification/shopping/order",
"schema": "https://ucp.dev/2026-08-25/schemas/shopping/order.json"
}
]
},
"payment_handlers": {
"com.example.processor_tokenizer": [
{
"id": "processor_tokenizer",
"version": "2026-08-25",
"spec": "https://example.com/specs/payments/processor_tokenizer",
"schema": "https://example.com/specs/payments/merchant_tokenizer.json",
"available_instruments": [
{
"type": "card",
"constraints": {
"properties": { "brand": { "enum": ["visa", "mastercard"] } }
}
}
]
}
]
}
},
"keys": [
{
"kid": "business_2025",
"kty": "EC",
"crv": "P-256",
"x": "qIVYZVLCrPZHGHjP17CTW0_-D9Lfw0EkjqF7xB4FivA",
"y": "Mc4nN9LTDOBhfoUeg8Ye9WedFRhnZXZJA12Qp0zZ6F0",
"use": "sig",
"alg": "ES256"
}
]
}
Все три профиля я проверил по JSON Schema из репозитория спецификации на теге v2026-08-25 — они проходят. Хосты, обработчик и ключ считайте заглушками: настоящие платёжные обработчики приходят от их провайдеров, а ключ — вашим собственным публичным ключом. Схема отвергает поля приватного ключа вроде d. Fulfillment и discount содержат extends, потому что это расширения, которые работают только поверх checkout.
Одно правило именования сбивает многих. Имена записываются в обратной доменной нотации: dev.ucp.* принадлежит ucp.dev, а com.example.* — example.com. Платформа обязана отвергнуть сущность, у которой origin URL из schema не совпадает с доменом в её имени, и не должна следовать редиректам при загрузке схемы (спецификация UCP, authority binding). Размещайте свои схемы на домене, который вы контролируете.
Кто хостит /.well-known/ucp: Shopify или вы?
На Shopify файл пишет платформа; в кастомном магазине вы пишете и хостите его сами. Документация Shopify для агентов помещает бизнес-профиль на витрину продавца, {shop}.myshopify.com/.well-known/ucp (документация Shopify для разработчиков). Сотрудник Shopify на форуме разработчиков выражается прямее: Shopify генерирует профиль сам, хостить свой не нужно, а поддерживаемого способа править или заменять его нет (форум, июль 2026). Второй ответ добавляет, что разработчики не могут ничего добавить в профиль или изменить в нём (форум, август 2026).
В Справочном центре Shopify сказано, что функция Agentic storefronts по умолчанию включена для подходящих магазинов (Справочный центр Shopify), но про UCP на этой странице ни слова, так что не гадайте, а запросите свой домен. Я так и сделал для одной витрины на Shopify на её собственном домене 2 октября 2026 года. Ответ — прямой 200 с application/json; charset=utf-8, Cache-Control: public, max-age=60, слабым ETag и Access-Control-Allow-Origin: *. Тело в 4,8 КБ прошло ту же проверку по схеме, объявляло версию 2026-08-25, перечисляло два прошлых релиза в supported_versions и содержало MCP-эндпоинт, десять возможностей и три платёжных обработчика, без keys[].
В кастомном магазине вы сами публикуете файл и отвечаете за каждый объявленный в нём эндпоинт, поэтому объявляйте только то, что реально работает. Тема на форуме от января 2026 года сообщает, что у headless-магазина на Shopify профиль нашёлся на домене checkout, а не на домене витрины (форум); документации Shopify о проксировании я не нашёл, так что смотрите, что отдаёт ваш домен.
Правила хостинга коротки и в основном нормативны (спецификация UCP, Hosting).
| Требование | Уровень | Источник и пояснения |
|---|---|---|
| HTTPS | MUST | Спецификация UCP |
| Без редиректов (3xx) на эндпоинте профиля | MUST NOT | Спецификация UCP; платформы за ними не идут |
Cache-Control с public и max-age от 60 | MUST | Спецификация UCP; никаких private, no-store, no-cache |
ETag или Last-Modified | SHOULD | Спецификация UCP: чтобы потребители могли перепроверять кэш |
| Доступность без аутентификации | Обязательно на практике | В руководстве Google сказано, что файл должен быть публично доступен без аутентификации |
Content-Type: application/json | Для профиля не указан | REST-транспорт спецификации требует его для API-трафика; Shopify отдаёт его |
Access-Control-Allow-Origin | Нет в спецификации | Правила про CORS в тексте спецификации я не нашёл; Shopify отдаёт * |
Про CORS: браузер даёт скрипту прочитать кросс-доменный ответ, только если сервер явно разрешил это заголовком Access-Control-Allow-Origin (MDN). Агенту в браузере он нужен, серверному — нет. Обычному GET preflight не нужен, так что обработчик OPTIONS не требуется. Заголовок ничего не стоит, и aiSiteReady его ищет, но это любезность, а не правило спецификации.
Статического файла за nginx достаточно: спецификация отмечает, что документированные профили занимают меньше 5 КиБ. add_header внутри location заменяет заголовки, унаследованные от блока server, так что свои заголовки безопасности повторите здесь.
location = /.well-known/ucp {
alias /var/www/shop/ucp-profile.json;
default_type application/json;
add_header Cache-Control "public, max-age=300";
add_header Access-Control-Allow-Origin "*";
# для статических файлов nginx по умолчанию добавляет ETag и Last-Modified
}
В приложении на Nuxt то же делает маршрут Nitro. Собственные .well-known-документы aiSiteReady — это маршруты Nitro в server/routes/.well-known/, а имя файла ucp.get.ts превращается в маршрут /.well-known/ucp.
// server/routes/.well-known/ucp.get.ts
import profile from "../../data/ucp-profile.json";
export default defineEventHandler((event) => {
setResponseHeader(event, "Content-Type", "application/json");
setResponseHeader(event, "Cache-Control", "public, max-age=300");
setResponseHeader(event, "Access-Control-Allow-Origin", "*");
return profile;
});
Как согласование UCP превращает два профиля в одну сессию?
Решает бизнес. UCP работает по схеме server-selects: бизнес выбирает версию протокола и активные возможности из пересечения двух профилей (Google). Агент хостит собственный профиль по HTTPS-адресу и передаёт этот адрес с каждым запросом — в заголовке UCP-Agent для HTTP или в объекте meta для MCP (спецификация UCP).
POST /ucp/v1/checkout-sessions HTTP/1.1
Host: shop.example.com
UCP-Agent: profile="https://agent.example/profiles/shopping-agent.json"
Content-Type: application/json
Бизнес загружает и проверяет этот профиль, а затем запускает алгоритм пересечения (спецификация UCP).
- Оставьте возможность бизнеса, только если агент объявил возможность с тем же именем.
- Из версий, которые есть у обеих сторон, выберите самую позднюю. Если общей версии нет, возможность отбрасывается.
- Удалите расширения, у которых ни один из родителей из
extendsне попал в результат. - Повторяйте удаление, пока удалять больше нечего.
Версии сравниваются по точному равенству, а не по порядку дат. Более ранняя версия не подразумевает совместимости с более поздними, и старые релизы dev.ucp.* отдаются только через профили из supported_versions, а не дополнительными записями в текущем профиле (спецификация UCP, versioning). Бизнес отвечает согласованным набором, так что агент всегда знает, что активно.
{
"ucp": {
"version": "2026-08-25",
"capabilities": {
"dev.ucp.shopping.checkout": [{ "version": "2026-08-25" }],
"dev.ucp.shopping.fulfillment": [{ "version": "2026-08-25" }]
}
}
}
Если согласование не удалось, ошибка будет конкретной и может содержать continue_url, чтобы агент передал покупателя в ваш обычный веб-checkout (спецификация UCP, error handling).
| Код | Значение | Статус REST |
|---|---|---|
invalid_profile_url | URL профиля искажён, отсутствует или не разрешается | 400 |
profile_unreachable | Адрес разрешился, но загрузка не удалась или вернула не-2xx | 424 |
profile_malformed | Невалидный JSON или нарушение схемы | 422 |
version_unsupported | Версия протокола агента не поддерживается | 422 |
capabilities_incompatible | Пересечение пустое | 200 |
Что изменилось в релизе UCP v2026-08-25?
Четыре датированных релиза — 2026-01-11, 2026-01-23, 2026-04-08 и 2026-08-25 (релизы). В заметках к последнему релизу перечислено несколько обратно несовместимых изменений (заметки к релизу); вот те, что касаются профиля.
| Область | До (2026-04-08) | После (2026-08-25) |
|---|---|---|
| Ключи подписи | signing_keys[] верхнего уровня | keys[] верхнего уровня, JWK Set; signing_keys[] удалён |
| Конфигурация fulfillment | allows_multi_destination, allows_method_combinations | multi_destination (массив объектов), method_combinations |
| Согласие покупателя | Фиксированные булевы поля вроде marketing | Словарь с ключами-целями в обратной доменной нотации, например dev.ucp.consent.marketing |
| Платёжные расширения | Имена dev.ucp.shopping.* | dev.ucp.common.payment.*, например ap2_mandate |
| Контракт версий | Датированные версии, правила менее явные | Уточнён и ужесточён: каждая запись dev.ucp.* объявляет версию профиля |
Последняя строка названа уточнением, а не breaking change, но последствия у неё серьёзные: платформа обязана отвергать любую запись dev.ucp.*, версия которой отличается от ucp.version (спецификация UCP). Переименование в fulfillment — то, что видно прямо в профиле: сначала в том виде, как его показывает спецификация 2026-04-08, затем как 2026-08-25.
{
"config": {
"allows_multi_destination": { "shipping": true },
"allows_method_combinations": [["shipping", "pickup"]]
}
}
{
"config": {
"multi_destination": [{ "method": "shipping" }],
"method_combinations": [["shipping", "pickup"]]
}
}
Путь обновления короткий. Отдавайте новую версию на /.well-known/ucp. Прошлый релиз храните как полный профиль на собственном URL, например /.well-known/ucp/2026-04-08, и сошлитесь на него в supported_versions; у такого листового профиля собственного supported_versions быть не должно (спецификация UCP, protocol version). Поднимайте версии всех записей dev.ucp.* вместе с ucp.version. Чтобы заранее узнавать о breaking changes, следите за репозиторием: такие pull request анонсируют в GitHub Discussions за две недели до слияния (версионирование UCP).
Какие ошибки ломают профиль UCP?
Большинство сбоев укладывается в восемь паттернов, причём самые неприятные проходят валидацию по схеме: схема допускает неизвестные поля, а правила версий описаны только в тексте спецификации.
| Ошибка | Что происходит | Исправление |
|---|---|---|
| Копирование примера начала 2026 года | capabilities как массив с полями name и payment.handlers верхнего уровня не проходят текущую схему | Берите актуальный пример из спецификации |
Ключи остались в signing_keys[] | Схема пропускает, но верификатор 2026-08-25 читает keys[] | Перенесите ключи |
Поднята только ucp.version | Схема пропускает; платформа отвергает каждую запись dev.ucp.* с другой версией | Поднимайте все записи вместе |
Нет payment_handlers в магазине только с каталогом | Ошибка схемы: поле обязательно | Публикуйте "payment_handlers": {} |
Редирект с /.well-known/ucp | Спецификация запрещает редиректы на эндпоинте профиля | Отдавайте прямой 200 |
no-store, private, крошечный max-age или HTML со статусом 200 от fallback приложения | Нарушены правила кэширования либо тело не JSON | Исправьте заголовки; для отсутствующего файла верните 404 |
Origin schema не совпадает с именем | Сущность отвергается и никогда не активируется | Размещайте схемы на соответствующем домене |
| Защита от ботов или логин перед файлом | Google требует, чтобы файл был публичным; сканеры и агенты видят 403 или 429 | Исключите этот путь из проверок |
Первую строку легко проверить. В примере из Google Developers Blog за январь 2026 года capabilities был массивом объектов с name, а payment.handlers лежал вне ucp (Google Developers Blog). Текущая схема выдаёт на ней ошибки: capabilities не проходит проверку на объект, services — на массив, а payment_handlers отсутствует. Примеры стареют; копируйте из спецификации своего релиза.
Как проверить профиль через curl?
Сначала заголовки, потом тело, и обе проверки запускайте на публичном edge-URL, а не только по localhost. Не добавляйте -L: редирект на этом пути — баг, а -L его скроет.
curl -sS -D - -o /dev/null -H 'Accept: application/json' https://shop.example.com/.well-known/ucp
Должны быть прямой 200, JSON-тип и заголовки кэширования.
HTTP/2 200
content-type: application/json; charset=utf-8
cache-control: public, max-age=300
etag: "7f3a1c"
access-control-allow-origin: *
Затем прочитайте то, что прочитал бы агент. Эта выборка выводит версию, поддерживаемые прошлые релизы и ключи реестров.
curl -sS https://shop.example.com/.well-known/ucp | jq '{
version: .ucp.version,
older: (.ucp.supported_versions // {} | keys),
services: (.ucp.services | keys),
capabilities: (.ucp.capabilities | keys),
handlers: (.ucp.payment_handlers | keys),
keys: [.keys[]?.kid]
}'
Для полного профиля выше вывод выглядит так.
{
"version": "2026-08-25",
"older": ["2026-04-08"],
"services": ["dev.ucp.shopping"],
"capabilities": [
"dev.ucp.shopping.checkout",
"dev.ucp.shopping.discount",
"dev.ucp.shopping.fulfillment",
"dev.ucp.shopping.order"
],
"handlers": ["com.example.processor_tokenizer"],
"keys": ["business_2025"]
}
Встройте эти правила в CI, чтобы сломанный профиль не попал в релиз. Скрипт проверяет статус, заголовки, обязательные поля, переименование в keys[] и расхождение версий. Я прогнал его на локальном сервере с корректным профилем и пятью сломанными вариантами — он отметил каждый.
#!/usr/bin/env bash
# использование: check-ucp.sh https://shop.example.com
set -uo pipefail
URL="${1:?usage: check-ucp.sh https://shop.example.com}"
URL="${URL%/}/.well-known/ucp"
HDR="$(mktemp)"; BODY="$(mktemp)"; fail=0
bad() { echo "FAIL: $1"; fail=1; }
# Без -L намеренно: спецификация запрещает редиректы на эндпоинте профиля.
CODE="$(curl -sS -m 15 -H 'Accept: application/json' -D "$HDR" -o "$BODY" -w '%{http_code}' "$URL")" \
|| { echo "FAIL: request error"; exit 1; }
[ "$CODE" = "200" ] || { echo "FAIL: status $CODE (expected a direct 200)"; exit 1; }
grep -qi '^content-type: *application/json' "$HDR" || bad "Content-Type is not application/json"
CC="$(grep -i '^cache-control:' "$HDR" | head -1)"
grep -qi 'public' <<<"$CC" || bad "Cache-Control lacks public"
grep -qiE 'private|no-store|no-cache' <<<"$CC" && bad "forbidden Cache-Control directive"
MAXAGE="$(grep -oi 'max-age=[0-9]*' <<<"$CC" | head -1 | cut -d= -f2)"
[ "${MAXAGE:-0}" -ge 60 ] || bad "max-age is missing or below 60"
grep -qiE '^(etag|last-modified):' "$HDR" || echo "WARN: no ETag or Last-Modified"
grep -qi '^access-control-allow-origin:' "$HDR" || echo "WARN: no CORS header (not in the spec)"
jq -e 'type == "object"' "$BODY" >/dev/null 2>&1 || { bad "body is not a JSON object"; exit 1; }
V="$(jq -r '.ucp.version // empty' "$BODY")"
[[ "$V" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]] || bad "ucp.version is missing or not YYYY-MM-DD"
jq -e '.ucp.services | type == "object"' "$BODY" >/dev/null 2>&1 || bad "ucp.services must be an object"
jq -e '.ucp.payment_handlers | type == "object"' "$BODY" >/dev/null 2>&1 \
|| bad "ucp.payment_handlers must exist, even when empty"
if [[ ! "$V" < "2026-08-25" ]]; then
jq -e 'has("signing_keys") | not' "$BODY" >/dev/null || bad "signing_keys was removed; publish keys[]"
fi
# Каждая запись dev.ucp.* обязана нести версию самого профиля.
stale() { jq -r --arg v "$V" --arg f "$1" \
'[(.ucp[$f] // {}) | to_entries[] | select(.key | startswith("dev.ucp.")) | .key as $k | .value[] | select(.version != $v) | $k] | unique | join(", ")' "$BODY"; }
CAPS="$(stale capabilities)"; SVCS="$(stale services)"
[ -z "$CAPS" ] || bad "capabilities not at ucp.version $V: $CAPS"
[ -z "$SVCS" ] || echo "WARN: services not at ucp.version $V: $SVCS"
[ "$fail" = 0 ] && echo "OK: $URL ($V)"
exit "$fail"
Для полной структурной проверки валидируйте тело по source/schemas/profile.json, определение business_schema, из репозитория спецификации на теге вашего релиза — любым валидатором JSON Schema 2020-12. Ни скрипт, ни схема не доказывают, что ваши эндпоинты работают. Прежде чем полагаться на профиль, вызовите каждый объявленный эндпоинт с тестовым профилем агента.
Как проверить весь магазин сразу?
С этим файлом работают две коммерческие проверки aiSiteReady. Обе запускаются в профилях сканирования Интернет-магазин и Все проверки и отключены в профилях Контентный сайт и API/приложение. Запросы только читают и защищены от SSRF. Точные проверки и веса описаны на странице методологии.
Проверка «Сигналы агентной коммерции» (agentic_commerce_signals) запрашивает вашу главную страницу и, только если она вернула 2xx, параллельно опрашивает /.well-known/ucp и /.well-known/acp.json с заголовком Accept, в котором приоритет у application/json, — всего не больше трёх запросов. Документ UCP считается валидным, если ответ имеет статус 2xx и JSON-тип, а тело разбирается в JSON-объект с ucp.version в виде строки YYYY-MM-DD. Валидный документ с заголовком Access-Control-Allow-Origin — это PASS; без этого заголовка проверка ставит PARTIAL. Отсутствующий документ никогда не превращает проверку в FAIL. Если главная отвечает 403 или 429, проверка сообщает UNKNOWN, а не засчитывает вам FAIL. Сайту без коммерческой поверхности ставится N/A.
Пробу я сознательно сделал узкой, поэтому знайте её границы. Она проверяет только ucp.version, а не сервисы, возможности, ключи и правила версий выше. Она терпима к версиям: сканер, зашивший в код сегодняшний релиз, после следующего начал бы ошибочно браковать валидные профили. Она не вызывает объявленные вами эндпоинты. Она следует до пяти редиректов, поэтому может засчитать профиль, который по спецификации не должен редиректить; продолжайте проверять curl без -L. Правило про CORS — наше, а не спецификации. Проверка «Подсказки товарного фида» (product_feed_hints) запрашивает тот же путь и считает любой JSON-объект подсказкой обнаружения каталога. Обе проверки имеют низкую важность, так что отсутствие профиля сдвигает оценку умеренно.
Сочетайте этот гайд с чек-листом и материалом о структурированных данных (ссылки на них — в начале статьи), а остальной сайт проверьте с помощью 30-минутного аудита готовности к агентам. Запустите бесплатное сканирование, чтобы увидеть, может ли агент найти и прочитать ваш /.well-known/ucp, какие коммерческие сигналы он видит и какие исправления сделать первыми — на английском, украинском или русском.
Если коротко: для UCP нужен один публичный JSON-файл, доступный по HTTPS, без редиректов и с настоящими заголовками кэширования. В нём должны быть только те эндпоинты и возможности, которые вы реально обслуживаете, а его даты — согласовываться друг с другом. Если вы на Shopify, проверьте, что уже отдаётся. Если нет — опубликуйте профиль сами и тестируйте его как любой другой API.
У IMozz 20 лет в разработке ПО, а последний год он строит продукты с помощью LLM. Он развивает aiSiteReady, сканер только для чтения, который проверяет, может ли ИИ-агент прочитать сайт. Сканер серверно рендерит собственный контент как рабочий пример.