Shopify Admin API tools: products, media, orders, customers, inventory, and sales channels
MCP Server for Shopify API, enabling interaction with store data through GraphQL API. This server provides tools for managing products, customers, orders, and more.
π¦ Package Name: @kisasa/shopify-mcp
π Command: shopify-mcp (NOT shopify-mcp-server)
Built on lucasjahn/shopify-mcp, itself a fork of GeLi2001/shopify-mcp. See Credits.
fields parameter to fetch only the data you needcountOnly to get result counts without fetching dataThis server supports two authentication methods:
As of January 1, 2026, new Shopify apps are created in the Dev Dashboard and use OAuth client credentials instead of static access tokens.
From your Shopify admin, go to Settings > Apps and sales channels
Click Develop apps > Build app in dev dashboard
Create a new app and configure Admin API scopes. The core scopes are:
read_products, write_productsread_customers, write_customersread_orders, write_ordersSome tools need additional scopes. Add only the ones for features you use:
| Scopes | Needed for |
|---|---|
read_all_orders | Orders older than 60 days (otherwise order tools only see recent orders) |
read_inventory, write_inventory | get-inventory-levels, inventory-set-quantities |
read_locations | get-locations, get-inventory-levels |
read_markets | get-markets |
read_draft_orders, write_draft_orders | create-draft-order, complete-draft-order |
read_merchant_managed_fulfillment_orders, write_merchant_managed_fulfillment_orders | get-fulfillment-orders, create-fulfillment |
read_publications, write_publications | get-publications, get-product-publications, publish-product, unpublish-product |
For every tool, the full list is:
read_products,write_products,read_customers,write_customers,read_orders,write_orders,read_all_orders,read_inventory,write_inventory,read_locations,read_markets,read_draft_orders,write_draft_orders,read_merchant_managed_fulfillment_orders,write_merchant_managed_fulfillment_orders,read_publications,write_publications
After changing scopes, release a new app version, approve it on the store, and restart the MCP server so it fetches a token with the new scopes.
Install the app on your store
Copy your Client ID and Client Secret from the app's API credentials
The server will automatically exchange these for an access token and refresh it before it expires (tokens are valid for ~24 hours).
If you have an existing custom app with a static shpat_ access token, you can still use it directly.
Client Credentials (recommended):
{
"mcpServers": {
"shopify": {
"command": "npx",
"args": [
"@kisasa/shopify-mcp",
"--clientId",
"<YOUR_CLIENT_ID>",
"--clientSecret",
"<YOUR_CLIENT_SECRET>",
"--domain",
"<YOUR_SHOP>.myshopify.com"
]
}
}
}
Client Credentials via environment variables (keeps secrets out of the command line):
{
"mcpServers": {
"shopify": {
"command": "npx",
"args": ["-y", "@kisasa/shopify-mcp"],
"env": {
"MYSHOPIFY_DOMAIN": "<YOUR_SHOP>.myshopify.com",
"SHOPIFY_CLIENT_ID": "<YOUR_CLIENT_ID>",
"SHOPIFY_CLIENT_SECRET": "<YOUR_CLIENT_SECRET>"
}
}
}
}
To connect more than one store, add one entry per store with a different name (e.g. "shopify-store-a", "shopify-store-b").
Static Access Token (legacy):
{
"mcpServers": {
"shopify": {
"command": "npx",
"args": [
"@kisasa/shopify-mcp",
"--accessToken",
"<YOUR_ACCESS_TOKEN>",
"--domain",
"<YOUR_SHOP>.myshopify.com"
]
}
}
}
Locations for the Claude Desktop config file:
~/Library/Application Support/Claude/claude_desktop_config.json%APPDATA%/Claude/claude_desktop_config.jsonClient Credentials:
claude mcp add shopify -- npx @kisasa/shopify-mcp \
--clientId YOUR_CLIENT_ID \
--clientSecret YOUR_CLIENT_SECRET \
--domain your-store.myshopify.com
Static Access Token (legacy):
claude mcp add shopify -- npx @kisasa/shopify-mcp \
--accessToken YOUR_ACCESS_TOKEN \
--domain your-store.myshopify.com
If you prefer to use environment variables instead of command-line arguments:
Create a .env file with your Shopify credentials:
Client Credentials:
SHOPIFY_CLIENT_ID=your_client_id
SHOPIFY_CLIENT_SECRET=your_client_secret
MYSHOPIFY_DOMAIN=your-store.myshopify.com
Static Access Token (legacy):
SHOPIFY_ACCESS_TOKEN=your_access_token
MYSHOPIFY_DOMAIN=your-store.myshopify.com
Run the server with npx:
npx @kisasa/shopify-mcp
If you want to install the package globally:
npm install -g @kisasa/shopify-mcp
Then run it:
shopify-mcp --clientId=<ID> --clientSecret=<SECRET> --domain=<YOUR_SHOP>.myshopify.com
--apiVersion: Specify the Shopify API version (default: 2026-10). Can also be set via SHOPIFY_API_VERSION environment variable.β οΈ Important: If you see errors about "SHOPIFY_ACCESS_TOKEN environment variable is required" when using command-line arguments, you might have a different package installed. Make sure you're using @kisasa/shopify-mcp, not shopify-mcp-server or the unscoped shopify-mcp.
All list query tools (get-products, get-customers, get-orders, get-customer-orders, get-collections) support:
after / before (cursor strings), with pageInfo in the response (hasNextPage, hasPreviousPage, startCursor, endCursor)sortKey (enum specific to each resource) and reverse (boolean)query or searchQuery parameter accepting Shopify query syntaxfields parameter to fetch only specific fields, reducing response sizecountOnly parameter returns { count: N } without fetching any resource data β useful for sizing queries before paginatingget-products
searchTitle (string, optional): Filter products by title (wraps in title:*...*)limit (number, default: 10): Maximum number of products to returnquery (string, optional): Raw Shopify query string (e.g. "status:active vendor:Nike tag:sale")sortKey (string, optional): One of CREATED_AT, ID, INVENTORY_TOTAL, PRODUCT_TYPE, PUBLISHED_AT, RELEVANCE, TITLE, UPDATED_AT, VENDORreverse (boolean, optional): Reverse the sort orderafter / before (string, optional): Pagination cursorsfields (array, optional): Fields to fetch. Available: id, title, description, handle, status, createdAt, updatedAt, totalInventory, vendor, productType, tags, mediaCount, priceRange, media, variants (first 10, with id, title, price, inventoryQuantity, sku, barcode, inventoryPolicy)countOnly (boolean, optional): Return only { count: N }get-product-by-id
productId (string, required): Shopify product GIDproductType, descriptionHtml, seo, options (with optionValues), media (images), variants, collections, tags, vendor, price range, inventorycreate-product
productOptions, Shopify registers all option values but only creates one default variant (first value of each option, price $0). Use manage-product-variants with strategy: REMOVE_STANDALONE_VARIANT afterward to create all real variants with prices.title (string, required): Title of the productdescriptionHtml (string, optional): Description with HTMLhandle (string, optional): URL slug. Auto-generated from title if omittedvendor (string, optional): Vendor of the productproductType (string, optional): Type of the producttags (array of strings, optional): Product tagsstatus (string, optional): "ACTIVE", "DRAFT", or "ARCHIVED". Default "DRAFT"seo (object, optional): { title, description } for search enginesmetafields (array of objects, optional): Custom metafields (namespace, key, value, type)productOptions (array of objects, optional): Options to create inline, e.g. [{ name: "Size", values: [{ name: "S" }, { name: "M" }] }]. Max 3 options.collectionsToJoin (array of strings, optional): Collection GIDs to add the product toupdate-product
id (string, required): Shopify product GIDtitle (string, optional): New titledescriptionHtml (string, optional): New descriptionhandle (string, optional): New URL slugvendor (string, optional): New vendorproductType (string, optional): New product typetags (array of strings, optional): New tags (overwrites existing)status (string, optional): "ACTIVE", "DRAFT", or "ARCHIVED"seo (object, optional): { title, description } for search enginesmetafields (array of objects, optional): Metafields to set or updatecollectionsToJoin (array of strings, optional): Collection GIDs to add the product tocollectionsToLeave (array of strings, optional): Collection GIDs to remove the product fromredirectNewHandle (boolean, optional): If true, old handle redirects to new handledelete-product
id (string, required): Shopify product GIDmanage-product-options
productId (string, required): Shopify product GIDaction (string, required): "create", "update", or "delete"variantStrategy (string, optional): "LEAVE_AS_IS" (default) or "CREATE" β controls whether new variant combinations are generated when adding optionsaction: "create":
options (array, required): Options to create, e.g. [{ name: "Size", values: ["S", "M", "L"] }]action: "update":
optionId (string, required): Option GID to updatename (string, optional): New name for the optionposition (number, optional): New positionvaluesToAdd (array of strings, optional): Values to addvaluesToDelete (array of strings, optional): Value GIDs to removeaction: "delete":
optionIds (array of strings, required): Option GIDs to deletemanage-product-variants
productId (string, required): Shopify product GIDstrategy (string, optional): How to handle the default variant when creating. "DEFAULT" (removes "Default Title" automatically), "REMOVE_STANDALONE_VARIANT" (recommended for full control), or "PRESERVE_STANDALONE_VARIANT"variants (array, required): Variants to create or update. Each variant:
id (string, optional): Variant GID for updates. Omit to create newprice (string, optional): Price, e.g. "49.00"compareAtPrice (string, optional): Compare-at price for showing discountssku (string, optional): SKU (mapped to inventoryItem.sku)tracked (boolean, optional): Whether inventory is tracked. Set false for print-on-demandtaxable (boolean, optional): Whether the variant is taxablebarcode (string, optional): Barcodeweight (number, optional): Weight of the variantweightUnit (string, optional): "GRAMS", "KILOGRAMS", "OUNCES", or "POUNDS"optionValues (array, optional): Option values, e.g. [{ optionName: "Size", name: "A4" }]mediaId (string, optional): Product media GID (gid://shopify/MediaImage/...) to use as the variant's image. The media must already be attached to the product (see add-product-media) and should be READYinventoryPolicy (string, optional): "DENY" stops sales at 0 inventory, "CONTINUE" allows overselling{ created, updated, userErrors }. Each variant includes inventoryPolicy, mediaId, and imageUrl. userErrors are returned (tagged operation: "create" or "update") rather than thrown, so always check themdelete-product-variants
productId (string, required): Shopify product GIDvariantIds (array of strings, required): Variant GIDs to deleteadd-product-media
productUpdate with media; productCreateMedia is deprecated)productId (string, required): Shopify product GIDmedia (array, required): Images to attach, each { originalSource, alt? }. originalSource must be a publicly accessible URL that Shopify can download{ productId, media, userErrors }, where each new media item has id, status, alt, imageUrl, and mediaErrorsUPLOADED or PROCESSING with imageUrl: null; use get-product-media to confirm they reach READYget-product-media
productId (string, required): Shopify product GIDid, status (UPLOADED / PROCESSING / READY / FAILED), mediaContentType, alt, imageUrl, and mediaErrors (explains a FAILED status)Example: give a variant its own image
add-product-media with the image URLget-product-media until the new item is READY, and copy its idmanage-product-variants with { id: "<variant GID>", mediaId: "<media GID>" }These tools work with sales channels (publications with an app catalog, such as Online Store and Point of Sale). Market and B2B catalogs are not included. Requires the read_publications and write_publications scopes.
get-publications
id, name (e.g. "Online Store"), handle (e.g. "online_store"), autoPublish, and supportsFuturePublishingget-product-publications
productId (string, required): Shopify product GID{ productId, title, publications }, with publicationId, name, published, and publishDate for every channelpublish-product
publishablePublish)productId (string, required): Shopify product GIDpublicationIds (array of strings, optional): Publication GIDs from get-publicationspublicationNames (array of strings, optional): Channel names exactly as returned by get-publications, e.g. ["Online Store", "Point of Sale"]publicationIds or publicationNames. A name that matches no channel, or more than one, is rejected before anything changesuserErrorsunpublish-product
publishableUnpublish)publish-productuserErrorsget-customers
searchQuery (string, optional): Freetext or Shopify query syntax (e.g. "country:US tag:vip orders_count:>5")limit (number, default: 10): Maximum number of customers to returnsortKey (string, optional): One of CREATED_AT, ID, LAST_UPDATE, LOCATION, NAME, ORDERS_COUNT, RELEVANCE, TOTAL_SPENT, UPDATED_ATreverse (boolean, optional): Reverse the sort orderafter / before (string, optional): Pagination cursorsget-customer-by-id
id (string, required): Shopify customer ID (numeric only, e.g. "6276879810626")create-customer
firstName (string, optional): Customer's first namelastName (string, optional): Customer's last nameemail (string, optional): Customer's email addressphone (string, optional): Customer's phone numbertags (array of strings, optional): Tags to applynote (string, optional): Note about the customertaxExempt (boolean, optional): Whether the customer is exempt from taxesmetafields (array of objects, optional): Custom metafields (namespace, key, value, type)addresses (array of objects, optional): Customer addresses (address1, address2, city, provinceCode, zip, country, phone)update-customer
id (string, required): Shopify customer ID (numeric only, e.g. "6276879810626")firstName (string, optional): Customer's first namelastName (string, optional): Customer's last nameemail (string, optional): Customer's email addressphone (string, optional): Customer's phone numbertags (array of strings, optional): Tags to apply to the customernote (string, optional): Note about the customertaxExempt (boolean, optional): Whether the customer is exempt from taxesemailMarketingConsent (object, optional): Email marketing consent settings
marketingState (string, required): "NOT_SUBSCRIBED", "SUBSCRIBED", "UNSUBSCRIBED", or "PENDING"consentUpdatedAt (string, optional): ISO 8601 timestampmarketingOptInLevel (string, optional): "SINGLE_OPT_IN", "CONFIRMED_OPT_IN", or "UNKNOWN"metafields (array of objects, optional): Customer metafieldsdelete-customer
id (string, required): Shopify customer ID (numeric only, e.g. "6276879810626")customer-merge
customerOneId (string, required): GID of the first customercustomerTwoId (string, required): GID of the second customeroverrideFields (object, optional): Override which fields to keep from which customer (firstName, lastName, email, phone, defaultAddress, note, tags)manage-customer-address
customerId (string, required): Customer GIDaction (string, required): "create", "update", or "delete"addressId (string, optional): Address GID (required for update/delete)address (object, optional): Address fields (required for create/update): address1, address2, city, company, countryCode, firstName, lastName, phone, provinceCode, zipsetAsDefault (boolean, optional): Set as customer's default addressget-orders
status (string, optional): "any", "open", "closed", or "cancelled". Default "any"limit (number, default: 10): Maximum number of orders to returnquery (string, optional): Raw Shopify query string (e.g. "financial_status:paid fulfillment_status:shipped tag:rush")sortKey (string, optional): One of CREATED_AT, ORDER_NUMBER, TOTAL_PRICE, FINANCIAL_STATUS, FULFILLMENT_STATUS, UPDATED_AT, CUSTOMER_NAME, PROCESSED_AT, ID, RELEVANCEreverse (boolean, optional): Reverse the sort orderafter / before (string, optional): Pagination cursorsget-order-by-id
#77235 or 77235), numeric ID (8054938337547), or full GID (gid://shopify/Order/...)orderId (string, required): Order name, numeric ID, or full GIDupdate-order
id (string, required): Shopify order GIDtags (array of strings, optional): New tags for the orderemail (string, optional): Update customer email on the ordernote (string, optional): Order notesphone (string, optional): Phone number for the orderpoNumber (string, optional): Purchase order numbercustomAttributes (array of objects, optional): Custom key-value attributesmetafields (array of objects, optional): Order metafieldsshippingAddress (object, optional): Shipping address fieldsget-customer-orders
customerId (string, required): Shopify customer ID (numeric only, e.g. "6276879810626")limit (number, default: 10): Maximum number of orders to returnsortKey (string, optional): Same sort keys as get-ordersreverse (boolean, optional): Reverse the sort orderafter / before (string, optional): Pagination cursorsorder-cancel
orderId (string, required): Order GIDreason (string, required): "CUSTOMER", "DECLINED", "FRAUD", "INVENTORY", "OTHER", or "STAFF"restock (boolean, required): Whether to restock inventorynotifyCustomer (boolean, default: false): Notify the customerstaffNote (string, optional): Internal noterefund (boolean, optional): Refund to original payment methodorder-close-open
orderId (string, required): Order GIDaction (string, required): "close" or "open"order-mark-as-paid
orderId (string, required): Order GIDcreate-fulfillment
lineItemsByFulfillmentOrder (array, required): Fulfillment orders and line items to fulfilltrackingInfo (object, optional): { number, url, company } tracking detailsnotifyCustomer (boolean, default: false): Send shipping notificationrefund-create
orderId (string, required): Order GIDrefundLineItems (array, optional): Line items to refund with lineItemId, quantity, restockType (CANCEL/RETURN/NO_RESTOCK), locationIdshipping (object, optional): { amount, fullRefund } shipping refundnote (string, optional): Refund notenotify (boolean, optional): Send refund notificationcreate-draft-order
lineItems (array, required): Product variants (variantId) or custom items (title + price). Max 499customerId (string, optional): Customer GIDemail, phone, note, tags, poNumber (optional)shippingAddress, billingAddress (objects, optional)appliedDiscount (object, optional): { title, value, valueType } order-level discountcomplete-draft-order
draftOrderId (string, required): Draft order GIDpaymentGatewayId (string, optional): Payment gateway GIDget-metafields
ownerId (string, required): GID of any resourcenamespace (string, optional): Filter by namespacefirst (number, default: 25): Number of metafields to returnafter (string, optional): Pagination cursorset-metafields
metafields (array, required): Metafields to set, each with ownerId, key, value, and optional namespace, typedelete-metafields
metafields (array, required): Metafields to delete, each with ownerId, namespace, keyinventory-set-quantities
reason (string, required): Reason for change (e.g. "correction", "cycle_count_available")name (string, required): "available" or "on_hand"quantities (array, required): Items with inventoryItemId, locationId, quantitymanage-tags
id (string, required): GID of the resource (e.g. gid://shopify/Product/123)tags (array of strings, required): Tags to add or removeaction (string, required): "add" or "remove"manage-tags-bulk
ids (array of strings, required): Resource GIDs (up to 100). For larger sets, call multiple timestags (array of strings, required): Tags to add or removeaction (string, required): "add" or "remove"get-collections
query (string, optional): Shopify query syntaxlimit (number, default: 10): Maximum collections to returnsortKey, reverse, after/before: Standard pagination and sortingfields, countOnly: Standard field selection and count-only modeget-collection-by-id
collectionId (string, required): Collection ID (e.g. gid://shopify/Collection/123 or just 123)productsFirst (number, default: 25): Number of products to include (0 to skip)fields (array, optional): Collection-level fields to fetch (id, title, handle, descriptionHtml, sortOrder, templateSuffix, updatedAt, productsCount, ruleSet, image, seo)get-shop-info β Get shop details (name, domain, plan, currency, timezone, etc.)get-metafield-definitions β List metafield definitions for any owner type with paginationget-locations β List all active locations with addresses and fulfillment service infoget-markets β List all markets with regions, currencies, and domainsget-order-transactions β Get payment transactions for an order (gateway, status, amounts, card details)get-fulfillment-orders β Get fulfillment orders for an order (line items, assigned location, status)get-order-refund-details β Get refund history for an order (line items, amounts, restocking)See also inventory-set-quantities under Inventory Management.
get-inventory-levels β Get inventory levels for an item across locationsget-inventory-items β Get inventory items for a product (cost, tracking, country of origin)get-price-lists β List price lists with fixed prices and quantity rulesget-product-variants-detailed β Get detailed variant info (inventory, pricing, images, metafields)The get-orders tool's query parameter supports Shopify search syntax:
| Filter | Example |
|---|---|
name | name:#77235 |
created_at | created_at:>2024-01-01 or created_at:2024-01-01..2024-03-31 |
updated_at | updated_at:>2024-06-01 |
financial_status | financial_status:paid |
fulfillment_status | fulfillment_status:shipped |
status | status:open |
email | email:customer@example.com |
tag / tag_not | tag:vip tag_not:wholesale |
discount_code | discount_code:SUMMER20 |
sku | sku:PROD-001 |
risk_level | risk_level:high |
gateway | gateway:shopify_payments |
test | test:true |
If you encounter issues, check Claude Desktop's MCP logs.
macOS:
tail -n 20 -f ~/Library/Logs/Claude/mcp*.log
Windows (PowerShell):
Get-Content "$env:APPDATA\Claude\logs\mcp*.log" -Tail 20 -Wait
Common causes:
Releases are published automatically by .github/workflows/publish.yml when a version bump reaches main:
version in package.json and both version fields in server.json (they must match, or the workflow fails).main. The workflow publishes to npm (with provenance), publishes to the MCP Registry, and creates a vX.Y.Z GitHub release.Merges that don't change the version publish nothing.
This project builds on the work of:
Thank you to both projects and their contributors.
MIT. See LICENSE. The original copyright notice is retained.
Source-derived launch command. Check the maintainerβs required arguments and credentials before running:
npx -y @kisasa/shopify-mcpMerge this template into ~/Library/Application Support/Claude/claude_desktop_config.json. Keep existing servers. Add any arguments, credentials, and permissions required by the maintainer; this template has not been install-tested.
{
"mcpServers": {
"io-github-kisasa-shopify-mcp": {
"command": "npx",
"args": [
"-y",
"@kisasa/shopify-mcp"
]
}
}
}Restart Claude Desktop completely for changes to take effect. Confirm the server appears connected in the clientβs tool list, then try a read-only example from its documentation.
Claude Desktop setup reference@kisasa/shopify-mcpnpmShopify works with any MCP-compatible client. Copy the config snippet from the Configuration section above and add it to the file shown for your client, then restart the application.
~/Library/Application Support/Claude/claude_desktop_config.jsonRestart Claude Desktop completely for changes to take effect.~/.cursor/mcp.jsonRestart Cursor for changes to take effect..vscode/mcp.jsonReload VS Code window for changes to take effect.~/.codeium/windsurf/mcp_config.jsonRestart Windsurf for changes to take effect..mcp.jsonSave at the project root, then start Claude Code in that project and review the MCP server approval prompt. Keep real credentials out of shared files.