Manage your entire Shopify store through conversation with AI. This complete guide includes all the code, step-by-step instructions, and troubleshooting you need. Expected setup time: 30 minutes.
📑 Table of Contents
- What is an MCP Server?
- What You’ll Build
- Prerequisites Checklist
- Complete Setup Guide (6 Steps)
- Testing Your Setup
- Troubleshooting
- Using Your Shopify MCP Server
🤖 What is an MCP Server?
MCP (Model Context Protocol) is an open standard that allows AI assistants like Claude to connect directly to external systems.
Simple Explanation:
- Without MCP: You ask Claude to create product descriptions, then YOU manually copy/paste them into Shopify
- With MCP: You ask Claude to create and publish products, and Claude does EVERYTHING automatically
Think of it like giving Claude a key to your Shopify admin panel – but in a secure, controlled way.
🎯 What You’ll Build
By the end of this guide, you’ll be able to tell Claude things like:
“Create a product called ‘AI-Powered Smartwatch’ priced at $299, with a description highlighting fitness tracking and health monitoring features, and publish it to my store”
And Claude will:
- ✅ Create the complete product listing
- ✅ Add the description and pricing
- ✅ Publish it to your Shopify store
- ✅ Give you the product link
What You Can Do:
Create, update, search, and delete products
View orders, check status, and analyze sales
Access customer data and insights
Monitor stock levels across locations
📋 Prerequisites Checklist
Before you begin, make sure you have everything you need. Check each item:
✅ Python 3.8 or Higher
Test by running: python --version in your terminal
Download from python.org if needed
✅ Claude Desktop Application
Download from claude.ai/download
✅ Shopify Store
You need admin access to create custom apps
Free option: Create a development store via Shopify Partners
✅ 30 Minutes
To complete the setup without rushing
✅ Basic Terminal Skills
You’ll need to run a few simple commands
Verify Your Python Installation
Open your terminal (Command Prompt on Windows, Terminal on Mac) and run:
python --version
You should see something like: Python 3.8.0 or higher
💡 Tip: If python --version doesn’t work, try python3 --version on Mac
⚙️ Complete Setup Guide
Step 1: Create Your Project Folder
First, create a folder where all your MCP server files will live.
Windows:
Create a folder at: C:\Users\YourName\shopify-mcp\
Replace YourName with your actual Windows username
Mac/Linux:
Create a folder at: /Users/YourName/shopify-mcp/
Or use the terminal:
mkdir -p ~/shopify-mcp cd ~/shopify-mcp
Step 2: Create the Server Files
You need to create 2 files in your shopify-mcp folder. I’ll provide the complete code for both.
File 1: requirements.txt
This file lists the Python libraries we need.
mcp>=1.0.0 httpx>=0.27.0 python-dotenv>=1.0.0
💡 How to create this file:
- Open Notepad (Windows) or TextEdit (Mac) or any text editor
- Copy the 3 lines above
- Paste them into the editor
- Save as
requirements.txtin your shopify-mcp folder
File 2: shopify_mcp_server.py
This is the main server code. Copy ALL of this code:
#!/usr/bin/env python3
"""
Shopify MCP Server
Connects to Shopify Admin API to manage store
"""
import asyncio
import os
import httpx
from typing import Any
from mcp.server import Server
from mcp.types import Tool, TextContent
import mcp.server.stdio
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
# Shopify configuration
SHOPIFY_STORE_DOMAIN = os.getenv("SHOPIFY_STORE_DOMAIN", "")
SHOPIFY_ACCESS_TOKEN = os.getenv("SHOPIFY_ACCESS_TOKEN", "")
server = Server("shopify-mcp")
async def shopify_request(method: str, endpoint: str, data: dict = None) -> dict:
"""Make authenticated request to Shopify Admin API"""
url = f"https://{SHOPIFY_STORE_DOMAIN}/admin/api/2024-10/{endpoint}"
headers = {
"X-Shopify-Access-Token": SHOPIFY_ACCESS_TOKEN,
"Content-Type": "application/json"
}
async with httpx.AsyncClient(timeout=30.0) as client:
if method == "GET":
response = await client.get(url, headers=headers)
elif method == "POST":
response = await client.post(url, headers=headers, json=data)
elif method == "PUT":
response = await client.put(url, headers=headers, json=data)
elif method == "DELETE":
response = await client.delete(url, headers=headers)
response.raise_for_status()
return response.json()
@server.list_tools()
async def list_tools() -> list[Tool]:
"""List available Shopify management tools"""
return [
Tool(
name="get_products",
description="Fetch products from the Shopify store",
inputSchema={
"type": "object",
"properties": {
"limit": {
"type": "number",
"description": "Number of products to fetch (max 250)",
"default": 50
},
"fields": {
"type": "string",
"description": "Comma-separated list of fields to include",
"default": "id,title,vendor,product_type,created_at,updated_at"
}
}
}
),
Tool(
name="get_product_by_id",
description="Get a specific product by ID",
inputSchema={
"type": "object",
"properties": {
"product_id": {
"type": "string",
"description": "The ID of the product"
}
},
"required": ["product_id"]
}
),
Tool(
name="create_product",
description="Create a new product in the Shopify store",
inputSchema={
"type": "object",
"properties": {
"title": {
"type": "string",
"description": "Product title"
},
"body_html": {
"type": "string",
"description": "Product description in HTML"
},
"vendor": {
"type": "string",
"description": "Product vendor"
},
"product_type": {
"type": "string",
"description": "Product type"
},
"price": {
"type": "string",
"description": "Product price"
}
},
"required": ["title"]
}
),
Tool(
name="update_product",
description="Update an existing product",
inputSchema={
"type": "object",
"properties": {
"product_id": {
"type": "string",
"description": "The ID of the product to update"
},
"title": {
"type": "string",
"description": "New product title"
},
"body_html": {
"type": "string",
"description": "New product description"
},
"vendor": {
"type": "string",
"description": "New vendor"
},
"product_type": {
"type": "string",
"description": "New product type"
}
},
"required": ["product_id"]
}
),
Tool(
name="search_products",
description="Search for products by title or vendor",
inputSchema={
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Search query"
}
},
"required": ["query"]
}
),
Tool(
name="get_orders",
description="Fetch orders from the Shopify store",
inputSchema={
"type": "object",
"properties": {
"limit": {
"type": "number",
"description": "Number of orders to fetch (max 250)",
"default": 50
},
"status": {
"type": "string",
"description": "Filter orders by status (open, closed, cancelled, any)",
"default": "any"
}
}
}
),
Tool(
name="get_customers",
description="Fetch customers from the Shopify store",
inputSchema={
"type": "object",
"properties": {
"limit": {
"type": "number",
"description": "Number of customers to fetch (max 250)",
"default": 50
}
}
}
),
Tool(
name="get_inventory_levels",
description="Get inventory levels for products",
inputSchema={
"type": "object",
"properties": {
"limit": {
"type": "number",
"description": "Number of inventory items to fetch",
"default": 50
}
}
}
)
]
@server.call_tool()
async def call_tool(name: str, arguments: Any) -> list[TextContent]:
"""Execute Shopify management tools"""
try:
if name == "get_products":
limit = arguments.get("limit", 50)
fields = arguments.get("fields", "id,title,vendor,product_type")
data = await shopify_request("GET", f"products.json?limit={limit}&fields={fields}")
products = data.get("products", [])
result = f"Found {len(products)} products:\\n\\n"
for product in products:
result += f"ID: {product.get('id')}\\n"
result += f"Title: {product.get('title')}\\n"
result += f"Vendor: {product.get('vendor', 'N/A')}\\n"
result += f"Type: {product.get('product_type', 'N/A')}\\n\\n"
return [TextContent(type="text", text=result)]
elif name == "get_product_by_id":
product_id = arguments["product_id"]
data = await shopify_request("GET", f"products/{product_id}.json")
product = data.get("product", {})
result = f"Product Details:\\n\\n"
result += f"ID: {product.get('id')}\\n"
result += f"Title: {product.get('title')}\\n"
result += f"Vendor: {product.get('vendor', 'N/A')}\\n"
result += f"Type: {product.get('product_type', 'N/A')}\\n"
result += f"Status: {product.get('status', 'N/A')}\\n"
result += f"Created: {product.get('created_at', 'N/A')}\\n"
return [TextContent(type="text", text=result)]
elif name == "create_product":
product_data = {
"product": {
"title": arguments["title"],
"body_html": arguments.get("body_html", ""),
"vendor": arguments.get("vendor", ""),
"product_type": arguments.get("product_type", "")
}
}
# Add variant with price if provided
if "price" in arguments:
product_data["product"]["variants"] = [{
"price": arguments["price"]
}]
data = await shopify_request("POST", "products.json", product_data)
product = data.get("product", {})
result = f"Product created successfully!\\n\\n"
result += f"ID: {product.get('id')}\\n"
result += f"Title: {product.get('title')}\\n"
result += f"Status: {product.get('status')}\\n"
return [TextContent(type="text", text=result)]
elif name == "update_product":
product_id = arguments["product_id"]
product_data = {"product": {}}
if "title" in arguments:
product_data["product"]["title"] = arguments["title"]
if "body_html" in arguments:
product_data["product"]["body_html"] = arguments["body_html"]
if "vendor" in arguments:
product_data["product"]["vendor"] = arguments["vendor"]
if "product_type" in arguments:
product_data["product"]["product_type"] = arguments["product_type"]
data = await shopify_request("PUT", f"products/{product_id}.json", product_data)
product = data.get("product", {})
result = f"Product updated successfully!\\n\\n"
result += f"ID: {product.get('id')}\\n"
result += f"Title: {product.get('title')}\\n"
return [TextContent(type="text", text=result)]
elif name == "search_products":
query = arguments["query"]
data = await shopify_request("GET", f"products.json?title={query}")
products = data.get("products", [])
result = f"Found {len(products)} products matching '{query}':\\n\\n"
for product in products:
result += f"ID: {product.get('id')}\\n"
result += f"Title: {product.get('title')}\\n\\n"
return [TextContent(type="text", text=result)]
elif name == "get_orders":
limit = arguments.get("limit", 50)
status = arguments.get("status", "any")
data = await shopify_request("GET", f"orders.json?limit={limit}&status={status}")
orders = data.get("orders", [])
result = f"Found {len(orders)} orders:\\n\\n"
for order in orders:
result += f"Order #{order.get('order_number')}\\n"
result += f"Status: {order.get('financial_status')}\\n"
result += f"Total: ${order.get('total_price')}\\n"
result += f"Date: {order.get('created_at')}\\n\\n"
return [TextContent(type="text", text=result)]
elif name == "get_customers":
limit = arguments.get("limit", 50)
data = await shopify_request("GET", f"customers.json?limit={limit}")
customers = data.get("customers", [])
result = f"Found {len(customers)} customers:\\n\\n"
for customer in customers:
result += f"ID: {customer.get('id')}\\n"
result += f"Name: {customer.get('first_name')} {customer.get('last_name')}\\n"
result += f"Email: {customer.get('email')}\\n"
result += f"Orders: {customer.get('orders_count', 0)}\\n\\n"
return [TextContent(type="text", text=result)]
elif name == "get_inventory_levels":
limit = arguments.get("limit", 50)
data = await shopify_request("GET", f"inventory_levels.json?limit={limit}")
levels = data.get("inventory_levels", [])
result = f"Found {len(levels)} inventory items:\\n\\n"
for level in levels:
result += f"Available: {level.get('available', 0)}\\n"
result += f"Location ID: {level.get('location_id')}\\n\\n"
return [TextContent(type="text", text=result)]
else:
return [TextContent(type="text", text=f"Unknown tool: {name}")]
except httpx.HTTPStatusError as e:
return [TextContent(type="text", text=f"HTTP Error: {e.response.status_code} - {e.response.text}")]
except Exception as e:
return [TextContent(type="text", text=f"Error: {str(e)}")]
async def main():
"""Run the MCP server"""
async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
await server.run(
read_stream,
write_stream,
server.create_initialization_options()
)
if __name__ == "__main__":
asyncio.run(main())
💡 How to create this file:
- Open Notepad (Windows) or TextEdit (Mac) or VS Code
- Copy ALL the code above (scroll through the entire code block)
- Paste it into the editor
- Save as
shopify_mcp_server.pyin your shopify-mcp folder - Important: Make sure it’s saved as .py, not .txt!
Your Folder Should Now Look Like This:
shopify-mcp/ ├── requirements.txt └── shopify_mcp_server.py
Step 3: Install Python Dependencies
Now we need to install the Python libraries that the server needs.
- Open your terminal/command prompt
- Navigate to your shopify-mcp folder:
cd C:\Users\YourName\shopify-mcp
(Replace with your actual path)
- Run this command:
pip install -r requirements.txt
- Wait for installation to complete (about 30 seconds)
✅ Success looks like:
Successfully installed mcp-1.0.0 httpx-0.27.0 python-dotenv-1.0.0
⚠️ If you see errors:
- Try using
pip3instead ofpip - Make sure you’re in the correct folder
- Check that Python is properly installed
Step 4: Get Your Shopify API Credentials
Now we need to create a custom app in Shopify to get API access.
4.1: Log into Shopify Admin
- Go to
https://[your-store-name].myshopify.com/admin - Log in with your admin credentials
4.2: Create a Custom App
- Click Settings (gear icon) in the bottom left
- Click Apps and sales channels
- Click Develop apps button (top right)
- Click Allow custom app development (if prompted)
- Click Create an app
4.3: Name Your App
- App name:
Claude MCP Server - App developer: Your name
- Click Create app
4.4: Configure API Scopes
- Click Configuration tab
- In Admin API access scopes, click Configure
- Enable these scopes:
- ✅
read_products - ✅
write_products - ✅
read_orders - ✅
read_customers - ✅
read_inventory
- ✅
- Click Save
4.5: Install the App
- Click Install app button
- Click Install to confirm
4.6: Get Your API Credentials
- Click API credentials tab
- Copy these two values:
- Admin API access token: Starts with
shpat_– Click “Reveal token once” and copy immediately - Store domain: Your store domain (e.g.,
your-store.myshopify.com)
- Admin API access token: Starts with
⚠️ IMPORTANT: Save these credentials securely! You won’t see the token again. Store them in a password manager or secure note.
Step 5: Configure Claude Desktop
Now we’ll tell Claude Desktop about your MCP server.
5.1: Find Your Config File
Windows:
Press Win + R, type %APPDATA%\Claude, press Enter
Then open claude_desktop_config.json
Mac:
In Finder, press Cmd + Shift + G, paste: ~/Library/Application Support/Claude/
Then open claude_desktop_config.json
5.2: Edit the Config File
Open it with Notepad (Windows) or TextEdit (Mac). Add this configuration (replace the placeholder values with YOUR actual information):
{
"mcpServers": {
"shopify": {
"command": "python",
"args": ["C:\\Users\\YourName\\shopify-mcp\\shopify_mcp_server.py"],
"env": {
"SHOPIFY_STORE_DOMAIN": "your-store.myshopify.com",
"SHOPIFY_ACCESS_TOKEN": "shpat_your_access_token_here"
}
}
}
}
⚠️ IMPORTANT – Replace These Values:
C:\\Users\\YourName\\shopify-mcp\\shopify_mcp_server.py→ Your ACTUAL full path- Windows: Use double backslashes (
\\) - Mac: Use forward slashes:
/Users/YourName/shopify-mcp/shopify_mcp_server.py
- Windows: Use double backslashes (
your-store.myshopify.com→ Your Shopify store domainshpat_your_access_token_here→ Your actual Admin API access token
💡 If you already have other MCP servers configured:
Add the shopify section inside the existing mcpServers object, like this:
{
"mcpServers": {
"existing-server": {
...existing config...
},
"shopify": {
...new shopify config...
}
}
}
Make sure to save your changes!
Step 6: Restart Claude Desktop
- Completely quit Claude Desktop (don’t just close the window)
- Windows: Right-click the system tray icon and choose “Quit”
- Mac: Press
Cmd + Q
- Wait 5 seconds
- Restart Claude Desktop
🧪 Testing Your Setup
Let’s make sure everything is working!
Test 1: Check Store Connection
Open a new conversation with Claude and type:
List my Shopify products
✅ If it works, Claude will respond with:
- A list of your actual products
- Product IDs, titles, vendors, and types
🎉 Congrats! Your MCP server is working!
Test 2: Create a Product
Try creating a test product:
Create a product called “Test Product” priced at $9.99 with a description “This is a test product from Claude”
Then check your Shopify admin to see if the product appears!
Test 3: Search Products
Try searching for products:
Search for products with “test” in the name
✅ All three tests passed? You’re ready to use your MCP server!
🔧 Troubleshooting Common Issues
Problem: “HTTP Error: 401” or “Unauthorized”
This means authentication failed.
Solutions:
- Double-check your access token – make sure you copied it correctly (no extra spaces)
- Verify your store domain is in the format:
your-store.myshopify.com(include myshopify.com) - Make sure you clicked “Install app” in Shopify admin
- Try generating a new access token
Problem: “Connection Error” or “Cannot connect to store”
The server can’t reach your Shopify store.
Solutions:
- Make sure your store domain includes
myshopify.com - Verify your Shopify store is online and accessible
- Check if your network has a firewall blocking API requests
- Try accessing
https://your-store.myshopify.com/admin/api/2024-10/products.jsonin your browser
Problem: Claude doesn’t seem to have the Shopify tools
The MCP server didn’t load properly.
Solutions:
- Make sure you completely quit and restarted Claude Desktop (not just minimized)
- Check the file path in your config – it must be the FULL path to shopify_mcp_server.py
- Verify Python is installed: run
python --versionin terminal - Check for JSON syntax errors in claude_desktop_config.json (missing commas, brackets)
- Look at Claude Desktop logs for error messages
Problem: “Module not found” or Import Errors
Python dependencies aren’t installed.
Solutions:
- Navigate to your shopify-mcp folder in terminal
- Run
pip install -r requirements.txtagain - Try using
pip3instead ofpip - Make sure you’re in the correct folder when installing
Common Beginner Mistakes
Watch out for these common errors:
- ❌ Missing
myshopify.comfrom store domain - ❌ Using relative paths instead of absolute paths in config
- ❌ Not restarting Claude Desktop after config changes
- ❌ JSON syntax errors (missing commas, mismatched brackets)
- ❌ Saving shopify_mcp_server.py as a .txt file instead of .py
- ❌ Not copying the entire server code (missing the bottom part)
- ❌ Forgetting to click “Install app” in Shopify after creating it
🚀 Using Your Shopify MCP Server
Now that it’s working, here are powerful ways to use it:
Product Management
“Create 5 products for a tech accessories store: wireless earbuds, phone cases, charging cables, screen protectors, and phone stands. Price them between $15-$50 and write compelling descriptions for each”
Inventory Checks
“Show me all products and tell me which ones might need restocking based on their inventory levels”
Order Analysis
“Get my recent orders and create a summary of total sales, average order value, and most popular products”
Bulk Updates
“Find all products with ‘summer’ in the name and update their descriptions to mention our fall sale”
Customer Insights
“Show me my top customers by order count and help me create a VIP customer appreciation plan”
🎉 Conclusion
Congratulations! You’ve successfully connected Claude to your Shopify store through an MCP server. You can now manage your entire store through natural conversation.
This workflow saves hours of manual work and makes store management feel effortless. Instead of context-switching between tools, you can do everything in one place through conversation.
What’s Next?
- Explore more complex product operations
- Automate your daily store management tasks
- Use Claude to analyze sales trends and customer behavior
- Create batch operations for seasonal updates
- Share your implementation with the community
💬 Need Help?
Having trouble with the setup? Questions about the integration? Drop a comment below and I’ll help you troubleshoot!
Found this guide helpful? Check out my other technical tutorials or explore my portfolio to see more of my work!