Build a Shopify MCP Server: Let an AI Agent Run Your Store Operations
A practical guide to building a Model Context Protocol server that exposes Shopify operations as governed tools an AI agent can call safely — with scopes, guardrails, rate-limit awareness and an audit log on every action.
An AI agent that can answer “which SKUs are about to stock out and what should I reorder?” is genuinely useful.
An AI agent with your Shopify admin API key and no guardrails is a liability waiting to happen.
The gap between the two is an MCP server. Build it right and the agent gets a curated menu of governed actions, not the keys to the shop.
This is how you build that layer.
Who This Is For
- Store owners who want an agent to handle repetitive ops questions and low-risk actions
- Developers wiring Claude (or any MCP-capable agent) into a real Shopify backend
- Teams nervous about letting an LLM anywhere near production, correctly nervous
- Anyone who has read “just give the agent the API” and felt their stomach drop
What You Will Need
- A Shopify store with a custom app and an Admin API access token
- Familiarity with the Shopify Admin GraphQL API and its cost-based rate limiting
- An MCP-capable host: Claude Desktop, the Claude API with MCP, or your own agent runtime
- A runtime for the server (Node/TypeScript or Python both have solid MCP SDKs)
- A place to write an audit log that is not the agent’s context window
The Architecture
MCP is a client–server protocol. Anthropic published it as an open standard so any agent can discover and call tools over a uniform interface. Your job is to sit between the agent and Shopify and decide exactly what the agent is allowed to do.
The mental model: the agent never touches Shopify. It touches your server. Your server touches Shopify, and only in ways you have explicitly permitted.
flowchart LR A[Store owner] --> B[AI agent] B --> C[MCP server] C --> D[Guardrail layer] D --> E[Shopify GraphQL] D --> F[Audit log] E --> C C --> B
Every tool call flows through the same choke point. That choke point is where scopes, validation, rate-limit budgeting and logging live. The model proposes; your server disposes.
Tools Are Your Real API Surface
An MCP tool is a named, typed, described function. The agent sees the name, the description and the JSON schema for its arguments. That is all. Design these deliberately. A tool called update_product with a free-form payload is an invitation to trouble. A tool called adjust_price_within_band with a hard-capped percentage is a contract.
Split your surface into three tiers and never blur them:
- Read tools: orders, products, inventory levels, customer counts. Safe, and where you start.
- Draft tools: generate a product description, propose a discount, suggest a reorder. Produce output; change nothing.
- Write tools: actually mutate the store. Scoped tightly, validated hard, and where required, gated behind human approval.
Here is a read tool and a guarded write tool sketched in TypeScript:
server.tool(
"get_low_stock_variants",
"List product variants at or below a stock threshold.",
{
threshold: z.number().int().min(0).max(1000).default(5),
limit: z.number().int().min(1).max(50).default(20),
},
async ({ threshold, limit }) => {
const data = await shopify.graphql(LOW_STOCK_QUERY, { limit });
const rows = data.productVariants.nodes
.filter((v) => v.inventoryQuantity <= threshold)
.map((v) => ({ sku: v.sku, qty: v.inventoryQuantity }));
await audit.log("get_low_stock_variants", { threshold, count: rows.length });
return { content: [{ type: "text", text: JSON.stringify(rows) }] };
}
);
server.tool(
"adjust_price_within_band",
"Change a variant price by a capped percentage. Requires approval above 5%.",
{
variantId: z.string().regex(/^gid:\/\/shopify\/ProductVariant\/\d+$/),
pctChange: z.number().min(-10).max(10),
},
async ({ variantId, pctChange }, ctx) => {
if (Math.abs(pctChange) > 5 && !ctx.approved) {
return { needsApproval: true, summary: `Change ${variantId} by ${pctChange}%` };
}
const result = await shopify.graphql(PRICE_MUTATION, { variantId, pctChange });
await audit.log("adjust_price_within_band", { variantId, pctChange, actor: ctx.actor });
return { content: [{ type: "text", text: `Updated ${variantId}` }] };
}
);Notice what the schema does before any business logic runs: it rejects malformed IDs, clamps the percentage to ±10, and forces a percentage above 5 through an approval branch. The model cannot argue its way past a Zod schema.
Scopes, Not Trust
Do not authenticate the agent as “admin”. Give each deployment a scope set that mirrors the tools it is allowed to see. A read-only analytics agent should be physically incapable of calling a mutation. Not because it was told not to, but because those tools were never registered for its session.
flowchart TD
A[Incoming tool call] --> B{In session scope?}
B -->|No| C[Reject and log]
B -->|Yes| D{Write action?}
D -->|No| E[Execute read]
D -->|Yes| F{Risky or above band?}
F -->|Yes| G[Queue for human approval]
F -->|No| H[Validate and execute]
H --> I[Write audit entry]
G --> I
E --> I
The approval queue is the piece most people skip. For price changes, bulk edits, publishing products or issuing discounts, the safe default is: the agent prepares the action, a human confirms it, then it executes. This is the same instinct behind filtering and verifying Shopify webhooks before acting on them: never let an automated signal trigger an irreversible action unchecked.
Respect the Cost Budget
Shopify’s Admin GraphQL API meters by query cost, not request count. An agent left to its own devices will happily fan out ten expensive queries to answer one question and trip the throttle mid-conversation.
Build cost awareness into the server, not the prompt:
- Give each tool a known, bounded query cost. No unbounded
first: 250connections buried in a nested field - Read the
throttleStatusin every response and back off before you hit the ceiling - Cache slow-changing reads (product catalogue, collections) so the agent is not re-querying them every turn
- Prefer one well-shaped query per tool over letting the agent compose many
The cost-aware querying patterns I use for scheduled Shopify automation apply directly here. An agent is just a very chatty client, and chatty clients get throttled first.
Log Everything, Store Nothing Sensitive
Two rules that sound similar and are opposites.
Log everything the agent does. Every tool call, its arguments, its outcome, who approved it and when. When someone asks “why did the price of SKU-1183 change on Tuesday”, you need an answer that is not “the AI did it”.
Store nothing sensitive in the agent’s context. Customer emails, addresses, payment details: these should never flow into the model’s context window. If a tool needs to act on a customer, pass an opaque ID and resolve PII server-side, out of the model’s sight. The agent can know “order 4021 needs a refund” without ever seeing the customer’s name.
| Before (raw API access) | After (governed MCP server) |
|---|---|
| Agent holds the admin token | Agent holds only a scoped session |
| Any mutation is one prompt away | Writes gated by schema, scope and approval |
| Customer PII flows into context | PII resolved server-side, never in context |
| No record of what the agent did | Every call written to an audit log |
| One bad completion mutates the store | One bad completion hits a validation wall |
Failure Modes to Avoid
- The god tool: a single
run_graphqlthat accepts arbitrary queries. You have just handed over the store. - Trusting the description: the model can misread a tool. Enforce limits in code, not in the prose description.
- Silent writes: a mutation with no audit entry. If it is not logged, it did not happen safely.
- PII in context: pulling full customer records into a prompt “so the agent has context”. Resolve it server-side.
- Ignoring throttle status: an agent that retries into a rate limit and stalls the whole conversation.
- No approval path: letting the agent publish products or change prices with no human in the loop.
What to Build First
- A read-only server with three tools:
get_orders_summary,get_low_stock_variants,search_products. Ship nothing that writes. - An audit log, even a single append-only table, wired into every tool from day one.
- Cost budgeting: bound each query, read
throttleStatus, add caching for the catalogue. - One draft tool:
draft_product_description. It returns text; it changes nothing. - Your first guarded write,
adjust_price_within_bandwith a hard cap and an approval branch. Ship it only once the read layer has earned trust.
Final Take
The interesting engineering in agentic ecommerce is not the model. It is the governance layer between the model and your business.
MCP gives you a clean place to put that layer. Treat every tool as a contract, every write as a privilege, and every action as something you will one day have to explain. Do that and an agent running your store operations stops being a gamble and starts being infrastructure.
If you want to extend this thinking to reporting and multi-agent workflows, I covered the analytics side in agentic dashboards with MCP and A2A. Same principle throughout: the agent proposes, your governed layer disposes.
Frequently Asked Questions
- What is a Shopify MCP server?
- It is a small server that speaks the Model Context Protocol — Anthropic's open standard — and exposes Shopify Admin operations as discrete, typed tools an AI agent can call. Instead of giving an agent raw API keys, you give it a curated menu of governed actions with scopes and guardrails.
- Is it safe to let an AI agent write to my Shopify store?
- Only if you design for it. Start read-only, gate every write behind explicit scopes and validation, require human approval for risky actions like price changes, and log every tool call. The MCP layer is where you enforce that policy, not the model.
- Do I need the Shopify Admin GraphQL API for this?
- Yes, in practice. The Admin GraphQL API gives you precise field selection and a cost-based rate limit that suits agent workloads far better than REST. Your MCP tools become thin, cost-aware wrappers around specific GraphQL queries and mutations.
Enjoyed this article?
Get notified when I publish new articles on automation, ecommerce, and data engineering.
Get in touch