shopify-store-sync-mcp

mcp
Security Audit
Warn
Health Warn
  • License — License: MIT
  • Description — Repository has a description
  • Active repo — Last push 0 days ago
  • Low visibility — Only 6 GitHub stars
Code Warn
  • process.env — Environment variable access in docs/video/render.mjs
  • process.env — Environment variable access in docs/video/tts-elevenlabs.mjs
  • network request — Outbound network request in docs/video/tts-elevenlabs.mjs
  • fs module — File system access in package.json
  • process.env — Environment variable access in scripts/demo-cli.ts
Permissions Pass
  • Permissions — No dangerous permissions requested

No AI report is available for this listing yet.

SUMMARY

Diff and sync Shopify stores from Claude, Cursor or any MCP client: metafield and metaobject definitions, metaobjects, collections, pages, menus, redirects, policies, metafield values and translations. Dry-run plans, cross-store ID remapping, never deletes. Connect with Shopify CLI, no app needed.

README.md

shopify-store-sync-mcp: diff and sync configuration between Shopify stores from your AI assistant

npm version MIT license Node 22+ Listed on the MCP Registry Shopify Admin API 2026-10

Keep your Shopify stores in sync by asking for it.

Compare a dev store with production, see every difference field by field, review a dry-run plan, then apply it.
From Claude, Cursor or any MCP client, or straight from the command line.

Get started · What it syncs · Why this tool · Safety · Tested · Roadmap

Three steps: connect your stores with npx shopify-store-sync-mcp connect, add the server to your AI client, ask in plain language

https://github.com/user-attachments/assets/66a2dfd0-5770-4403-992c-cfe133ea0e9c

Compare, plan, confirm, apply: the full walkthrough, with sound. Every output shown is real, from two sample stores.

diff_stores, plan_sync and apply_plan output: 6 created, 3 updated, 1 conflict skipped, protected store refused until a person confirms

Real output of the server's tools on two sample stores.

What it syncs

The store setup that usually gets rebuilt by hand, plus the images it uses. References between items (a size guide pointing at a product, a menu linking to a page) are carried across by handle, so they land on the right item instead of a broken ID.

Metafield definitions, metaobject definitions and entries, collections, pages, menus, redirects, store policies, files and images, and opt-in products (variants matched by option values), metafield values and translations

Details per resource
Resource Keyed by Notes
metafield_definitions OWNER_TYPE.namespace.key Name, description, pinning, storefront and customer account access, capabilities, validations. A type change is reported as a conflict, since Shopify can't change a definition's type in place.
metaobject_definitions type Schema, access, capabilities. Fields are added or updated, never removed. Self-referencing definitions are handled.
pages handle Title, body HTML, template, published state. With as_drafts (--as-drafts), new pages are created hidden and existing ones keep their state.
metaobjects type/handle All field values, publish status. References to products, collections, pages and other metaobjects are remapped. A field the source leaves empty is never cleared in the target. With as_drafts, new entries are created as drafts.
menus handle Full item tree (3 levels), including customer account menus. Links to resources are remapped; custom URLs are kept; links to things that can't be matched are left out and reported. Links that only the target menu has are kept, in place, and the plan names them.
redirects path Thousands are fine.
collections handle Title, description, sort order, template, SEO and smart-collection rules (rules on a metafield definition are remapped). Manual collections keep their own product list. A new collection is not published to any sales channel. Manual versus smart is reported as a conflict.
shop_policies type Refund, privacy, terms, shipping and contact policy text.
products (opt-in) handle Title, description, vendor, type, category, tags, template, SEO, options, variants (matched by option values; SKU, taxable flag and inventory policy are copied) and images. New products are created as drafts. Prices only with prices: true (--with-prices). On update, the tags, variants, option values and media (images, videos, 3D models) that only the target has are kept, and don't count as differences. Gift cards are skipped. See "What product sync leaves alone" below.
metafields (opt-in) OWNER/handle/namespace.key Metafield values on products and collections, landed on the item with the same handle. Reference values are remapped. Filter with namespaces.
translations (opt-in) locale:Type/handle Translations of products, pages, collections, metaobjects, menus and policies. Registered against the target's own text, after the content itself is synced. A translated field only the target has is kept. The plan warns when a language isn't added to the target.

products, metafields and translations can be large, so they're read only when you ask for them (resources: ["products"]). Name what you want and only that is read: exact product handles or prefixes (include: ["linen-shirt", "linen-*"]) for products, and patterns that name their owner (include: ["PRODUCT/linen-shirt/*"]) for metafield values. App-owned (app--…) and Shopify-reserved (shopify--…) data is skipped and listed in the report.

Files and images referenced by any of the above are matched by file name in the target. When one is missing, the plan says so and apply_plan uploads it from the source store.

What product sync leaves alone. Never copied: inventory levels, sales-channel publishing, the status of existing products, barcodes, weight and shipping settings, the image assigned to a variant, videos and 3D models (the target keeps its own), and translations of option names and values. Nothing is ever removed from a target product: a tag, variant or image deleted in the source stays in the target until you remove it there.

Nothing goes live by surprise. New products are drafts and new collections aren't on any sales channel. Pages and metaobject entries copy the published state of the source unless you pass as_drafts; the plan tells you when new ones would be visible right away.

Use cases

Dev to production, multi-region brands, new market launch, drift audits

Why this tool

Plenty of Shopify MCP servers read and write one store. This one is built for the job between stores, where every ID is different:

Generic Admin MCP servers shopify-store-sync-mcp
Compare two stores ❌ ✅ field-level diff
Cross-store ID remapping ❌ you copy raw IDs ✅ products, collections, pages, blogs, metaobjects, definitions, customer account pages and shop policies
Dry run before writing ❌ ✅ every write goes through a reviewed plan
Detects changes made after planning ❌ ✅ re-reads the target and skips anything that drifted
Deletes sometimes never
Production guardrails ❌ ✅ writes are off by default; protected stores are confirmed by a person, not by the assistant
Audit trail ❌ ✅ JSONL log of every write

Why not just Shopify CLI? The CLI runs one query you wrote, on one store. This tool uses the CLI only to sign in, and does the comparing, ID remapping, ordering, uploads and safety checks for you.

Left: raw shopify store execute commands per store with manual comparison and ID rewriting. Right: diff_stores, plan_sync and apply_plan doing it with remapping, ordering, uploads and drift checks

Safe on production

Read-only by default, plan then apply, drift protection, never deletes, protected stores, conflicts not forced, audit log, no broken IDs

How each safeguard works
  1. Read-only by default. allowWrites is false until you set it, per store.
  2. Plan, then apply. apply_plan only runs a plan produced by plan_sync. Plans expire after an hour and their files are removed after a week.
  3. Drift protection. Before writing, the target is re-read. Any item that changed since planning is skipped, not overwritten.
  4. No deletes. Items that exist only in the target are listed, never removed. The same holds inside an item wherever Shopify's API replaces a whole list: menu links, product tags, variants and media that only the target has are read first and sent back with the update. One thing is not kept: when the source has a value for a field and the target has another, the source's value wins. That is what a sync is, and the plan shows each one before you apply.
  5. Protected stores are confirmed by a person. For a store marked protected, the assistant can't supply the confirmation itself. Your client asks you to type the store's domain (MCP elicitation); in a client that can't ask, the plan is applied from the command line with --confirm <domain>. The assistant sees store content (page bodies, product descriptions), so this is what stands between a prompt hidden in that content and your production store.
  6. Conflicts aren't forced. A metafield definition whose type differs is reported and skipped.
  7. Audit log. Every write is appended to ~/.shopify-store-sync/audit.log (override with SYNC_AUDIT_LOG).
  8. Snapshot folders are treated as input. A folder used as a source (dir:) is validated file by file, can only hold the known resource types, and files are only ever uploaded from Shopify's own file hosts. Over MCP, folders are read and written inside one base folder (SYNC_SNAPSHOT_DIR, or the folder the server runs in).
  9. No silent shortcuts. A list too long to read fully stops with an error instead of coming back short, and a request that gets no answer is abandoned after a time limit; a write is never repeated blindly.

To report a vulnerability, see SECURITY.md.

Get started

1. Connect your stores

Most people only need the first option: no app to create. The other two are for scheduled or unattended runs.

connect output: browser approval, Connected, and the stores.json line to add; then the three ways to connect compared

npm install -g @shopify/cli
npx shopify-store-sync-mcp connect my-dev-store            # read-only
npx shopify-store-sync-mcp connect my-staging-store --writes
More about the Shopify CLI sign-in

Each connect opens Shopify in your browser; click Install to approve. It asks for read-only scopes unless you pass --writes (or the store is configured with "allowWrites": true), then prints the line to add to stores.json:

{ "stores": { "my-dev-store": { "domain": "my-dev-store.myshopify.com", "auth": "shopify-cli" } } }

Requests then run through shopify store execute, Shopify CLI's supported command, using the sign-in it saved. Each request starts Shopify CLI (about a second), so a full diff takes tens of seconds instead of one or two with an app. That sign-in belongs to you and expires after a while; when it does, the tool says so and you run connect again. Because of that, use option 2 or 3 for scheduled or unattended runs.

Option 2: a Dev Dashboard app (automation on your own stores)

One app covers every store in the same Shopify organization, and tokens are fetched and refreshed for you.

  1. Open the Dev Dashboard, go to Apps, click Create app, then Create app manually.
  2. Give it a name, for example Store Sync. Any URL works as the App URL (it's never opened); untick Embed app in Shopify admin.
  3. In Scopes, paste the scopes from "Scopes needed per resource" below, then click Create app and Release.
  4. Install it on each store: open https://admin.shopify.com/store/<store-handle>/oauth/install?client_id=<your client ID> and click Install.
  5. Under the app's Settings, copy the Client ID and Client secret into environment variables (step 2 reads them from there).

Adding a scope later means releasing a new version and approving the update on each store (same install link).

Option 3: a store in someone else's organization (a client's store)

An app's client credentials only work on stores owned by the same organization as the app; elsewhere Shopify answers shop_not_permitted. For a store you don't own there are two ways:

  • Sign in with Shopify CLI (option 1) with your staff or collaborator account. Nothing to create; best for work you run yourself.
  • For unattended runs, ask the store's owner to follow option 2 in their organization (create the app in their Dev Dashboard, install it on their store) and share the client ID and client secret with you.

An existing Admin API access token (shpat_...) from a custom app that was created in the store admin still works: use it as accessToken in step 2. New apps of that kind can no longer be created there (Shopify moved app creation to the Dev Dashboard on January 1, 2026), so don't plan new setups around it.

Scopes needed per resource
Resource Read scope Write scope
metafield definitions read_products (and the owner type's read scope) write_products (and the owner type's write scope)
metaobjects and definitions read_metaobjects, read_metaobject_definitions write_metaobjects, write_metaobject_definitions
pages read_online_store_pages write_online_store_pages
menus read_online_store_navigation write_online_store_navigation
redirects read_online_store_navigation write_online_store_navigation
collections read_products write_products
products read_products write_products (plus read_files/write_files for their images)
shop policies read_legal_policies write_legal_policies
metafield values read_products write_products
translations read_translations, read_locales write_translations
files read_files write_files (to upload missing files)
theme diff read_themes none, never writes
ID remapping read_products, read_content none

Everything at once (the set used for the live tests), ready to paste:

read_products,write_products,read_metaobjects,write_metaobjects,read_metaobject_definitions,write_metaobject_definitions,read_online_store_pages,write_online_store_pages,read_online_store_navigation,write_online_store_navigation,read_legal_policies,write_legal_policies,read_translations,write_translations,read_locales,read_files,write_files,read_themes,read_content

Give source stores read-only scopes when you can.

2. Describe your stores

connect prints the line to add. A stores.json mixing all three ways to connect:

{
  "stores": {
    "dev":  { "domain": "my-brand-dev.myshopify.com", "auth": "shopify-cli" },
    "eu":   { "domain": "my-brand-eu.myshopify.com", "clientId": "env:EU_CLIENT_ID", "clientSecret": "env:EU_CLIENT_SECRET", "allowWrites": true, "protected": true },
    "uk":   { "domain": "my-brand-uk.myshopify.com", "accessToken": "env:UK_TOKEN" }
  }
}
All store options

Create stores.json. Secrets can come from environment variables with env:NAME:

{
  "stores": {
    "dev": {
      "domain": "my-brand-dev.myshopify.com",
      "accessToken": "env:DEV_TOKEN",
      "label": "Development"
    },
    "eu": {
      "domain": "my-brand-eu.myshopify.com",
      "clientId": "env:EU_CLIENT_ID",
      "clientSecret": "env:EU_CLIENT_SECRET",
      "allowWrites": true,
      "protected": true
    }
  }
}
Option Default Meaning
domain required *.myshopify.com domain
auth "shopify-cli": use the sign-in saved by connect (no token needed), or
accessToken Static Admin API token from an existing custom app, or
clientId + clientSecret Dev Dashboard app; tokens are fetched and refreshed automatically
apiVersion 2026-10 Admin API version
allowWrites false Must be true for apply_plan to write to this store
protected false Applying also needs a person to confirm with the store's domain: asked by your AI client, or --confirm <domain> on the command line

You can also pass the whole config as JSON in SHOPIFY_STORES, or point SHOPIFY_STORES_FILE at a file elsewhere. For a single store, SHOPIFY_STORE_DOMAIN plus SHOPIFY_ACCESS_TOKEN works.

Environment variables
Variable Default Meaning
SHOPIFY_STORES_FILE ./stores.json Where the store config lives
SHOPIFY_STORES The whole store config as inline JSON
SHOPIFY_API_VERSION 2026-10 Admin API version for stores that don't set apiVersion
SHOPIFY_CLI_BIN shopify Path to Shopify CLI, when it isn't on the PATH
SYNC_SNAPSHOT_DIR folder the server runs in Over MCP, snapshot folders (export_snapshot, dir:) must be inside this folder
SYNC_ALLOW_AGENT_CONFIRM off true lets apply_plan take the protected-store confirmation as an argument. Only for unattended automation where no person can be asked
SYNC_REQUEST_TIMEOUT_MS 60000 How long one request to Shopify may take
SYNC_CLI_TIMEOUT_MS 120000 How long one Shopify CLI call may take before it is stopped
SYNC_MAX_ITEMS 50000 Longest list read from a store; a longer one is an error, never a silent cut
SYNC_WRITE_CONCURRENCY 4 How many independent writes run at once
SYNC_AUDIT_LOG ~/.shopify-store-sync/audit.log Where every write is logged
SYNC_STATE_DIR ~/.shopify-store-sync Where plans and the audit log are kept

3. Add it to your AI client

{
  "mcpServers": {
    "shopify-store-sync": {
      "command": "npx",
      "args": ["-y", "shopify-store-sync-mcp"],
      "env": { "SHOPIFY_STORES_FILE": "/absolute/path/to/stores.json" }
    }
  }
}
Claude Desktop, Claude Code and Cursor

Claude Desktop (claude_desktop_config.json):

{
  "mcpServers": {
    "shopify-store-sync": {
      "command": "npx",
      "args": ["-y", "shopify-store-sync-mcp"],
      "env": {
        "SHOPIFY_STORES_FILE": "/absolute/path/to/stores.json",
        "DEV_TOKEN": "shpat_...",
        "EU_CLIENT_ID": "...",
        "EU_CLIENT_SECRET": "..."
      }
    }
  }
}

Claude Code

claude mcp add shopify-store-sync \
  -e SHOPIFY_STORES_FILE=/absolute/path/to/stores.json \
  -e DEV_TOKEN=shpat_... \
  -- npx -y shopify-store-sync-mcp

Cursor (.cursor/mcp.json) takes the same command, args and env as Claude Desktop.

4. Ask

  • "Compare dev and eu. Only metaobjects and menus."
  • "Which product metafield definitions in namespace custom are missing from eu?" (uses include: ["PRODUCT.custom.*"])
  • "Plan a sync of the faq and size_guide metaobjects from dev to eu."
  • "Apply only ops 1 to 4 of that plan."
  • "Sync the pages from dev to eu, but keep new ones hidden." (uses as_drafts: true)
  • "Copy the linen-shirt product to eu, with its prices." (uses resources: ["products"], include: ["linen-shirt"], prices: true)
  • "Copy the French translations from dev to eu." (uses resources: ["translations"], locales: ["fr"])
  • "Export eu's configuration to ./store-config so I can commit it."
  • "Has eu drifted from ./store-config?"
  • "What's different in the theme settings between dev and eu?"

Tools

What diff_stores reports, field by field (real output):

Field-level diff: page body, menu items, definition name, and a type conflict reported but not forced

All 9 tools
Tool Writes? What it does
list_stores no Configured stores and their write permissions. Secrets are never returned.
list_resource_types no What can be synced and how it's keyed.
snapshot_store no Read one store's configuration in portable form.
diff_stores no Only-in-source, changed (field by field), only-in-target.
plan_sync no Dry-run plan: creates, updates, conflicts, missing references. Returns a plan_id.
get_plan no Show a plan, or list recent ones.
apply_plan yes Execute a plan, with every safety check above. For a protected store your client asks you to confirm.
export_snapshot local files only Write a store's configuration to a folder for git (config as code).
diff_themes no Compare theme templates and settings between two stores (theme settings).

Every tool except apply_plan is read-only toward Shopify and annotated as such, so clients can auto-approve them. Long calls report progress (what was read, which step was written) to clients that ask for it.

Command line

Everything also works without an AI client, and --exit-code turns diff into a scheduled drift check.

CLI output of diff, plan and themes commands

Commands

Everything also works without an AI client. Run a command instead of starting the server:

npx shopify-store-sync-mcp stores
npx shopify-store-sync-mcp diff dev eu --resources pages,menus
npx shopify-store-sync-mcp plan dev eu --as-drafts
npx shopify-store-sync-mcp apply <plan-id> --confirm my-brand-eu.myshopify.com
npx shopify-store-sync-mcp export eu ./store-config
npx shopify-store-sync-mcp themes dev eu

--json gives machine-readable output and --exit-code makes diff and themes exit with 2 when they find differences, handy for a scheduled drift check. Run npx shopify-store-sync-mcp help for every option.

Config as code

Export a store's setup as small, sorted JSON files, commit it, and use the folder as a source anywhere (dir:./store-config) to spot drift, review changes as a pull request, or restore.

Exported store-config folder and one translation file

How to use it

Export a store's configuration as one small JSON file per item, with sorted keys and no store-specific IDs, then commit it:

npx shopify-store-sync-mcp export prod ./store-config
git add store-config && git commit -m "Snapshot prod configuration"

Any command or tool that takes a source also takes dir:<path>:

  • Spot drift: diff dir:./store-config prod shows what changed in production since the commit.
  • Review changes: edit the files or re-export from staging, and review the change as a pull request.
  • Restore or promote: plan dir:./store-config prod, then apply, with all the usual safety checks.
Theme settings diff (read-only)

diff_themes (or themes on the command line) compares the published themes of two stores: JSON templates, section groups and config/settings_data.json, setting by setting. Pass theme ids to compare other themes, and files to narrow it down (for example templates/product*.json). It is read-only: themes are never written to.

How it works

Snapshot, diff, plan, apply, with safety checks before every write

gid://shopify/Product/101 in dev becomes shopify://Product/linen-shirt, then gid://shopify/Product/9101 in eu

How ID remapping works

Every Shopify ID is store-specific, so the server never compares or copies raw IDs. When it reads a store, each gid://shopify/... value is rewritten into a portable reference:

gid://shopify/Product/8123456          →  shopify://Product/linen-shirt
gid://shopify/Metaobject/2918          →  shopify://Metaobject/faq/returns
gid://shopify/MetaobjectDefinition/77  →  shopify://MetaobjectDefinition/faq
gid://shopify/CustomerAccountPage/12   →  shopify://CustomerAccountPage/orders
gid://shopify/ShopPolicy/5             →  shopify://ShopPolicy/PRIVACY_POLICY
gid://shopify/Menu/41                  →  shopify://Menu/main-menu
gid://shopify/MetafieldDefinition/9    →  shopify://MetafieldDefinition/PRODUCT.custom.fabric
gid://shopify/MediaImage/730           →  shopify://File/size-chart.png

This covers JSON list values (list.product_reference) and definition validations (metaobject_definition_id). When it writes, references are resolved by handle in the target. A reference that doesn't exist there is flagged in the plan before anything is written.

Things in the same plan can depend on each other, for example a metaobject pointing at another one being created, or a definition that references itself. These are written in two passes: first without the pending references, then complete.

Files (images, generic files and videos) are matched by file name and uploaded to the target when missing. Collection rules that filter on a metafield are remapped to the target's definition.

Not remapped yet: product variants and articles. Their values are left out of the sync and listed as warnings, never copied as broken IDs.

Two references followed from the source store to the target, as the sample sync above wrote them:

Tested on real stores

104 automated tests, 56 GraphQL operations validated, 11 item types written live, 2 sign-in methods used live

Every live test and its result
Feature Live result
Diff of all default resource types both stores read in about 2 seconds with an app, about 30 through the Shopify CLI sign-in
Create: metafield and metaobject definitions, metaobjects, smart collection, page, redirect, French translation applied, second diff empty
File reference on a metaobject image uploaded to the target by file name and linked
Update: page title and body, collection description applied, second diff empty
Shop policies synced; a policy Shopify manages automatically is reported with how to unblock it
Metafield values, including a product_reference applied, reference remapped to the target's own product
Values on products missing from the target skipped up front with the reason
Export to a folder, then diff that folder against the store no differences
Theme settings diff read-only, 15 files compared in about 1 second
Products (Shopify CLI sign-in, Windows) a real product copied as a draft with its 4 images and alt text; after editing the copy and adding a variant and an image there, the update restored the title and kept both; the next diff was identical
Products: reading only named handles planning one product went from 97 to 6 seconds
Menus (0.5.0) a link added only in the target stayed in place through an update that added a link from the source; the plan named it; the next plan was empty
Products: what only the target has (0.5.0) a tag and a YouTube video added only in the target were both still there after an update that renamed the product and added a tag
Protected store over MCP (0.5.0, stdio, Windows) refused when the assistant passed the domain itself; applied after the person typed it when the client asked
Snapshot folders over MCP (0.5.0) a folder outside the base folder was refused
Safety: write to a read-only store refused
Safety: target edited after the plan was made step skipped, nothing overwritten
Shopify CLI sign-in on Windows (CLI 4.9.4), both stores connect read-only and --writes; plan, apply (7 creates incl. file upload and translation), update and re-diff all exact; also through the MCP server over stdio

Behaviours of the live API found this way are covered by tests: nodes returns null for customer account pages (they're looked up by listing instead), ShopPolicy.type has to be aliased next to Metaobject.type, Shopify refuses edits to auto-managed policies, and a failed token request carries its reason (such as app_not_installed) in an HTML page.

Roadmap

Roadmap: shipped (syncing, safety, ways to use it, quality) and next (collection sources, batch writes, more product fields, variants and articles, more metafield owners, menu translations, markets, undo, optional deletes)

The full list

Shipped

Syncing

  • Metafield definitions, metaobject definitions and metaobject entries
  • Products: content, category, options, variants matched by option values (SKU, taxable flag, inventory policy), images; new ones as drafts, prices only when asked
  • Collections with smart-collection rules, pages, menus (3 levels, customer account menus too), redirects, store policies
  • Metafield values on products and collections, translations per language, products included (opt-in)
  • Images and files matched by file name, uploaded to the target when missing
  • Cross-store ID remapping by handle, including list references and rules on metafield definitions
  • Two-pass writes for items that point at each other or at themselves
  • Filters: resource types, include and exclude patterns, namespaces, metaobject types, owner types, languages
  • as_drafts: new pages hidden, new metaobject entries as drafts, published state of existing ones left alone

Safety

  • Dry-run plans that expire after an hour, applied in full or step by step (--only)
  • Drift check before every write, read-only by default
  • Never deletes, also inside an item: menu links, product tags, variants and media that only the target has are kept
  • Protected stores are confirmed by a person (asked by the client, or --confirm on the command line), never by the assistant
  • Conflicts reported instead of forced (type changes, manual versus smart collections, values on items missing from the target)
  • Snapshot folders validated, files uploaded only from Shopify's hosts, folders kept inside a base folder over MCP
  • Time limits on every request, and an error instead of a shortened list
  • Audit log of every write

Ways to use it

  • MCP server with 9 tools, read-only tools annotated for auto-approval, progress reported on long calls
  • Command line with --json output and --exit-code for scheduled drift checks
  • Config as code: export a store to files and use the folder as a source (dir:)
  • Read-only theme settings diff
  • Connect without creating an app (Shopify CLI sign-in), or with a Dev Dashboard app or an access token
  • Published on npm, runs with npx

Quality

  • 104 automated tests against an in-memory Shopify, plus MCP protocol tests
  • Every GraphQL operation validated against Shopify's schema for 2026-04, 2026-10 and 2027-01
  • Tested live between two stores on Linux and Windows, Shopify CLI 4.9 and 5
  • Parallel reads and writes with throttle-aware retries; clear errors for missing installs, expired sign-ins and auto-managed policies
  • Linted, no known vulnerabilities in any dependency, release checklist for Shopify's quarterly API versions

Next

  • Move collection rules to Shopify's new sources model. ruleSet is deprecated but supported through 2027-01; the new model only exists from 2026-07, so this waits until 2026-04 can be dropped
  • Batch writes for very large syncs (urlRedirectImport, bulk operations): writes run 4 at a time today
  • Product prices per market and inventory levels, as explicit opt-ins
  • Barcodes, weight and variant images on products; translations of option names and values
  • Remap product variants and blog articles, so references to them sync too
  • Metafield values on variants, pages, blogs and the shop; metafield definition constraints
  • Menu item (link) translations
  • Markets and market web presences
  • Undo: restore the previous values of an applied plan from the audit log
  • Optional deletes, behind an explicit flag

Ideas and PRs are welcome. Issues tagged good first issue are a good place to start.

Development
npm install           # Node 22 or newer
npm test              # vitest: unit, sync engine against an in-memory Shopify, and MCP protocol tests
npm run typecheck
npm run lint
npm run build

# Validate every GraphQL operation against Shopify's real Admin schema
npm run validate:graphql            # defaults to 2026-10
npm run validate:graphql -- 2027-01

npm run check         # typecheck, lint, tests and schema validation in one go

Layout:

src/
  index.ts            entrypoint: MCP server on stdio, or the CLI when given a command
  engine.ts           everything the tools and the CLI do, in one place
  server.ts           MCP tools
  cli.ts              command line
  snapshotFiles.ts    export and read snapshot folders (config as code)
  themes.ts           read-only theme settings diff
  config.ts           store config loading and validation
  shopify/client.ts   Admin GraphQL client (token refresh, throttling, retries)
  shopify/cliClient.ts  the same requests through Shopify CLI's sign-in (`shopify store execute`)
  refs.ts             GID <-> portable reference mapping
  snapshot.ts         read a store into portable form
  drafts.ts           the "as drafts" view: published state left out
  diff.ts             field-level diff
  plan.ts             dry-run plans and plan storage
  apply.ts            guarded, drift-checked, two-pass writes
  resources/          one adapter per resource type
test/
  fakeShop.ts         in-memory Admin API used by the tests

Adding a resource type means writing one adapter (fetch, create, update) in src/resources/. See CONTRIBUTING.md, which also has the release checklist.

Related projects

Syncing definitions between stores isn't a new idea, and these tools are worth knowing:

Project Type What it covers
groupthought/metasync CLI Metafield and metaobject definitions plus resource data; dry run by default; has a delete mode
TeamDijon/meta-sync CLI Metafield and metaobject definitions and entries; manifest-based copy; bulk mode deletes the target first
shopify-metadata-migrator CLI Copies metafield and metaobject definitions
MetaMaintain, Metora, Rewind Staging Shopify apps (paid or freemium) Definition and entry sync through an embedded app

What this project adds: it runs inside your AI client as an MCP server (or as a CLI) on your own machine, signed in as you through Shopify CLI or with your own private app, so there's no third-party app from the App Store and no data goes anywhere except Shopify. You can ask for a diff in plain language; it also covers products, pages, menus, redirects, collections, policies, metafield values and translations, remaps references and files across stores, keeps store configuration in git, checks for drift before writing and never deletes. If you only need a scripted one-shot copy of definitions, one of the CLIs above may be all you need.

Disclaimer

Not affiliated with, endorsed by or sponsored by Shopify Inc. "Shopify" is a trademark of Shopify Inc. Test on development stores first; you are responsible for what you apply to production.

License

MIT © Youssef Idder

Reviews (0)

No results found