# shop
`shop` lets agents search millions of stores, build carts, check out and pay with the shopper's Shop account, and track orders. When a store has no agent checkout, the shopper can approve a purchase on Shop and the agent gets a one-time credential to pay at the site's own checkout.
## Contents
- [Install](#install)
- [Search for products](#search-for-products)
- [Sign in for personalization and accelerated checkout](#sign-in-for-personalization-and-accelerated-checkout)
- [Read the shopper's addresses and payment methods](#read-the-shoppers-addresses-and-payment-methods)
- [Create a pre-filled checkout](#create-a-pre-filled-checkout)
- [Complete the checkout](#complete-the-checkout)
- [Track the order](#track-the-order)
- [Approve a browser purchase](#approve-a-browser-purchase)
- [Direct MCP integration](#direct-mcp-integration)
- [Reference](#reference)
## Install
```sh
npm install --global @shopify/shop@latest
shop skill --install
```
Installs the `shop` command, then teaches your agent when and how to use it. Then ask your agent:
> Find three rain jackets under US$100 that ship to the US.
A full purchase, from search to tracking, takes five commands:
```sh
shop search "trail running shoes" --ships-to US --limit 3
shop cart create --shop example-store.com --variant gid://shopify/ProductVariant/1001 --quantity 1
shop checkout create --shop example-store.com --cart gid://shopify/Cart/…
shop checkout complete gid://shopify/Checkout/… --shop example-store.com --instrument '…'
shop order search "trail running shoes"
```
## Search for products
```sh
shop search "trail running shoes" --ships-to US --currency USD --limit 3
```
<details>
<summary>Example output</summary>
```jsonc
{
"ok": true, "v": 1,
"data": {
"products": [
{
"id": "gid://shopify/p/7Qk2mN9pR4sT6vW8xY0zA1",
"title": "Ridgeline Trail Runner",
"description": { "plain": "Lightweight trail running shoes with a grippy outsole…" },
"variants": [{
"id": "gid://shopify/ProductVariant/1001",
"price": { "amount": 14500, "currency": "USD" },
"availability": { "available": true },
"options": [{ "name": "Size", "label": "10" }, { "name": "Color", "label": "Slate" }],
"rating": { "value": 4.7, "scale_max": 5, "count": 212 },
"seller": { "name": "Example Store", "url": "https://example-store.com", "domain": "example-store.myshopify.com" },
"checkout_url": "https://example-store.com/cart/1001:1?…&payment=shop_pay"
}]
},
// …two more products
],
"pagination": { "has_next_page": true, "total_count": 1180 },
"next": [
"A featured variant may already answer the user's needs; use get_product only for other options or a concrete missing fact…",
// …
]
}
}
```
</details>
Prices are integers in the currency's minor units, so `14500` is US$145.00.
Beyond a text query, a search can be shaped three ways:
- Filters such as `--ships-to`, `--price-max` and `--size` are hard constraints.
- `--intent` describes what the shopper needs and steers the ranking.
- `--like-image` and `--like-id` find products similar to an image or to another product.
```sh
shop search --like-image ./jacket.jpg --ships-to US
shop search --like-id gid://shopify/p/7Qk2mN9pR4sT6vW8xY0zA1 --limit 3
shop search "trail shoes" --intent "wide feet, wet Pacific Northwest trails" --currency USD --price-max 20000 --size 11
```
Every variant also has a `checkout_url`, a buy-now link to the merchant's checkout. A shopper signed in to Shop sees a one-tap review screen there. They can approve the purchase as it is, or change the delivery option, payment method, discounts, and more first.
## Sign in for personalization and accelerated checkout
Sign-in is optional: search, carts, and checkouts work without it. Signing in to Shop adds:
- Search results ranked using the shopper's own history.
- Carts and checkouts that arrive with the shopper's saved address and payment preference filled in.
- The account, checkout completion, order, and spend-request commands in the sections below.
```sh
shop account login --device-name "My Awesome Agent"
```
<details>
<summary>Example output</summary>
```json
{
"ok": true, "v": 1,
"data": {
"signed_in": false,
"pending": {
"verification_uri": "https://accounts.shop.app/device",
"verification_uri_complete": "https://accounts.shop.app/device?user_code=…",
"user_code": "…",
"expires_at": "…",
"interval": 5
}
}
}
```
</details>
Signing in takes two runs of the same command:
1. With `--wait 0`, it returns a link and code right away. The agent shows them to the shopper.
2. With `--wait 90`, it waits up to 90 seconds for the shopper to approve on Shop and returns `signed_in: true` as soon as they do. If they haven't approved yet, it returns `signed_in: false` and the agent can run it again.
`--device-name` is the name Shop's approval page shows for the agent. Once the shopper approves, the CLI attaches the credential to every request.
## Read the shopper's addresses and payment methods
```sh
shop account
```
<details>
<summary>Example output</summary>
```jsonc
{
"ok": true, "v": 1,
"data": {
"signed_in": true,
"buyer": { "email": "ada@example.com" },
"addresses": [{
"id": "address-a", "type": "shipping_address",
"first_name": "Ada", "last_name": "Lovelace",
"street_address": "1 Example Way", "address_locality": "London", "address_country": "GB", "postal_code": "SW1A 1AA"
}],
"preferred_address_id": "address-a",
"spend_permissions": [{
"id": "spend-permission-a", "status": "active", "currency": "USD",
"limit": 10000, "period": { "interval": "month", "ends_at": "…" },
"allowance": { "status": "available", "remaining": 8000 }
}],
"selectable_instruments": [{
"id": "3f1e2d4c-5b6a-4789-8abc-def012345678",
"type": "shop_pay",
"display": { "funding_instrument": { "type": "card", "brand": "visa", "last_digits": "4242" } }
}],
"preferred_instrument_id": "3f1e2d4c-5b6a-4789-8abc-def012345678",
// …links, messages
}
}
```
</details>
The response has the shopper's saved details, each with an `id`:
- `addresses`: saved delivery addresses. `preferred_address_id` is the one the shopper prefers.
- `selectable_instruments`: saved Shop Pay cards. `preferred_instrument_id` is the one the shopper prefers.
- `spend_permissions`: what the shopper allowed this agent to spend ahead of time on shop.com. Each may set a total `limit`, a `per_purchase_limit` and a `period` after which the limit starts over (none means it never does), in minor units of its own currency, and shows how much of the limit remains; a permission can also set no money limit at all. Within a permission, the agent can complete Shop Pay purchases without asking for approval each time. When it is missing, `messages` says why.
When a checkout needs a destination or a payment instrument, the agent passes one of these `id`s. It never types an address or asks for a card number.
## Create a pre-filled checkout
Create a cart, then turn it into a checkout:
```sh
shop cart create --shop example-store.com --variant gid://shopify/ProductVariant/1001 --quantity 1
shop checkout create --shop example-store.com --cart gid://shopify/Cart/… # the response below
```
<details>
<summary>Example output</summary>
```jsonc
{
"ok": true, "v": 1,
"data": {
"id": "gid://shopify/Checkout/…",
"status": "ready_for_complete",
"buyer": { "email": "ada@example.com", "phone_number": "+1555…" },
"line_items": [{ "id": "line-a", "item": { "id": "gid://shopify/ProductVariant/1001", "title": "Ridgeline Trail Runner — Size 10, Slate", "price": 14500 }, "quantity": 1 }],
"fulfillment": { "methods": [{ "type": "shipping", "selected_destination_id": "address-a", "groups": [{ "selected_option_id": "standard", "options": [ /* … */ ] }] }] },
"totals": [{ "type": "subtotal", "amount": 14500 }, { "type": "fulfillment", "amount": 0 }, { "type": "tax", "amount": 1290 }, { "type": "total", "amount": 15790 }],
"payment": { "instruments": [{ "id": "instrument-a", "handler_id": "shop_pay", "type": "shop_pay", "display": { "funding_instrument": { "brand": "visa", "last_digits": "4242" } } }] },
"continue_url": "https://example-store.com/checkouts/…"
}
}
```
</details>
The checkout's `status` says what happens next:
- `ready_for_complete`: nothing is missing. The contact details, delivery address, and Shop Pay card all came from the shopper's account.
- `requires_escalation`: something is missing. `messages[]` says what, and the shopper finishes at `continue_url`.
`--patch` is the efficient way to change a checkout. You write only the edit, as an RFC 6902 patch, and the CLI:
1. Reads the current checkout.
2. Applies the patch to that copy.
3. Sends the merchant the full updated document.
An edit to a line must first `test` that line's `id`. If the checkout changed after the CLI read it, the edit then fails instead of changing the wrong line:
```sh
shop checkout update gid://shopify/Checkout/… --shop example-store.com \
--patch '[{"op":"test","path":"/line_items/0/id","value":"line-a"},{"op":"replace","path":"/line_items/0/quantity","value":2}]'
```
## Complete the checkout
Pass one of the instruments the checkout offered in `payment.instruments`. The CLI marks it selected and changes nothing else:
```sh
shop checkout complete gid://shopify/Checkout/… --shop example-store.com \
--instrument '{"id":"instrument-a","handler_id":"shop_pay","type":"shop_pay","credential":{"type":"shop_token"}}'
```
<details>
<summary>Example output</summary>
```jsonc
{
"ok": true, "v": 1,
"data": {
"status": "completed",
"order": { "id": "gid://shopify/Order/…", "label": "#1042", "permalink_url": "https://example-store.com/…/orders/…" }
// …line items, totals, payment as charged
}
}
```
</details>
## Track the order
```sh
shop order search "trail runner"
```
<details>
<summary>Example output</summary>
```jsonc
{
"ok": true, "v": 1,
"data": {
"orders": [{
"id": "…", "label": "#1042", "placed_at": "…",
"seller": { "name": "Example Store", "domain": "example-store.com" },
"line_items": [{ "item": { "title": "Ridgeline Trail Runner — Size 10, Slate", "price": 14500 }, "quantity": { "original": 1, "fulfilled": 1 } }],
"fulfillment": { "events": [{ "carrier": "UPS", "tracking_url": "https://…", "occurred_at": "…" }] },
"permalink_url": "https://…"
}],
"shipments": [ /* standalone shipments Shop tracks without an order */ ]
// …
}
}
```
</details>
The results cover every order Shop tracks for the shopper, from any store. By default only in-progress orders are shown; pass `--status all` to search the full order history.
## Approve a browser purchase
Some stores have no agent checkout API (UCP); the agent has to enter a payment credential into the site's checkout form in a browser. A spend request covers that case — it is not a fallback for a Shop checkout that failed or needs the shopper:
1. The agent describes the purchase with `shop spend-request create`.
2. The shopper reviews and approves it on Shop.
3. `shop spend-request get` returns a one-time credential that fills the card fields of the site's checkout form, good for up to the `approval_limit` the shopper saw and approved.
4. The agent pays at the site's checkout and reports the outcome with `shop spend-request complete`.
`--intent` is required: describe the purchase and rationale in the shopper's language (100–500 characters; no links, card details, emails or addresses) — it is what they read on the Shop approval page. In `--arguments` it is `context.intent`. `--instrument-id` names one of the `selectable_instruments` from `shop account`; omit it to use the shopper's preferred card. `--currency` is the code the site's checkout charges in; never convert. `--business-country` is the business's two-letter country when the site states it; omit it rather than guess. Nested values such as line items take JSON, a `@file`, or `-` for stdin:
```sh
shop spend-request create \
--business-name "Ridgeline Running" --business-url https://ridgeline-running.example \
--currency USD --approval-limit 12500 \
--line-item @items.json \
--total '{"type":"total","amount":12500}' \
--intent "The trail running shoes you asked me to order this morning from Ridgeline Running, wide fit, in your usual size"
```
```jsonc
// items.json
{ "item": { "title": "Trail running shoe, wide, men's 11", "price": 12500 }, "quantity": 1 }
```
<details>
<summary>Example output</summary>
```json
{
"ok": true, "v": 1,
"data": {
"id": "0198e63b-1234-7abc-8def-123456789abc",
"status": "pending",
"approval_limit": 12500,
"continue_url": "https://accounts.shop.app/…",
"expires_at": "…"
}
}
```
</details>
Give the shopper the `continue_url`, then wait for their approval. `--output-file` keeps the credential out of the transcript: the issued instrument is written to a new file only you can read, and stdout carries the status and terms:
```sh
shop spend-request get 0198e63b-1234-7abc-8def-123456789abc --wait 90 --output-file ./card.json
```
<details>
<summary>Example output</summary>
```jsonc
{
"ok": true, "v": 1,
"data": {
"status": "consumed",
"credential_output_file": "./card.json"
// …terms as approved
}
}
```
```jsonc
// card.json
{
"spend_request_id": "0198e63b-1234-7abc-8def-123456789abc",
"instrument": {
"type": "card",
"display": { "last_digits": "…" },
"credential": { "type": "pan", "number": "5556…", "expiry_month": "…", "expiry_year": "…", "cvv": "…", "name": "Ada Lovelace" },
"billing_address": { "street_address": "1 Example Way", "address_locality": "London", "address_country": "GB", "postal_code": "SW1A 1AA" }
}
}
```
</details>
The credential arrives when the status is `consumed`. Hand the file to the program that fills the checkout form; without `--output-file`, the instrument is returned inline instead.
After paying at the site's checkout, the agent reports the outcome:
```sh
shop spend-request complete 0198e63b-1234-7abc-8def-123456789abc \
--status success --order '{"id":"EB-20931","total":1495,"currency":"USD"}'
```
<details>
<summary>Example output</summary>
```json
{
"ok": true, "v": 1,
"data": { "id": "0198e63b-1234-7abc-8def-123456789abc", "result": { "status": "success", "recorded_at": "…" } }
}
```
</details>
The response only confirms that Shop recorded the report. It is not proof that the payment went through.
## Direct MCP integration
The CLI is a client of Shop's MCP server at `https://mcp.shop.com/`. Apart from sign-in, each command above calls one of its tools:
- Most commands send a single `tools/call`. `--patch` reads the checkout before it writes, and `--wait` polls.
- The CLI adds argument parsing, sign-in, and the JSON envelope.
To see the requests, or to call a tool by its MCP name:
```sh
shop checkout create --shop example-store.com --cart gid://shopify/Cart/… --dry-run # the exact request, unsent
shop cart get gid://shopify/Cart/… --shop example-store.com --as curl # the exact request, as curl
shop call search_catalog --arguments '{"catalog":{"query":"rain jacket","pagination":{"limit":3}}}' # any tool by MCP name
shop tools # every command, its tool, its schema
```
To use the server without the CLI:
- Connect an MCP host to `https://mcp.shop.com/` to access the same tools. Sign-in goes through the host's OAuth flow, against the same Shop account, and the tools behave as they do in the CLI.
- A host that supports the skills extension ([SEP-2640](https://modelcontextprotocol.io/seps/2640-skills-extension)) finds the server's skill with `skills/list` on the same connection and reads its files with `resources/read`, so there is nothing to install. This package bundles the same skill.
- A backend that already holds a Shop token for this resource can hand it to the CLI as `SHOP_ACCESS_TOKEN`; the CLI sends it unchanged, never stores or refreshes it, and a rejected token says whether to mint a fresh one (401) or widen the grant (403). The full contract is in the CLI guide.
Install the skill from an installed `shop` command:
```sh
shop skill --install
```
## Reference
```sh
shop --help # the command tree
shop search --help # a command's flags and examples
shop search --schema # its complete input schema
shop tools # every command with its MCP tool, usage and schema, as JSON — for agents
shop skill # the bundled agent guidance, as prose
```
Every command accepts `--arguments <JSON|@FILE|->` with the complete argument object from `--schema`.
| Exit | Meaning |
| --- | --- |
| 0 | ok |
| 1 | no response from the network |
| 2 | usage error |
| 3 | sign-in required |
| 4 | the merchant or Shop answered with an error, or a precondition failed |
Errors use the same JSON envelope, and their `next` steps say what to do:
```json
{"ok":false,"v":1,"error":{"code":"auth_required","message":"Not signed in on this device","retryable":false,"next":["Run shop account login --wait 0 and show the shopper the verification URL and code","Run shop account login --wait 90 to finish once they approve","Retry this command after signing in"]}}
```
Run `shop skill --install` again after upgrading `shop` to replace the installed guidance.
---
_By accessing or using Shopify technology, or any data or content provided through it, you agree to the [Shopify API Terms](https://www.shopify.com/legal/api-terms)._