|
| 1 | +# Shopify MCP |
| 2 | + |
| 3 | +Read-only MCP for the **Shopify Admin GraphQL API** (pinned to `2026-07`). Every tool is a |
| 4 | +GraphQL query — never a mutation — so the MCP can never modify store data. |
| 5 | + |
| 6 | +45 tools across 11 domains: products & collections, orders (drafts, abandoned checkouts, |
| 7 | +returns, refund previews), fulfillment & locations, inventory, customers & segments, |
| 8 | +discounts & marketing, online store content (pages, blogs, articles, menus, redirects, |
| 9 | +themes), store properties & localization, B2B companies & markets, Shopify Payments, |
| 10 | +and ShopifyQL analytics. See [TOOLS.md](./TOOLS.md) for the full catalog and scope map. |
| 11 | + |
| 12 | +## Setup |
| 13 | + |
| 14 | +1. In the Shopify admin, create a **custom app** (Settings → Apps and sales channels → |
| 15 | + Develop apps) and give it the **read scopes** you need — e.g. `read_products`, |
| 16 | + `read_orders`, `read_customers`, `read_inventory`, `read_fulfillments`, `read_locations`, |
| 17 | + `read_discounts`, `read_content`, `read_themes`, `read_locales`, `read_translations`, |
| 18 | + `read_marketing_events`, `read_users`, `read_companies`, `read_markets`, |
| 19 | + `read_shopify_payments_payouts`, `read_shopify_payments_disputes`, `read_reports`. |
| 20 | + Tools whose scope is missing fail with a clear ACCESS_DENIED hint; grant only what you use. |
| 21 | +2. Install the app on the store and copy the **Admin API access token** (`shpat_…`). |
| 22 | +3. Connect the MCP: |
| 23 | + - **Token** (connection Authorization field): the Admin API access token |
| 24 | + - **Store Domain** (config field): `my-store.myshopify.com` |
| 25 | + - **API Version** (optional): defaults to `2026-07` |
| 26 | + |
| 27 | +Orders older than 60 days additionally require the `read_all_orders` scope, which Shopify |
| 28 | +only grants to approved apps. |
| 29 | + |
| 30 | +### Local development |
| 31 | + |
| 32 | +```sh |
| 33 | +SHOPIFY_STORE_DOMAIN=my-store.myshopify.com SHOPIFY_ACCESS_TOKEN=shpat_xxx bun run dev |
| 34 | +``` |
| 35 | + |
| 36 | +Env vars are a fallback — connection state (`storeDomain`) and the Authorization header |
| 37 | +always win. |
| 38 | + |
| 39 | +## Development |
| 40 | + |
| 41 | +```sh |
| 42 | +bun run check # typecheck |
| 43 | +bun test # unit tests (credential resolution, retry/backoff, connection flattening) |
| 44 | +bun run validate:queries # validates every GraphQL document against the live Shopify schema |
| 45 | +``` |
| 46 | + |
| 47 | +`validate:queries` posts each tool's query to shopify.dev's public GraphiQL proxy |
| 48 | +(`https://shopify.dev/admin-graphql-direct-proxy/<version>`), which validates against the |
| 49 | +real Admin API schema and executes on demo data — no store or token required. Run it when |
| 50 | +bumping the API version. |
| 51 | + |
| 52 | +## Auth notes |
| 53 | + |
| 54 | +Token auth only (same pattern as the Magento MCP). Shopify OAuth would require a registered |
| 55 | +partner app (client id/secret) and a per-store authorization URL, which the runtime `oauth` |
| 56 | +hook can't currently express — the shop domain isn't known when the authorization URL is |
| 57 | +built. If a partner app ever exists, the OAuth flow can be added in `server/main.ts`. |
0 commit comments