# Ucp

> Use when the user wants to find, compare, buy, or track products from any online merchant. Covers cross-merchant catalog search ("find me X under $Y"), named-merchant transactions ("buy this from Z.com"), and order tracking. Trigger on shopping, products, prices, sellers, carts, checkout, orders, or any commercial intent. Falls back to merchant-hosted handoff when direct in-protocol checkout isn'…

- **Type:** Skill
- **Install:** `agentstack add skill-shopify-ucp-cli-ucp`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [Shopify](https://agentstack.voostack.com/s/shopify)
- **Installs:** 0
- **Category:** [Search](https://agentstack.voostack.com/c/search)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [Shopify](https://github.com/Shopify)
- **Source:** https://github.com/Shopify/ucp-cli/tree/main/skills/ucp
- **Website:** https://shopify.dev/agents

## Install

```sh
agentstack add skill-shopify-ucp-cli-ucp
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# ucp

When a buyer expresses commercial intent — wanting to find, buy, or track products — this is your toolkit. You can search across thousands of merchants via a bundled global catalog, build carts and complete checkouts against any UCP-supporting merchant, and follow up on orders. For merchants that don't support direct transactions, hand off gracefully to the merchant's own flow.

> **Setup**: Run `ucp profile init --name agent` at the start of any session. It's idempotent — re-running with an existing profile no-ops (`created: false`, exit 0) — so call it unconditionally rather than checking state first. This creates the local CLI identity required for all UCP operations; it's not a merchant onboarding step or a Catalog API key. See `references/SETUP.md` for installation paths and `ucp doctor`.

## How to decide what to do

| Buyer says... | Do this |
|---|---|
| "Find me X", "I need X for Y", "what's a good X under $Z" — no merchant named | `ucp catalog search` against the global catalog. Each result names its merchant via `seller.domain`. |
| "Show me this" — buyer pastes a product/variant link or wants a specific product's full PDP/options matrix | `ucp catalog get_product ` — single call, returns `result.product` (singular). Omit `--business` for global Catalog IDs. |
| "Are these still available?" — refreshing prices/stock/validity for known IDs (saved lists, wish lists, stale carts) | `ucp catalog lookup` with the IDs (up to 50). To distinguish OOS from delisted, pass `filters.available: false` — the default filters to in-stock only, so OOS and delisted both look like absence. |
| "Buy this from \" — buyer names a specific merchant | `ucp discover --business ` first; if it succeeds, transact via `--business `. If it fails, the merchant doesn't speak UCP — tell the buyer and offer alternatives. |
| "Track my order" | `ucp order get  --business ` |

**Rule of thumb:** broad product discovery → global catalog (no `--business` needed). Business-scoped operations — cart, checkout, order, or catalog scoped to a specific merchant — → pass `--business `. Reach for one or the other based on the buyer's intent.

## Journey heuristics

- **Broad shopping request** → search immediately with useful context. Don't ask clarifying questions first unless the request is impossible or unsafe.
- **Refinement** ("cheaper", "different brand") → re-run search with a sharper query or filter; don't reuse stale results.
- **Comparison** → lead with the key tradeoff (price vs feature, brand reputation vs cost), then cite concrete fields from the response.
- **Cart** → low-commitment basket assembly. Pass `context` (locality signals: country, region, postal code; optional language/currency preference) on create when known — it lets the merchant localize currency, surface region-specific availability, and apply regional discounts.
- **Checkout** → high-intent. Preserve request-shaped `line_items` on every update (`line_items[].id` targets the existing line; `line_items[].item.id` identifies the item/variant); introspect the merchant's schema before adding fields beyond the basics.
- **Order** → read-only post-purchase status. Summarize fulfillment expectations and tracking events; don't invent return/reorder actions unless the response supports them.

## Introspect first (capabilities + schemas)

The merchant decides what it accepts and what it exposes. These introspection commands save the agent from guessing:

1. **Merchant capabilities** — `ucp discover --business ` returns the operations and tools this merchant exposes (e.g. `create_cart`, `update_checkout`, plus any extensions). Use when the buyer names a specific merchant you don't know, or when you need to confirm a merchant supports an operation before composing it.

2. **Operation input schema** — `ucp  --input-schema --business ` returns the inputSchema for a specific tool from that merchant — including buyer-supplied destination fields, payment methods, discount handling, business-specific extension keys, etc. Use before composing any non-trivial payload (delivery info, payment, discount, fulfillment).

3. **What hits the wire** — `ucp  [args] --dry-run` builds and validates the request, then prints the exact MCP envelope (`tool`, `arguments`, auto-injected `meta.idempotency-key` and `meta.ucp-agent`) without dispatching. Use when debugging a payload, confirming a mutation before issuing it, or learning the protocol shape (e.g. while building your own UCP-aware app). The printed `arguments` are the canonical MCP call; the CLI additionally wraps signing and web-bot-auth at the transport layer — if you build a client that calls MCP directly, you own that wrapping.

The CLI rejects unknown plain keys client-side before sending; if you hit `SCHEMA_VALIDATION_FAILED`, the error's CTA tells you the exact `--input-schema` command to run. Spec-canonical fields (per the UCP `Context` and `Buyer` types) may still be rejected if a specific merchant doesn't advertise them — the merchant's advertised schema is authoritative.

Bundled global catalog operations — `search` for discovery, `lookup` for refreshing saved or bookmarked product/variant IDs (carts, wish lists, deep links), and `get_product` for full PDP detail — take well-known inputs covered below and in `references/CATALOG.md`; you don't need to introspect before basic use. Reach for `--input-schema` when adding extension fields (`like`, signals, etc.), when live schema differs, or when composing checkout payloads.

> `ucp  --schema` is a different thing — it describes the CLI wrapper itself (args/options like `--input`, `--set`, `--business`). Not the payload schema. Use `--input-schema` for payload composition.

## Positional id vs body input

Ops that act on an existing resource take its id as the first positional argument. The id is not a body field — don't duplicate it in `--input`/`--set`.

- `cart get/update/cancel `
- `checkout get/update/complete/cancel `
- `order get `
- `catalog get_product ` (pass `result.products[N].id` from a prior search)

All other operations (`cart create`, `checkout create`, `catalog search`, `catalog lookup`, `discover`) take no positional; their full payload goes in `--input`/`--set`. Cart-to-checkout conversion accepts `cart_id` in the `checkout create` body and requires `line_items`, which can be empty for conversion.

```sh
ucp cart update  --business https:// --input '{...}'
ucp catalog get_product                  # global Catalog detail: omit --business
ucp catalog search --set /query='running shoes'
```

## Searching the global catalog

Compose a search with three field groups. For Catalog-specific recipes — search pages, lookup/re-pricing, PDP variant pickers, multimodal `like`, single-shop `shop_ids`, auth tiers, and ID pitfalls — read `references/CATALOG.md`.

- **`query`** — what the buyer is looking for. The literal search term.
- **`context`** — soft signals that inform ranking, localization, and estimates (not exclusions). Includes `intent` (free-text background, e.g. "looking for a gift under $50" or "durable for outdoor use"), `address_country`, `currency`, `language`, `eligibility`, etc.
- **`filters`** — hard exclusions. Results that don't satisfy these are dropped (price ranges, availability, shipping constraints, condition).
- **`pagination`** — `limit` to bound the page size.

```sh
ucp catalog search --input '{
  "query": "marathon training shoes",
  "context": {
    "intent": "daily trainer for marathon training",
    "address_country": "US",
    "currency": "USD",
    "language": "en-US"
  },
  "filters": {
    "price":     { "max": 15000 },
    "available": true,
    "ships_to":  { "country": "US" }
  },
  "pagination": { "limit": 10 }
}' \
  --view 'result.products[*].{title: title, seller_domain: variants[0].seller.domain, seller_url: variants[0].seller.url, price_from: price_range.min.amount, currency: price_range.min.currency, variant_id: variants[0].id, pdp: variants[0].url, buy: variants[0].checkout_url, rating: rating.value}'
```

`--view ''` projects the response down to the fields you actually need (title/seller/price/routing/handoff URLs in this case) instead of dragging the full variant tree into context. The `cta` survives the projection, so next-step recommendations remain available. Keep `variants[M].id` and `variants[M].seller.domain` in the projection whenever a cart or checkout step might follow. Use `--view :compact` only when a display-only title/price/variant/buy table is enough; use an inline or `@` projection when routing fields must survive. See **Working with responses** below for the projection pattern across cart, checkout, and order responses.

When the buyer mentioned a brand or store name, **read seller identity from `variants[*].seller.domain`, not the brand in `title`**. The same brand can appear from first-party stores and third-party resellers in the same result set; only `seller.domain` distinguishes them.

Don't fabricate context fields you don't have — leave them out. For "more like this" or visual similarity, use `--input '{"like": ...}'` and check `--input-schema` for the exact `like` fields supported.

### Pagination — vary the query first

`catalog search` is the only paginated operation. The response carries `result.pagination` when more pages exist, and the CTA includes the fetch-next command. **Pagination gives more of the same ranking.** When results miss the buyer's intent, vary the query first — try synonyms, broader/narrower terms, brand names — then paginate only if the new query confirms the result set is what you want. Cursors are opaque and may be invalidated as inventory changes; don't hand-roll cursor calls, follow the CTA.

### Looking up a specific product

`catalog search` is the right tool for browsing. When the buyer narrows to a specific product — picking switch/color/size from a multi-variant matrix, or wanting real-time per-variant pricing/availability — use `ucp catalog get_product ` (id is positional; pass `result.products[N].id` from a prior global Catalog search, and omit `--business` unless you intentionally want a merchant-scoped catalog). The response is `result.product` (singular, not `products[]`) and contains the full `options[]` matrix and current variant-level state.

**`get_product` vs `lookup`**: a single pasted link the buyer wants to *open* (PDP + variant picker) → `get_product`. A batch of saved IDs the buyer wants to *refresh* (prices, stock, validity) → `lookup`.

## Working with responses

UCP responses can be rich across every operation — catalog search returns dozens of products with full variant trees, cart can carry itemized totals + fulfillment estimates + messages, checkout carries final totals + full fulfillment options + messages, and order responses carry fulfillment events and adjustments. Across all of them, apply the same tactic: grok the response shape, then project the fields relevant to the current task before reasoning over the result. Loading a 300 KB blob into context just to find five product titles (or five total amounts) wastes most of your budget.

Two equally good options — pick whichever fits the task:

```sh
# Built-in alias: :summary resolves to cart.summary.jmespath for cart commands.
ucp cart create --input '...' --view :summary

# Inline JMESPath still works. The expression runs over the whole envelope;
# output replaces the envelope. CTAs survive the projection.
ucp cart create --input '...' \
  --view "result.{id: id, currency: currency, items: length(line_items), total: totals[?type=='total'] | [0].amount, continue_url: continue_url}"

# Or pipe to jq if you have it (full envelope as default JSON output)
ucp cart create --input '...' --format json \
  | jq '.result | {id, currency, items: (.line_items|length), total: (.totals[]? | select(.type=="total") | .amount), continue_url}'
```

The exact projection depends on what the current task needs — don't paste boilerplate, compose for THIS step. Common shapes by operation:

- **catalog search** — see the example under **Searching the global catalog** above; include `variants[M].id` in your projection whenever a cart or checkout step might follow.
- **cart** — `result.{id, currency, line_items, totals, messages, fulfillment, continue_url}`; cart totals are estimates when fulfillment destination is involved (`totals[?type=='total'] | [0].amount`).
- **checkout** — `result.{id, status, currency, line_items, totals, messages, fulfillment, continue_url}`; checkout is the final/full-fidelity fulfillment option surface. Reach into `messages[?severity=='recoverable']` for actionable errors.
- **order** — `result.{id, status, fulfillment: fulfillment.{status: status, events: events[*].{type: type, at: at, location: location}}}`.

Common JMESPath patterns (filters, sort, multi-select, slicing) are in `references/REFERENCE.md`. Package-local aliases such as `--view :compact` and `--view :summary` resolve from the CLI package's `skills/ucp/views` directory by operation capability (`catalog search --view :summary` → `catalog.summary.jmespath`; `cart create --view :summary` → `cart.summary.jmespath`). Read the files in `views/` as shape references, but **compose your own projection for what THIS call needs**; use `@` for edited/custom views.

### Key response fields and conventions

- **`result.products[N]`** — each product carries `id`, `title`, `description`, `rating`, `options[]` (variant axes like color/size), `price_range` (`{min, max}` across variants — prefer over `variants[0].price` for buyer-facing display), `variants[M]`.
- **`result.products[N].variants[M]`** — each variant carries `id` (merchant-specific format; pass verbatim into cart/checkout — don't reconstruct from URLs), `title`, `url` (PDP — the canonical browse target), `checkout_url` (merchant-hosted buy-now), `price` (object `{amount, currency}`), `availability` (object `{available, ...}` — check `.available`), `seller` (object `{name, domain, url}` — `domain` is the safe key for `--business`; `url` is the homepage for buyer-facing display).
- **`seller.url` is the seller's homepage, NOT a buyer-handoff target.** For handoff use `variants[M].url` (PDP) or `variants[M].checkout_url` (buy-now).
- **Minor currency units** apply to every amount in the response. `15000` = $150.00 USD; `4998` = $49.98 USD. Always check the corresponding `currency` field before formatting for the buyer.
- **Cart/checkout responses** carry pricing in `result.totals[]` (itemized; one `subtotal` + one `total` guaranteed), `result.currency` (resolved ISO 4217), and per-line `result.line_items[N].item.price`. Cart fulfillment lines are estimates; checkout fulfillment lines are the final pre-completion numbers. There is no `result.cost` field.
- **Line identity vs item identity** — `result.line_items[N].id` is the targetable existing line id for updates and fulfillment targeting; `result.line_items[N].item.id` is the underlying item/variant id. Net-new create lines do not have a line id yet — do not invent one.

To get the most up-to-date price, availability, and merchant-specific cart totals, add the product to a cart.

### Fulfillment and line items — quick model

Use this ladder: **`context` hints '` against the merchant's live `--input-schema`. Quote string-valued fields explicitly in JSON so numeric-looking strings (ZIP codes, IDs) stay strings:

```sh
ucp cart create --business https:// --input '{
  "line_items": [{"item":{"id":""},"quantity":1}],
  "context":    {"address_country":"US"}
}'
```

### Checkout

`checkout create` has two modes. **If you already built a cart, prefer cart conversion**: pass the cart result `id` as `cart_id` in the checkout body when `checkout create --input-schema` advertises it, and include `line_items: []`. `line_items` is required but can be empty for cart conversion; the merchant uses the cart contents when `cart_id` is present. Use real `line_items` only for buy-now flows where no cart exists. Do not use cart line I

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [Shopify](https://github.com/Shopify)
- **Source:** [Shopify/ucp-cli](https://github.com/Shopify/ucp-cli)
- **License:** MIT
- **Homepage:** https://shopify.dev/agents

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-shopify-ucp-cli-ucp
- Seller: https://agentstack.voostack.com/s/shopify
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
