shopify-multistore-mcp
Provides tools for managing multiple Shopify stores via the Admin GraphQL API, including listing stores, fetching shop info, products, orders, and executing raw GraphQL queries/mutations with a production write-gate for safety.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@shopify-multistore-mcpShow unfulfilled orders in acme-live."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
shopify-multistore-mcp
An MCP server for working across multiple Shopify stores (multi-client dev work)
through the Admin GraphQL API. Every tool takes a store argument that routes
the call to the right client store, so one connector covers all your stores.
Designed to sit alongside Shopify's official tooling, not replace it:
Layer | What it does | You build it? |
Shopify AI Toolkit / Dev MCP | Theme, apps, Functions, extensions, docs + GraphQL validation | No — install it |
Shopify CLI |
| No — install it |
This server | Conversational Admin API read/write across stores, with a production write-gate | Yes — this repo |
Quick start
git clone https://github.com/Cubitrek/shopify-multistore-mcp.git
cd shopify-multistore-mcp
npm install
npm run build
cp stores.example.json stores.json # then edit — add your storesThen, per store, get an access token and connect it to Claude. Check your config loaded:
STORES_CONFIG=./stores.json node -e \
'import("./dist/config.js").then(m=>console.table(m.loadRegistry().list()))'Every store shows authenticated: true or false, so you can see at a glance
which ones still need a token.
Related MCP server: Shopify Store MCP Server
Tools
Reads: list_stores, shop_info, get_products, get_orders, get_metafields, get_pages
Writes (production-gated): set_metafields, add_tags, remove_tags,
create_product, update_product, create_page, update_page
Escape hatch: graphql — run any Admin GraphQL query or mutation
The production write-gate
Each store is tagged env: "production" or env: "dev".
Mutations against a dev store run freely.
Mutations against a production store are blocked unless the call passes
confirm: true. The model must inspect what will change, then explicitly confirm.
Failed writes surface their userErrors, so a rejected mutation can never look
like a success.
Getting an access token
Which path you need depends on the store's relationship to your Partner organization — not on how old the store is. Check this first; picking wrong costs an hour of dead ends:
The store is… | Where you see it | Path |
A merchant's own store you have collaborator access to, or your own live store | Dev Dashboard → Stores → Collaborations | Path A — OAuth capture |
A client-transfer store still held in your Partner org | Dev Dashboard → Stores → Client transfer | Path B — legacy custom app |
Already has a legacy custom app from before 2026 | Store admin → Settings → Apps | Reuse it — copy the existing |
⚠️ Custom apps cannot be installed on client-transfer stores. Shopify restricts those to free and partner-friendly apps, so every install attempt fails with "The installation link for this app is invalid" — no matter how the app is configured. This is not a misconfiguration; Path A simply cannot work until the store is transferred to the merchant. Use Path B.
Path A — OAuth capture (current Shopify platform) ⭐
Shopify has retired legacy custom apps on current stores (Spring '26 and later). Apps are now created in the Dev Dashboard, are OAuth-based, and only hand the access token to a backend during install — there's no "reveal token" button anymore.
This repo ships that backend as a one-time command. npm run auth runs a
throwaway local callback server, completes the install, and writes the resulting
offline access token into stores.json. Offline tokens don't expire, so it's
a single step per store — afterwards the MCP server behaves exactly as it would
with a legacy static token.
1. Create the app in the Dev Dashboard (Partners → your org → Dev dashboard → Create app → Start from Dev Dashboard).
2. Configure the version: set your Admin API scopes, uncheck Embed app in Shopify admin (this is a headless connector), and add your redirect URL:
http://localhost:3456/callback3. Set the distribution method. For a single client store, choose
Custom distribution and enter that store's .myshopify.com domain.
⚠️ Distribution choice is permanent per app, and custom distribution binds the
app to that one store — create a dedicated app per client store.
Custom distribution is what lets you install on a store that is not in your organization — e.g. a client store you only have collaborator access to. It does not work on client-transfer stores held in your own org; see the table above.
4. Configure this repo (stores.json) with the app's Client ID and scopes:
{
"defaultApiVersion": "2026-07",
"app": {
"clientId": "your_client_id",
"clientSecretEnv": "SHOPIFY_CLIENT_SECRET",
"redirectUri": "http://localhost:3456/callback",
"scopes": "read_products,write_products,read_orders,read_content,write_content"
},
"stores": {
"client-a": {
"label": "Client A (production)",
"domain": "client-a.myshopify.com",
"env": "production"
}
}
}5. Put the client secret where the tool can find it. clientSecretEnv names
an environment variable; the value belongs in .env (gitignored), which is
loaded automatically on startup:
echo 'SHOPIFY_CLIENT_SECRET=shpss_...' >> .envAn existing environment variable always wins over .env, so
SHOPIFY_CLIENT_SECRET=… npm run auth … and CI secrets still override the file.
Before this was wired up,
.envwas never read andnpm run authfailed with "App for … has no client secret" even when the secret was sitting in the file. If you see that error, the named variable genuinely isn't reaching the process — check the name inclientSecretEnvmatches the key in.env.
6. Run the capture:
npm run build
npm run auth -- --store=client-aIt prints an install URL. Open it in a browser signed into that store's admin,
approve the scopes, and the token is written into stores.json automatically.
The callback is verified before any token is stored: HMAC signature (so a forged callback is rejected), state nonce (CSRF), and a shop-domain match against your config.
First install of a custom-distribution app
If Shopify answers "The installation link for this app is invalid" or
Oauth error invalid_link, check the store type first: on a client-transfer
store no install link will ever work, and you want Path B
instead. Otherwise the app uses custom distribution, and its install link is
signed by Shopify and cannot be generated locally — neither
https://<shop>/admin/oauth/authorize?… nor
https://admin.shopify.com/store/<handle>/oauth/install_custom_app?client_id=…
will work, however correct they look.
Only the link on the app's own Distribution page works, and it must be copied by hand:
Set the app's App URL to
http://localhost:3456/(Shopify opens it with?shop=…and the local server answers that handshake). The redirect URL stayshttp://localhost:3456/callback.Start the listener and leave it running:
npm run auth -- --store=client-a --install-linkIn the Dev Dashboard / Partners: Apps → your app → Distribution. Press Copy on the Install link, open it in a browser signed into the store admin, and press Install.
Because Shopify originates this flow, the callback carries Shopify's own state
rather than a nonce we generated, so --install-link relaxes the state check.
HMAC verification and the shop-domain match still apply. After the first install,
the ordinary authorize URL works for re-auth.
⚠️ Scopes are fixed at install time. Adding a scope to stores.json later has
no effect until you also add it to the app in the Dev Dashboard, release a new
version, and re-run the capture. Verify what was actually granted:
curl -s -H "X-Shopify-Access-Token: $TOKEN" \
https://<shop>.myshopify.com/admin/oauth/access_scopes.jsonPath B — legacy custom app
Legacy custom apps mint a static shpat_… token directly in the store admin —
no OAuth, no install link, no callback server. Much simpler than Path A when
it's available.
As of January 1, 2026, merchants can no longer create them. Two exceptions:
Partners can still create them on stores they hold before transfer. This is the sanctioned path for client-transfer stores, and the only one that works there. Creation is disabled once the store transfers to the merchant.
Apps created before the cutoff keep working, including their tokens.
Store admin → Settings → Apps → App development → Create a legacy custom
app. Name it, Configure Admin API scopes, tick what you need, Save, then
Install app and reveal the token. Drop it into stores.json:
"woot": {
"domain": "your-store.myshopify.com",
"env": "production",
"adminToken": "shpat_..."
}A store using adminToken needs no app block — that's only read by
npm run auth.
If the page shows only a pointer to the Dev Dashboard with no Create button, the store is merchant-owned and past the cutoff. Use Path A.
Configuration reference
{
"defaultApiVersion": "2026-07",
"app": { /* global OAuth app, used by `npm run auth` */ },
"stores": {
"alias": {
"label": "Human-readable name",
"domain": "store.myshopify.com",
"env": "production", // or "dev" — drives the write-gate
"adminToken": "shpat_...", // inline token, OR
"adminTokenEnv": "SOME_VAR", // read from env, OR
"apiVersion": "2026-04", // optional per-store override
"app": { /* optional per-store app override */ }
}
}
}A store with no token is fine — tools return a clear "run
npm run auth" error, andlist_storesreportsauthenticated: false.The client secret is only needed for
npm run auth, never to run the server.stores.jsonis gitignored. Never commit real tokens.
Connect it to Claude
Add to your MCP config (Claude Desktop claude_desktop_config.json, or Claude Code
.mcp.json) — this wires the official Dev MCP and this multi-store server:
{
"mcpServers": {
"shopify-dev": {
"command": "npx",
"args": ["-y", "@shopify/dev-mcp@latest"]
},
"shopify-multistore": {
"command": "node",
"args": ["/absolute/path/to/shopify-multistore-mcp/dist/index.js"],
"env": {
"STORES_CONFIG": "/absolute/path/to/shopify-multistore-mcp/stores.json"
}
}
}
}Or register it once for every project with the Claude Code CLI:
claude mcp add shopify-multistore --scope user \
--env STORES_CONFIG=$PWD/stores.json \
-- node $PWD/dist/index.jsIf the server fails to start but works fine in your terminal, use an absolute path to
node(e.g./opt/homebrew/bin/node). Desktop apps don't inherit your shell'sPATH, so version-manager shims like nvm often aren't on it.
Restart Claude after adding it, then ask "list my shopify stores" to confirm.
For theme/app/Functions work, also install the official Shopify AI Toolkit plugin and the Shopify CLI.
Usage examples
"List my configured stores."
"Show active products from vendor Acme in client-a."
"How many unfulfilled orders does client-a have this week?"
"Add a metafield
custom.care_instructionsto product X in dev-store." (runs)"Update the price of variant Y in client-a." (production — asks to confirm first)
Development
npm run build # compile to dist/
npm run dev # tsc --watch
npm run typecheck # type-check without emitting
npm run auth -- --store=<alias> # one-time OAuth token captureProject layout
src/
index.ts # MCP server entry (stdio)
auth.ts # one-time OAuth token capture (local callback server)
config.ts # store registry loader + zod validation
dotenv.ts # minimal .env loader (no dependency)
shopify.ts # Admin GraphQL client + production write-gate
tools.ts # tool definitions
stores.example.json # template — copy to stores.json (gitignored)Roadmap
Inventory adjustments, theme asset read/write, bulk operations
Token refresh / re-auth helper for rotated secrets
Optional hosted redirect endpoint (for a permanent, non-localhost install URL)
License
MIT
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- Flicense-quality-maintenanceEnables interaction with multiple Shopify stores simultaneously through the Storefront API. Supports product search, cart management, and store operations across configured Shopify stores through natural language.Last updated2
- Alicense-qualityCmaintenanceEnables AI agents to interact with live Shopify stores through Admin and Storefront APIs for tasks like GraphQL execution, bulk operations, and file uploads. It includes built-in rate limiting and operation logging to manage store data and schema discovery securely.Last updated343ISC
- Flicense-qualityDmaintenanceExposes Shopify Admin API capabilities to LLMs, enabling product, order, customer, and inventory management via natural language.Last updated1
- AlicenseAqualityCmaintenanceEnables AI agents to read and write Shopify store data including products, orders, customers, inventory, and more via the Admin GraphQL API.Last updated2845MIT
Related MCP Connectors
Manage your Savanto store from your AI: catalog, content, prompts, and analytics, by chat.
Shared, permission-aware company context for AI agents, with provenance, approvals and audit.
Runtime permission, approval, and audit layer for AI agent tool execution.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/Cubitrek/shopify-multistore-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server