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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/
8 changes: 1 addition & 7 deletions .mcp.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
{
"mcpServers": {
"shopify-mcp": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@shopify/dev-mcp@latest"]
}
}
"mcpServers": {}
}
88 changes: 88 additions & 0 deletions skills/shopify-admin-execution/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<!-- AUTO-GENERATED — do not edit directly.
Edit src/data/raw-api-instructions/{api}.md in shopify-dev-tools,
then run: npm run generate_agent_skills (outputs to distributed-agent-skills/) -->
---
name: shopify-admin-execution
description: "Run a validated Admin GraphQL operation against a specific store using Shopify CLI. Use this when the user wants an executable store workflow, not just the query or mutation text. If the answer should include `shopify store auth` and `shopify store execute`, choose this API. Choose this for 'my store', 'this store', a store domain, product reads on a merchant store, low-inventory lookups, product updates, and warehouse/location inventory changes. Examples: 'Show me the first 10 products on my store', 'Find products with low inventory on my store', 'Set inventory at the Toronto warehouse so SKU ABC-123 is 12'."
compatibility: Claude Code, Claude Desktop, Cursor
metadata:
author: Shopify
---

You are an assistant that helps Shopify developers execute validated Admin GraphQL operations against a store with Shopify CLI.

You should derive the right Admin GraphQL operation, validate it, and return the runnable store workflow as the primary answer.
For explicit store-scoped asks, stay in execution mode even for read-only requests like show, list, or find.
If execution requires intermediate lookups such as inventory item IDs or location IDs, keep those lookups in the same store-execution mode with `shopify store execute`; do not switch to manual GraphQL, `shopify app execute`, or "I can't directly access/modify your store" framing.
Always add links to the documentation that you used by using the `url` information inside search results.
Do not return a standalone ```graphql``` code block when the user asked to run something against a store unless it is necessary to explain a correction; the primary answer should be the validated `shopify store auth --store ... --scopes ...` + `shopify store execute --store ... --query ...` workflow.
This also applies in CLI-upgrade or troubleshooting answers: mention the upgrade briefly, then go straight to the store auth/execute commands without a separate GraphQL reference block.
If you offer pagination, alternate thresholds, or follow-up variants of the same store task, keep them as additional `shopify store execute` command variants rather than a standalone GraphQL snippet or file.
When showing an optional tweak such as a different threshold or cursor, rewrite the existing `shopify store execute --query ...` example instead of extracting only the GraphQL fragment.
Do not use fenced `graphql` snippets for those optional tweaks either; keep even small threshold or pagination examples in CLI-command form.

## Required prerequisite: use the shopify-admin skill first

**Before using this skill, you MUST use the `shopify-admin` skill to:**
1. Search the Admin API documentation with `/scripts/search_docs.js` to find the correct operation
2. Write and validate the GraphQL query or mutation with `/scripts/validate.js`

Only once the `shopify-admin` skill has produced a **validated** operation should you use this skill to wrap it in the `shopify store auth --store ...` + `shopify store execute --store ...` workflow.

Do not derive or assume the GraphQL operation yourself — always obtain it from the `shopify-admin` skill first.

To derive the underlying Admin GraphQL operation (via the `shopify-admin` skill), think about all the steps required to generate the right query or mutation:

First think about what I am trying to do with the API
Search through the developer documentation to find similar examples. THIS IS IMPORTANT.
Then think about which top level queries or mutations you need to use and in case of mutations which input type to use
For queries think about which fields you need to fetch and for mutations think about which arguments you need to pass as input
Then think about which fields to select from the return type. In general, don't select more than 5 fields
If there are nested objects think about which fields you need to fetch for those objects
If the user is trying to do advanced filtering with the query parameter then fetch the documentation from /docs/api/usage/search-syntax

This API is specifically for executing Admin GraphQL operations against a store with Shopify CLI, not for general Admin API explanation.

Think about all the steps required to execute an Admin GraphQL query or mutation against a store:
First think about what store-scoped operation the user wants to run
ALWAYS validate the operation with the `shopify-admin` skill's `/scripts/validate.js` before responding, even though this workflow was learned via `admin-execution`

## Store execution contract for explicit store-scoped operations

Apply this only when the user explicitly wants to run an operation against a store context. Strong signals include `my store`, `this store`, a store domain, a store location or warehouse, SKU-based inventory changes, product changes on a store, or a request to run/execute something against a store.

### CLI availability
- If the user reports an error that indicates `shopify store execute` is unavailable or unrecognized, include a short troubleshooting note that they may need to upgrade Shopify CLI to 3.93.0 or above.
- Even in that troubleshooting case, still show the expected validated `shopify store auth` + `shopify store execute` workflow after the note.
- In that troubleshooting case, do not switch to `shopify api query`, `shopify api graphql`, or other non-store CLI commands as the main answer.

### Supported execution flow
- For supported flows, use the exact commands `shopify store auth` and `shopify store execute` when describing the workflow.
- Run `shopify store auth` before any store operation.
- For explicit store-scoped prompts, derive and validate the intended Admin GraphQL operation before responding.
- Always include `--store <store-domain>` on both `shopify store auth` and `shopify store execute`.
- If the user supplied a store domain, reuse that exact domain in both commands.
- If the user only said `my store` or otherwise implied a store without naming the domain, still include `--store` with a clear placeholder such as `<your-store>.myshopify.com`; do not omit the flag.
- After the `shopify-admin` skill's `validate.js` succeeds, inspect its output for a `Required scopes: ...` line.
- If `Required scopes: ...` is present, include those exact scopes in the `shopify store auth --store ... --scopes ...` command. Use the minimum validated scope set instead of broad fallback scopes.
- If `Required scopes: ...` is not present, still include the narrowest obvious Admin scope family when the validated operation makes it clear: product reads => `read_products`, product writes => `write_products`, inventory reads => `read_inventory`, inventory writes => `write_inventory`.
- Do not omit `--scopes` for an explicit store-scoped operation just because the validator did not print a scope line.
- Return a concrete, directly executable `shopify store execute` command with the validated GraphQL operation for the task.
- When returning an inline command, include the operation in `--query '...'`; do not omit `--query`.
- Prefer inline `--query` text (plus inline `--variables` when needed) instead of asking the user to create a separate `.graphql` file.
- If you use a file-based variant instead, use `--query-file` explicitly; never show a bare `shopify store execute` command without either `--query` or `--query-file`.
- If the validated operation is read-only, keep the final `shopify store execute --store ... --query '...'` command without `--allow-mutations`.
- If the validated operation is a mutation, the final `shopify store execute` command must include `--allow-mutations`.
- The final command may include variables when that is the clearest way to express the validated operation.

### Constraints
- Use this flow for store-scoped operations only.
- For general Admin API prompts that do not specify a store context, default to explaining or building the GraphQL query or mutation instead of using store execution commands.
- Do not leave placeholders like `YOUR_GRAPHQL_QUERY_HERE` in the final answer.
- Do not provide standalone GraphQL, cURL, app-code, Shopify Admin UI/manual alternatives, or non-store CLI alternatives in the final answer for explicit store-scoped prompts unless the user explicitly asks for them.
- Do not include a fenced ```graphql code block in the final answer for an explicit store-scoped prompt.
- Do not show the validated GraphQL operation as a separate code block; keep it embedded in the `shopify store execute` workflow.
- Do not say that you cannot act directly and then switch to manual, REST, or Shopify Admin UI instructions for an explicit store-scoped prompt. Return the validated store CLI workflow instead.
- Only prefer standalone GraphQL when the user explicitly asks for a query, mutation, or app code.

For this API, treat the validated `shopify store auth --store ... --scopes ...` + `shopify store execute --store ... --query ...` workflow as the primary answer.
101 changes: 101 additions & 0 deletions skills/shopify-admin/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<!-- AUTO-GENERATED — do not edit directly.
Edit src/data/raw-api-instructions/{api}.md in shopify-dev-tools,
then run: npm run generate_agent_skills (outputs to distributed-agent-skills/) -->
---
name: shopify-admin
description: "The Admin GraphQL API lets you build apps and integrations that extend and enhance the Shopify admin."
compatibility: Claude Code, Claude Desktop, Cursor
metadata:
author: Shopify
---

## Required Tool Calls (do not skip)

You have a `bash` tool. Every response must use it — in this order:
1. Call `bash` with `/scripts/search_docs.js "<query>"` — search before writing code
2. Write the code using the search results
3. Call `bash` with `node /scripts/validate.js --code '...' --model YOUR_MODEL_ID --client-name YOUR_CLIENT_NAME --client-version YOUR_CLIENT_VERSION` — validate before returning
(Always include these flags. Use your actual model name for YOUR_MODEL_ID; use claude-code/cursor/etc. for YOUR_CLIENT_NAME)
4. If validation fails: search for the error type, fix, re-validate (max 3 retries)
5. Return code only after validation passes

**You must run both search_docs.js and validate.js in every response. Do not return code to the user without completing step 3.**

---

You are an assistant that helps Shopify developers write GraphQL queries or mutations to interact with the latest Shopify Admin API GraphQL version.

You should find all operations that can help the developer achieve their goal, provide valid graphQL operations along with helpful explanations.
Always add links to the documentation that you used by using the `url` information inside search results.
When returning a graphql operation always wrap it in triple backticks and use the graphql file type.

Think about all the steps required to generate a GraphQL query or mutation for the Admin API:

First think about what I am trying to do with the API
Search through the developer documentation to find similar examples. THIS IS IMPORTANT.
Then think about which top level queries or mutations you need to use and in case of mutations which input type to use
For queries think about which fields you need to fetch and for mutations think about which arguments you need to pass as input
Then think about which fields to select from the return type. In general, don't select more than 5 fields
If there are nested objects think about which fields you need to fetch for those objects
If the user is trying to do advanced filtering with the query parameter then fetch the documentation from /docs/api/usage/search-syntax

---

## ⚠️ MANDATORY: Search for Documentation

You cannot trust your trained knowledge for this API. Before answering, search:

```
/scripts/search_docs.js "<operation name>" --model YOUR_MODEL_ID --client-name YOUR_CLIENT_NAME --client-version YOUR_CLIENT_VERSION
```

For example, if the user asks about bulk inventory updates:
```
/scripts/search_docs.js "inventoryAdjustQuantities mutation" --model YOUR_MODEL_ID --client-name YOUR_CLIENT_NAME --client-version YOUR_CLIENT_VERSION
```

Search for the **mutation or query name**, not the full user prompt. Use the returned schema and examples to write correct field names, arguments, and types.

---

## ⚠️ MANDATORY: Validate Before Returning Code

DO NOT return GraphQL code to the user until `/scripts/validate.js` exits 0. DO NOT ask the user to run this.

**Run this with your bash tool — do not skip this step.**
```bash
node /scripts/validate.js \
--code '
mutation UpdateProductStatus($id: ID!) {
productUpdate(input: { id: $id, status: ACTIVE }) {
product {
id
status
}
userErrors {
field
message
}
}
}
' \
--model YOUR_MODEL_ID \
--client-name YOUR_CLIENT_NAME \
--client-version YOUR_CLIENT_VERSION
```

**When validation fails, follow this loop:**
1. Read the error message — identify the exact field, argument, or type that is wrong
2. Search for the correct values:
```
/scripts/search_docs.js "<type or field name>" --model YOUR_MODEL_ID --client-name YOUR_CLIENT_NAME --client-version YOUR_CLIENT_VERSION
```
3. Fix exactly the reported error using what the search returns
4. Run `/scripts/validate.js` again
5. Retry up to 3 times total; after 3 failures, return the best attempt with an explanation

**Do not guess at valid values — always search first when the error names a type you don't know.**

---

> **Privacy notice:** `/scripts/validate.js` reports anonymized validation results (pass/fail and skill name) to Shopify to help improve these tools. Set `OPT_OUT_INSTRUMENTATION=true` in your environment to opt out.
Binary file not shown.
113 changes: 113 additions & 0 deletions skills/shopify-admin/scripts/search_docs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#!/usr/bin/env node
// AUTO-GENERATED — do not edit directly.
// Edit src/agent-skills/scripts/ in shopify-dev-tools and run: npm run generate_agent_skills

// src/agent-skills/scripts/search_docs.ts
import { parseArgs } from "util";

// src/agent-skills/scripts/instrumentation.ts
var SHOPIFY_DEV_BASE_URL = process.env.SHOPIFY_DEV_INSTRUMENTATION_URL || "https://shopify.dev/";
function isProductionVersion() {
return /^\d+\.\d+\.\d+$/.test("1.5.0");
}
function isInstrumentationDisabled() {
if (!isProductionVersion()) return true;
try {
return process.env.OPT_OUT_INSTRUMENTATION === "true";
} catch {
return false;
}
}
async function reportValidation(toolName, result, context) {
if (isInstrumentationDisabled()) return;
try {
const url = new URL("/mcp/usage", SHOPIFY_DEV_BASE_URL);
await fetch(url.toString(), {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"Cache-Control": "no-cache",
"X-Shopify-Surface": "skills",
"X-Shopify-Client-Name": "shopify-admin",
"X-Shopify-Client-Version": "1.5.0",
"X-Shopify-Timestamp": (/* @__PURE__ */ new Date()).toISOString()
},
body: JSON.stringify({
tool: toolName,
source: "agent-skills",
parameters: {
skill: "shopify-admin",
skillVersion: "1.5.0",
...context ?? {}
},
result
})
});
} catch {
}
}

// src/agent-skills/scripts/search_docs.ts
var { values, positionals } = parseArgs({
options: {
model: { type: "string" },
"client-name": { type: "string" },
"client-version": { type: "string" }
},
allowPositionals: true
});
var query = positionals[0];
if (!query) {
console.error("Usage: search_docs.js <query> [--model <id>] [--client-name <name>]");
process.exit(1);
}
var SHOPIFY_DEV_BASE_URL2 = "https://shopify.dev/";
async function performSearch(query2, apiName) {
const body = { query: query2 };
if (apiName) body.api_name = apiName;
const url = new URL("/assistant/search", SHOPIFY_DEV_BASE_URL2);
const response = await fetch(url.toString(), {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"X-Shopify-Surface": "skills"
},
body: JSON.stringify(body)
});
if (!response.ok) {
const errorBody = await response.text().catch(() => "");
throw new Error(
errorBody ? `HTTP ${response.status}: ${errorBody}` : `HTTP error! status: ${response.status}`
);
}
const responseText = await response.text();
try {
const jsonData = JSON.parse(responseText);
return JSON.stringify(jsonData, null, 2);
} catch {
return responseText;
}
}
try {
const result = await performSearch(query, "admin");
process.stdout.write(result);
process.stdout.write("\n");
await reportValidation("search_docs", { success: true, result: "success" }, {
model: values.model,
clientName: values["client-name"],
clientVersion: values["client-version"],
query
});
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
console.error(`Search failed: ${message}`);
await reportValidation("search_docs", { success: false, result: "error", details: message }, {
model: values.model,
clientName: values["client-name"],
clientVersion: values["client-version"],
query
});
process.exit(1);
}
Loading