Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Shopify Metafield MCP

A read-only MCP server that lets Claude (or any MCP client) look up a Shopify store's metafield definitions instead of guessing them.

When you build or migrate a Shopify theme, the metafield structure — every namespace, key, and data type — lives in the Admin. The theme code has to match it exactly. Reference custom.spec_sheet when the store actually defines custom.specs and the Liquid renders blank. There was no way for an AI assistant to see that structure, so it guessed. This server hands it the real list.

What it does

Three tools over stdio:

Tool What it answers
list_metafield_definitions "What metafields does a PRODUCT (or COLLECTION, CUSTOMER, …) have?" Returns namespace, key, type, name, and description for each.
get_definition "Is it custom.specs or custom.spec_sheet, and what type does it hold?" Resolves one namespace.key to its full shape, including validations.
diff_definitions "What changed between these two stores?" Compares two snapshots and reports what was added, removed, or changed. Built for migrations.

It reads from the Shopify Admin GraphQL API using a store domain and an Admin API access token. It also ships with a mock mode that runs against a recorded fixture, so you can try it without a store.

Read-only by design

This server only sends GraphQL queries. It never sends a mutation. The app it powers requests no write scope, so it cannot create, edit, or delete anything in your store — it can only read metafield definitions. It is safe to point at a live production store.

The token you give it should be a read-scoped Admin API access token (read_products, and the matching read scope for any other owner type you want to inspect). If you give it a token with write scopes, this server still never writes — but a least-privilege token is the right habit.

Install

Requires Node 20 or newer. The server has no runtime dependencies — it speaks JSON-RPC 2.0 on stdin/stdout directly.

npx shopify-metafield-mcp        # runs the server (expects env vars, see below)

Or clone it:

git clone https://github.com/VelkinaStudio/shopify-metafield-mcp
cd shopify-metafield-mcp
node --test                       # run the tests
node src/server.js --mock src/mockData.json   # run in mock mode

Add it to Claude Code

Put this under mcpServers in your Claude Code config (~/.claude.json, or a .mcp.json in a project):

{
  "mcpServers": {
    "shopify-metafields": {
      "command": "npx",
      "args": ["-y", "shopify-metafield-mcp"],
      "env": {
        "SHOPIFY_STORE": "your-store.myshopify.com",
        "SHOPIFY_ADMIN_TOKEN": "shpat_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      }
    }
  }
}

SHOPIFY_STORE accepts either your-store or your-store.myshopify.com. Restart Claude Code and the three tools appear.

Try it without a store

Mock mode reads a recorded fixture instead of calling Shopify — no token needed:

{
  "mcpServers": {
    "shopify-metafields": {
      "command": "node",
      "args": ["src/server.js", "--mock", "src/mockData.json"]
    }
  }
}

You can also pass a fixture by env var: MCP_MOCK_FILE=path/to/file.json.

Where to get an Admin API token

In the Shopify admin: Settings → Apps and sales channels → Develop apps → Create an app → Configure Admin API scopes. Grant the read scopes you need (read_products, read_customers, …), install the app, and copy the Admin API access token (shpat_…). Keep it out of source control — pass it through the env block above or a secret manager, never commit it.

Tool details

list_metafield_definitions

// input
{ "ownerType": "PRODUCT" }   // case-insensitive

Returns a sorted list of { namespace, key, type, name, description } plus a count. Owner types: PRODUCT, PRODUCTVARIANT, COLLECTION, CUSTOMER, ORDER, COMPANY, COMPANY_LOCATION, LOCATION, BLOG, ARTICLE, PAGE, SHOP, MARKET, DRAFTORDER, DISCOUNT.

get_definition

// input — either form works
{ "handle": "custom.specs" }
{ "namespace": "custom", "key": "specs", "ownerType": "PRODUCT" }

Returns { found, definition }. definition includes type, typeCategory, description, ownerType, and validations. If the namespace.key does not exist, found is false — that is the answer that tells you why the Liquid was rendering blank.

diff_definitions

// input — each snapshot is a definitions array OR a raw Admin API response
{ "before": [ /**/ ], "after": [ /**/ ] }

Returns { summary, added, removed, changed }. A definition's identity is (ownerType, namespace, key); a "change" means that identity exists in both snapshots but its type, description, name, or validations differ. changed entries carry field-level before/after so you can see exactly what moved.

Protocol notes

  • Transport is stdio. The server implements initialize, tools/list, tools/call, and ping, and acknowledges notifications/initialized.
  • It negotiates the protocol version the client requests (it supports 2025-11-25 and earlier).
  • All logging goes to stderr. stdout carries only JSON-RPC messages, so the stream stays clean. This is the most common way to break an stdio MCP server, and this one is careful about it.

Tests

node --test

The test suite runs entirely against a recorded fixture (test/fixtures/definitions.json). It never makes a live API call. It includes test/stdio.test.js, which spawns the server and pipes a real JSON-RPC sequence through it to confirm the wire protocol.

To watch the bytes go in and out, run the standalone handshake demo:

node scripts/handshake.mjs

License

MIT. See LICENSE.


Built by Velkina — https://velkina.com

About

An MCP server that lets Claude read and diff a Shopify store's metafield definitions so theme work stops guessing namespaces and keys.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages