Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .agents/plugins/marketplace.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{
"name": "shop",
"interface": {
"displayName": "Shop"
},
"plugins": [
{
"name": "shop",
Expand Down
16 changes: 16 additions & 0 deletions .cursor-plugin/marketplace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "shop",
"owner": {
"name": "Shopify"
},
"metadata": {
"description": "Shop for agents: product discovery, checkout, and Shop order tracking across millions of stores"
},
"plugins": [
{
"name": "shop",
"source": "./plugins/shop",
"description": "Shop across millions of stores: find and compare over a billion products, build carts, check out with Shop Pay, and track orders and deliveries."
}
]
}
1 change: 0 additions & 1 deletion .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
# Replace the individual with a team handle before the repository is made public.
/.github/ @igrigorik
28 changes: 26 additions & 2 deletions .github/scripts/verify.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,17 @@ const GOVERNANCE = ['.github/', '.gitattributes', '.gitignore', 'SECURITY.md', '
const PLUGIN_MANIFESTS = [
'plugins/shop/plugin.json',
'plugins/shop/.claude-plugin/plugin.json',
'plugins/shop/.codex-plugin/plugin.json',
'plugins/shop/.cursor-plugin/plugin.json',
];
const PLUGIN_SOURCE = './plugins/shop';
// One logo per format; each host overlay names it under its own field. Paths are plugin-relative.
const PLUGIN_LOGOS = ['assets/logo.svg', 'assets/logo.png'];
const LOGO_FIELDS = [
['plugins/shop/.cursor-plugin/plugin.json', ['logo']],
['plugins/shop/.claude-plugin/plugin.json', ['icon']],
['plugins/shop/plugin.json', ['extensions', 'com.openai', 'interface', 'logo']],
['plugins/shop/plugin.json', ['extensions', 'com.openai', 'interface', 'composerIcon']],
];

const native = (posixPath) => join(root, ...posixPath.split('/'));
const parseJson = (text, label) => {
Expand Down Expand Up @@ -96,14 +104,30 @@ async function main() {
const claudeEntry = (claude.plugins || []).find((entry) => entry?.name === 'shop');
assert.ok(claudeEntry, '.claude-plugin/marketplace.json: missing plugin entry named "shop"');
assert.ok(claudeEntry.source === PLUGIN_SOURCE, `.claude-plugin/marketplace.json: plugin "shop" source must be ${PLUGIN_SOURCE}`);
const cursor = readJson('.cursor-plugin/marketplace.json');
assert.ok(cursor.name === 'shop', '.cursor-plugin/marketplace.json: name must be "shop"');
const cursorEntry = (cursor.plugins || []).find((entry) => entry?.name === 'shop');
assert.ok(cursorEntry, '.cursor-plugin/marketplace.json: missing plugin entry named "shop"');
assert.ok(cursorEntry.source === PLUGIN_SOURCE, `.cursor-plugin/marketplace.json: plugin "shop" source must be ${PLUGIN_SOURCE}`);
const agents = readJson('.agents/plugins/marketplace.json');
assert.ok(agents.name === 'shop', '.agents/plugins/marketplace.json: name must be "shop"');
const agentsEntry = (agents.plugins || []).find((entry) => entry?.name === 'shop');
assert.ok(agentsEntry, '.agents/plugins/marketplace.json: missing plugin entry named "shop"');
assert.ok(agentsEntry.source?.path === PLUGIN_SOURCE, `.agents/plugins/marketplace.json: plugin "shop" source.path must be ${PLUGIN_SOURCE}`);
const pluginDir = native(PLUGIN_SOURCE.replace(/^\.\//, ''));
assert.ok(existsSync(pluginDir) && statSync(pluginDir).isDirectory(), `${PLUGIN_SOURCE} is not a directory in this checkout`);
pass('both marketplace catalogs name "shop" and resolve to plugins/shop');
pass('all three marketplace catalogs name "shop" and resolve to plugins/shop');

// (d2) every overlay's logo reference resolves to a shipped file
for (const logo of PLUGIN_LOGOS) {
const file = native(`plugins/shop/${logo}`);
assert.ok(existsSync(file) && statSync(file).isFile(), `plugins/shop/${logo} is missing; the plugin manifests reference it`);
}
for (const [manifest, keys] of LOGO_FIELDS) {
const value = keys.reduce((node, key) => (node && typeof node === 'object' ? node[key] : undefined), readJson(manifest));
assert.ok(typeof value === 'string' && PLUGIN_LOGOS.includes(value.replace(/^\.\//, '')), `${manifest}: ${keys.join('.')} must name one of ${PLUGIN_LOGOS.join(', ')}; found ${JSON.stringify(value)}`);
}
pass(`logo references resolve to ${PLUGIN_LOGOS.length} shipped files`);

// (e) the module runs under this Node and reports the package version
const banner = shop('--version').toString('utf8');
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ jobs:
VERSION: ${{ steps.plan.outputs.version }}
run: echo "::notice::@shopify/shop@$VERSION is already on the registry; nothing to publish."

# --ignore-scripts: the postinstall hook is for installers and does not run here.
# --ignore-scripts: the package declares no lifecycle scripts, and none may run here.
# --provenance: explicit, because npm's automatic OIDC provenance is best-effort; a publish that cannot attest fails instead of shipping without provenance.
- name: Publish
if: steps.plan.outputs.private != 'true' && steps.plan.outputs.published != 'true'
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ shop order search "trail runner"

</details>

The results cover orders placed through this agent from any store, plus the shopper's other recent and in-progress orders that Shop tracks. Leave out the query to get the most recent orders.
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

Expand Down Expand Up @@ -426,7 +426,7 @@ Every command accepts `--arguments <JSON|@FILE|->` with the complete argument ob
| 1 | no response from the network |
| 2 | usage error |
| 3 | sign-in required |
| 4 | the merchant or Shop answered with an error |
| 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:

Expand Down
Binary file modified lib/shop.wasm
Binary file not shown.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@shopify/shop",
"version": "1.1.1",
"version": "1.2.0",
"description": "Shop for agents: product discovery, checkout, and Shop order tracking across millions of stores",
"license": "MIT",
"repository": {
Expand All @@ -24,6 +24,7 @@
"skills",
"plugins",
".claude-plugin",
".cursor-plugin",
".agents",
"README.md",
"LICENSE"
Expand Down
35 changes: 31 additions & 4 deletions plugins/shop/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,35 @@
{
"name": "shop",
"version": "1.1.1",
"description": "Shop across merchants through the Universal Commerce Protocol",
"displayName": "Shop",
"version": "1.2.0",
"description": "Shop across millions of stores: find and compare over a billion products, build carts, check out with Shop Pay, and track orders and deliveries.",
"author": {
"name": "Shopify"
}
"name": "Shopify",
"url": "https://shop.com"
},
"homepage": "https://github.com/Shopify/shop",
"repository": "https://github.com/Shopify/shop",
"license": "MIT",
"keywords": [
"shop",
"shop app",
"shop pay",
"shopify",
"shopping",
"buy",
"products",
"price comparison",
"cart",
"checkout",
"orders",
"order tracking",
"delivery",
"ecommerce",
"ucp"
],
"icon": "./assets/logo.png",
"documentationUrl": "https://github.com/Shopify/shop#readme",
"supportUrl": "https://help.shop.app/",
"privacyPolicyUrl": "https://www.shopify.com/legal/privacy/consumers",
"termsOfServiceUrl": "https://shop.com/terms-of-service"
}
10 changes: 0 additions & 10 deletions plugins/shop/.codex-plugin/plugin.json

This file was deleted.

39 changes: 39 additions & 0 deletions plugins/shop/.cursor-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "shop",
"displayName": "Shop",
"version": "1.2.0",
"description": "Shop across millions of stores: find and compare over a billion products, build carts, and check out. Sign in with Shop to pay with Shop Pay, use your saved addresses, get results personalized to you, and track your orders and deliveries. At stores that don't support Shop Pay directly, approve the purchase and your agent completes it with a single-use card.",
"author": {
"name": "Shopify"
},
"homepage": "https://shop.com",
"repository": "https://github.com/Shopify/shop",
"license": "MIT",
"logo": "assets/logo.svg",
"keywords": [
"shop",
"shop app",
"shop pay",
"shopify",
"shopping",
"buy",
"products",
"price comparison",
"cart",
"checkout",
"orders",
"order tracking",
"delivery",
"ecommerce",
"ucp"
],
"category": "integrations",
"tags": [
"shop",
"shopping",
"ecommerce",
"shop-pay",
"mcp"
],
"skills": "./skills/"
}
Binary file added plugins/shop/assets/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions plugins/shop/assets/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
59 changes: 55 additions & 4 deletions plugins/shop/plugin.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,61 @@
{
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
"name": "shop",
"version": "1.1.1",
"description": "Shop across merchants through the Universal Commerce Protocol",
"version": "1.2.0",
"description": "Shop across millions of stores: find and compare over a billion products, build carts, check out with Shop Pay, and track orders and deliveries.",
"author": {
"name": "Shopify"
"name": "Shopify",
"url": "https://shop.com"
},
"license": "MIT"
"homepage": "https://github.com/Shopify/shop",
"repository": "https://github.com/Shopify/shop",
"license": "MIT",
"keywords": [
"shop",
"shop app",
"shop pay",
"shopify",
"shopping",
"buy",
"products",
"price comparison",
"cart",
"checkout",
"orders",
"order tracking",
"delivery",
"ecommerce",
"ucp"
],
"extensions": {
"com.openai": {
"interface": {
"displayName": "Shop",
"shortDescription": "Shop millions of stores",
"longDescription": "Shop across millions of stores: find and compare over a billion products, build carts, and check out. Sign in with Shop to pay with Shop Pay, use your saved addresses, get results personalized to you, and track your orders and deliveries. At stores that don't support Shop Pay directly, approve the purchase and your agent completes it with a single-use card.",
"developerName": "Shopify",
"category": "Shopping",
"capabilities": [
"Search over 1 billion products from millions of stores",
"Compare products, prices and options",
"Build carts and check out",
"Pay with Shop Pay and saved addresses",
"Track orders and deliveries",
"Approve single-use card purchases at stores without Shop Pay"
],
"websiteURL": "https://shop.com",
"supportURL": "https://help.shop.app/",
"privacyPolicyURL": "https://www.shopify.com/legal/privacy/consumers",
"termsOfServiceURL": "https://shop.com/terms-of-service",
"defaultPrompt": [
"Find a waterproof running jacket under $150 that ships to Canada",
"Compare ceramic coffee drippers under $40",
"What's on its way?"
],
"brandColor": "#5433EB",
"composerIcon": "./assets/logo.png",
"logo": "./assets/logo.png"
}
}
}
}
2 changes: 1 addition & 1 deletion plugins/shop/skills/shop/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ This is notation, not JSON. `?` means optional. Replace `RESOURCE` with `cart` o
| `get_spend_request`, `cancel_spend_request` | `{id}`; cancel may add `meta`. Get may issue/replay a credential and is not read-only. |
| `update_spend_request` | `{id,spend_request:{shipping_address?,line_items?,totals?,approval_limit?},meta?}`. |
| `complete_spend_request` | `{id,result:{status:"success",order?:{id?,permalink_url?,total?,currency?}}}` or `{id,result:{status:"error",failure_code,message?}}`; present optional fields are non-null. |
| `search_orders` | `{query?,activity?,date_from?,date_to?,pagination?}`; omit query for recent orders. |
| `search_orders` | `{orders:{query?,filters?:{status?,date?:{from?,to?}},pagination?}}`; in-progress orders by default; `status:"all"` searches the full order history. |

Use `seller.domain` for `shop`. Keep merchants separate. Copy returned IDs and URLs exactly, with every query parameter; do not rebuild or shorten them.
### Search
Expand Down
Loading
Loading