An MCP (Model Context Protocol) server for fully managing a Shopify store through AI assistants like Cursor or Claude Desktop.
┌─────────────────┐ stdio ┌──────────────────┐ GraphQL ┌──────────────┐
│ Cursor / Claude │◄──────────────►│ MCP Server │◄────────────►│ Shopify API │
│ (MCP Client) │ │ (Node.js) │ │ Admin API │
└─────────────────┘ └──────────────────┘ └──────────────┘
│
▼
┌──────────────────┐
│ token-store.json │
│ (Access Token) │
└──────────────────┘
┌──────────┐ 1. /auth/start ┌──────────────────┐
│ Browser │◄────────────────────►│ OAuth Server │
│ │ │ (localhost:3847) │
└─────┬─────┘ └──────────────────┘
│ ▲
│ 2. Redirect to Shopify │ 5. Save token
▼ │
┌──────────────┐ 3. Authorize ┌──────────────────────────────┐
│ Shopify │───────────────►│ Cloudflare Worker │
│ Grant Screen│ 4. Callback │ (your-oauth-callback. │
└──────────────┘ + Auth Code │ workers.dev) │
│ │ │
│ Redirect to localhost:3847 │
│ /callback │
└──────────────────────────────┘
- Node.js >= 18
- A Shopify App (created in the Dev Dashboard)
- A Cloudflare Worker for URL redirect (OAuth callback)
npm installCopy .env.example to .env and fill in your values:
cp .env.example .envRequired values from the Shopify Dev Dashboard:
- SHOPIFY_API_KEY → Client ID of your app
- SHOPIFY_API_SECRET → Client Secret of your app
- SHOPIFY_DOMAIN → Your myshopify.com domain
- SHOPIFY_REDIRECT_URL → Your Cloudflare Worker callback URL
In the Shopify Dev Dashboard under your app:
- Set App URL to your Cloudflare Worker domain (e.g.
https://your-oauth-callback.workers.dev) - Add Allowed redirection URLs:
https://your-oauth-callback.workers.dev/callback
Your Cloudflare Worker must redirect OAuth callbacks from Shopify to your local server.
Example Worker:
export default {
async fetch(request: Request): Promise<Response> {
const url = new URL(request.url);
// OAuth callback from Shopify → Redirect to local server
if (url.pathname === "/callback") {
const localUrl = `http://localhost:3847/callback${url.search}`;
return Response.redirect(localUrl, 302);
}
// App URL request from Shopify → Redirect to local server
if (url.searchParams.has("shop")) {
const localUrl = `http://localhost:3847${url.pathname}${url.search}`;
return Response.redirect(localUrl, 302);
}
return new Response("Shopify OAuth Endpoint active.", { status: 200 });
},
};npm run oauthThis starts a local server and opens the browser. Log in to Shopify and grant the permissions. The access token is automatically saved to token-store.json.
Add the following to your Cursor configuration (~/.cursor/mcp.json):
{
"mcpServers": {
"shopify-manager": {
"command": "node",
"args": ["/ABSOLUTE/PATH/TO/src/mcp-server.js"],
"env": {
"SHOPIFY_DOMAIN": "your-store.myshopify.com",
"SHOPIFY_API_VERSION": "2026-01"
}
}
}
}| Tool | Description |
|---|---|
shop_info |
Shop basics (name, domain, plan, currency) |
shop_locales |
Configured languages |
shop_policies |
Privacy, terms of service, refund policies |
available_shipping_countries |
Shipping destinations |
| Tool | Description |
|---|---|
list_products |
List products with filtering & pagination |
get_product |
Product details with variants & images |
create_product |
Create a new product |
update_product |
Update a product |
delete_product |
Delete a product |
list_collections |
List collections |
| Tool | Description |
|---|---|
list_orders |
List orders with filtering |
get_order |
Order details |
update_order |
Update an order |
cancel_order |
Cancel an order |
create_draft_order |
Create a draft order |
| Tool | Description |
|---|---|
list_customers |
List customers |
get_customer |
Customer details |
create_customer |
Create a new customer |
update_customer |
Update a customer |
delete_customer |
Delete a customer |
| Tool | Description |
|---|---|
list_locations |
List locations |
get_inventory_levels |
Show inventory levels |
adjust_inventory |
Adjust inventory |
get_product_inventory |
Full product inventory |
| Tool | Description |
|---|---|
list_discount_codes |
List discount codes |
create_basic_discount |
Create a discount code |
list_automatic_discounts |
List automatic discounts |
| Tool | Description |
|---|---|
get_order_count |
Count orders |
get_product_count |
Count products |
get_customer_count |
Count customers |
shop_dashboard_summary |
Dashboard overview |
graphql_query |
Run arbitrary GraphQL queries |
The OAuth 2.0 Authorization Code Grant flow works as follows:
- Initiation: User starts the OAuth flow via
http://localhost:3847/auth/start - Redirect to Shopify: The app redirects the user to Shopify's authorization page with:
client_id(App API Key)scope(requested permissions)redirect_uri(Cloudflare Worker URL)state(nonce for CSRF protection)
- User authorizes: The store owner grants the requested permissions
- Callback: Shopify sends an authorization code to the redirect_uri
- Cloudflare Worker: Redirects the callback to the local OAuth server
- Security checks:
- HMAC verification (message authenticity from Shopify)
- State/nonce comparison (CSRF protection)
- Shop domain validation
- Token exchange: The authorization code is exchanged for an access token via
POST https://{shop}/admin/oauth/access_token - Persistence: The token is saved to
token-store.json
- Never commit
token-store.jsonor.env(both are in.gitignore) - The
SHOPIFY_API_SECRETis confidential - Access tokens have the permissions of the configured scopes
- If you suspect token compromise: rotate the token via the Shopify Dev Dashboard
MIT