Shopify Enterprise MCP Server

Shopify Enterprise MCP Server

Production-grade MCP server that exposes live Shopify Admin GraphQL data to Microsoft Copilot Studio agents, enabling real-time answers to business questions about inventory, orders, and products.

Category
访问服务器

README

Shopify Enterprise MCP Server

Production-grade Model Context Protocol server that exposes live Shopify Admin GraphQL data to Microsoft Copilot Studio agents.

Every business question is answered by querying Shopify at request time. The server never returns cached figures and instructs the connected agent never to answer from prior knowledge — inventory, orders and sales change continuously, so a remembered number is a wrong number.


Status

Complete. Twenty-one tools — seventeen read-only, four that change the store — implemented, unit tested and verified against a live Shopify store.

Area State
Build, TypeScript strict mode, ESLint, Prettier ✅ Complete
Configuration + fail-fast environment validation ✅ Complete
Shopify authentication (client credentials + renewal) ✅ Complete
Error hierarchy + RFC 9457 global handler ✅ Complete
Structured logging + correlation context ✅ Complete
Application Insights telemetry ✅ Complete
Shopify GraphQL client (retry, cost, throttle, paging) ✅ Complete
MCP registry, server factory, Streamable HTTP transport ✅ Complete
Health / readiness / version endpoints ✅ Complete
All 21 MCP tools ✅ Complete
Store-changing tools, gated and confirmed ✅ Complete
Test suite ✅ 446 passing

Available MCP tools

Catalogue

Tool Answers
search_products "How many Nike shoes are in stock?", "Find products under 50", "What perfumes do we sell?"
get_product_details Full record for one product: every variant, price, SKU, stock level, options and collections.
recommend_related_products "What else would go with this?" — catalogue similarity, not purchase behaviour.
get_collections "What collections do we have?", "Is that collection rule-based or curated?"

Orders, fulfilment and customers

Tool Answers
get_orders "How many orders this week?", "Show unfulfilled orders", "Any refunds?"
get_order_by_id Everything about one order: line items, addresses, fulfilment, refunds.
get_fulfillments "What has shipped?", "How long are we taking to despatch?", "No tracking?"
get_refunds "How much have we refunded?", "What came back, and was it restocked?"
search_customers "Who are our customers?", "Find customers tagged VIP", "Customers in India"
get_customer One customer's full profile, addresses and lifetime value.
get_customer_purchase_history "What has this person bought?", "When did they last order?"

Inventory

Tool Answers
get_inventory "What's in stock?", "Stock level for SKU X", "Anything oversold?"
get_low_stock_products "What's running low?", "What do we need to reorder?"

Commercial

Tool Answers
get_sales_summary "How much did we sell last month?", "What's our average order value?"
get_best_selling_products "What are our top sellers?", "Which product made the most revenue?"
get_discounts "What promotions are running?", "Is the free shipping offer still active?"
get_abandoned_carts "How many abandoned carts?", "What revenue are we losing at checkout?"

Every list tool filters and paginates server-side in Shopify. A price filter applied locally could only narrow the page that happened to be fetched, so "products under 50" would silently come to mean "products under 50 among the 20 that came back".

Tools that change the store

Tool Effect
create_product Adds a product. Created as DRAFT unless ACTIVE is asked for.
update_product Changes title, description, vendor, type, tags or status.
update_inventory_levels Sets or adjusts stock on hand, which decides what customers can buy.
create_refund Returns real money to a customer. Cannot be undone.

Three independent controls stand in front of them:

  1. Registration gate. MCP_ENABLE_WRITE_TOOLS=false withholds all four from tools/list entirely — an agent cannot call what was never published. It is a single App Service setting, so the capability can be withdrawn during an incident without a redeploy.
  2. Explicit confirmation. Every one requires confirm: true. Called without it they change nothing and instead report exactly what would change — for stock, after reading the live level, so the user approves real before-and-after figures rather than an intention.
  3. userErrors enforcement. Shopify rejects a mutation with HTTP 200, a null payload and the reason in userErrors. Every mutation response is checked, so a write that did not happen is never reported as one that did.

create_refund adds two more: the amount is always computed by Shopify's own suggestedRefund rather than by this server, and MCP_MAX_REFUND_AMOUNT can cap any single refund.

Every completed change writes an audit line carrying the correlation ID; refunds log at warn.


Documentation

Document Covers
architecture.md Layering, request flow, and the reasoning behind each design decision
configuration.md Every environment variable, production invariants, common misconfigurations
deployment.md Full Azure App Service procedure, slots, scaling, rollback, checklist
copilot-studio.md Connector import, agent wiring, grounding instructions, troubleshooting
copilot-studio-connector.yaml Power Platform custom connector definition
tools.md Each tool with real request/response examples
operations.md Runbook: KQL queries, alerts, incident diagnosis, rotation

Architecture

┌──────────────────────┐   Streamable HTTP (JSON-RPC 2.0)   ┌────────────────────────────┐
│  Copilot Studio      │ ─────────────────────────────────► │  Azure App Service (Linux) │
│  custom connector    │        HTTPS + x-api-key           │  Fastify + MCP SDK         │
└──────────────────────┘                                    └─────────────┬──────────────┘
                                                                          │
                        ┌─────────────────────────────────────────────────┘
                        ▼
        ┌───────────────────────────┐   Correlation context (AsyncLocalStorage)
        │  MCP transport            │   flows through every layer below
        │  (stateless per request)  │
        └─────────────┬─────────────┘
                      ▼
        ┌───────────────────────────┐   Cross-cutting: validation, timing,
        │  Tool executor            │   envelope, error → result, telemetry
        └─────────────┬─────────────┘
                      ▼
        ┌───────────────────────────┐   Business logic only
        │  Service layer            │
        └─────────────┬─────────────┘
                      ▼
        ┌───────────────────────────┐   Retry · cost governor · pagination
        │  ShopifyGraphQLClient     │   error mapping · dependency telemetry
        └─────────────┬─────────────┘
                      ▼
             Shopify Admin GraphQL API

Layer rules

  • Tools contain no business logic. They validate input, call a service, and return a summary plus structured data.
  • Services contain no MCP or HTTP concepts. They accept typed arguments and return domain objects.
  • The Shopify client is the only egress point. Nothing else issues an outbound request.
  • Configuration is read once, validated, frozen. No module touches process.env directly.

Project structure

src/
  app/            Fastify composition root, plugins, routes, error handler
  config/         Environment schema, validated + frozen configuration
  mcp/
    registry/     Tool definitions and the process-wide tool catalogue
    server/       McpServer factory and the tool execution wrapper
    transport/    Streamable HTTP transport management
  tools/          MCP tool definitions (composition root)
  services/       Domain services (product, order, inventory, customer, …)
  shopify/
    graphql-client.ts   Retry, cost governance, error mapping, telemetry
    cost-governor.ts    Client-side model of Shopify's leaky bucket
    error-mapper.ts     HTTP + GraphQL failure classification
    pagination.ts       Bounded cursor pagination
    queries/            Reusable GraphQL documents
    types/              Connection, money and cost types
  middleware/
    auth/           API key authentication
    logging/        Pino logger and AsyncLocalStorage request context
    telemetry/      Application Insights behind a vendor-neutral port
  errors/         Error hierarchy, codes, normaliser
  schemas/        Shared Zod schemas
  utils/          Identifiers, timing, backoff
  types/          Shared type declarations
tests/
  unit/ integration/ mocks/ helpers/
docs/

Getting started

Prerequisites

  • Node.js 22 LTS (the toolchain also runs on Node 24)
  • npm 10+
  • A Shopify app created in the Dev Dashboard, installed on your store

Install and run

npm install
cp .env.example .env       # then fill in the Shopify values
npm run dev                # watch mode
npm run build && npm start # production build

Verify

npm run verify             # typecheck + lint + tests
npm run test:coverage      # coverage report
curl http://localhost:8080/health
curl http://localhost:8080/ready
curl http://localhost:8080/version

MCP handshake:

curl -X POST http://localhost:8080/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{
        "protocolVersion":"2025-06-18","capabilities":{},
        "clientInfo":{"name":"curl","version":"1.0"}}}'

Getting the Shopify credentials

Shopify has retired legacy custom apps. The store admin no longer offers "Create an app", and there is no long-lived Admin API token to copy anywhere. Apps are now created in the Shopify Dev Dashboard, and credentials are exchanged for a short-lived access token through the OAuth client credentials grant.

  1. Create an app in the Dev Dashboard and install it on your store.
  2. Open your app → Settings → Credentials and copy the Client ID and Secret (shpss_…).
  3. Set them as SHOPIFY_CLIENT_ID and SHOPIFY_CLIENT_SECRET.

The server exchanges them for a token at startup and renews it automatically. Verify manually with:

curl -X POST https://<store>.myshopify.com/admin/oauth/access_token \
  -d grant_type=client_credentials \
  -d client_id=<CLIENT_ID> -d client_secret=<CLIENT_SECRET>
# -> {"access_token":"shpat_…","scope":"…","expires_in":86399}

expires_in is 24 hours. This is why a pasted SHOPIFY_ADMIN_ACCESS_TOKEN is rejected in production: it works the day it is set and starts returning HTTP 401 the next.

The grant requires the app and the store to be in the same Shopify organization. If they are not, Shopify answers invalid_client — which looks identical to a wrong secret.

Required read scopes: read_products, read_inventory, read_locations, read_orders, read_all_orders, read_customers, read_discounts, read_price_rules, read_analytics, read_marketing_events.

Grant read scopes only. This server is read-only and advertises readOnlyHint: true, but the credential itself is not constrained by that — an app granted write_products holds the ability to delete a catalogue, on a connection whose other end is an LLM.


Configuration

Every variable is validated at startup. A misconfigured deployment fails immediately with every offending variable listed at once, rather than failing on the first one or booting half-configured.

See .env.example for the full annotated contract. The essentials:

Variable Required Default Purpose
SHOPIFY_STORE Yes Store handle or *.myshopify.com domain
SHOPIFY_CLIENT_ID Yes¹ Dev Dashboard app client ID
SHOPIFY_CLIENT_SECRET Yes¹ Dev Dashboard app secret (shpss_…)
SHOPIFY_ADMIN_ACCESS_TOKEN Local dev only¹ Pre-minted token; expires in 24h
SHOPIFY_API_VERSION No 2026-07 Quarterly Admin API version
PORT No 8080 Injected by App Service
MCP_HTTP_PATH No /mcp Must match the connector's declared path
MCP_API_KEY In production Shared secret for the connector
MCP_ALLOWED_HOSTS In production DNS-rebinding protection allowlist
APPINSIGHTS_CONNECTION_STRING In production Application Insights
LOG_PRETTY Must be false in production false Human-readable logs (local only)

¹ Supply either the client credentials pair or a static token. Startup fails if neither is present. When both are given, client credentials win — they are the only renewable option — and the active mode is stated in the startup log and on GET /version.

Production invariants

The server refuses to start in NODE_ENV=production when any of these hold:

  • no MCP_API_KEY — an unauthenticated MCP endpoint exposes the store's entire order and customer dataset to anonymous callers;
  • no APPINSIGHTS_CONNECTION_STRING;
  • no MCP_ALLOWED_HOSTS;
  • LOG_PRETTY=true;
  • a static SHOPIFY_ADMIN_ACCESS_TOKEN instead of client credentials — it would expire within 24 hours with no way to renew itself.

These are configuration errors, not warnings.


Operational endpoints

Endpoint Question answered Behaviour
/health Is the process alive? Always 200 while running. No I/O.
/ready Can this instance serve a request? 200 / 503 based on a cached, 1-cost-point Shopify probe.
/version Which build, store, API version, tools? Build identity and the published tool catalogue.

Configure the App Service health check against /health, not /ready. Pointing the platform probe at a dependency-aware endpoint makes App Service recycle healthy workers during a Shopify incident, turning a degraded service into an outage. Use /ready for deployment gates and load balancer decisions.


Design decisions worth knowing

Stateless MCP sessions by default. A fresh McpServer is built per request, so any App Service instance can serve any request and scale-out needs no ARR affinity. MCP_SESSION_MODE=stateful is available but requires session affinity.

Tokens are minted and renewed by the server. Shopify's client credentials grant issues 24-hour tokens. The provider caches one in memory, renews it 5 minutes before expiry, and collapses concurrent callers onto a single in-flight mint so a burst of tool calls on a cold instance does not trigger a token request each. If Shopify rejects a token early — after a credential rotation — the client discards it and retries exactly once, outside the retry budget, so recovery also works for callers that disable retries (such as the readiness probe).

Client-side cost governance. Shopify meters GraphQL by query cost against a leaky bucket (1000 points refilling at 50/s on standard plans; the live test store reported 4000 at 200/s). The client reads extensions.cost from every response, adopts whatever limits the store actually reports, and waits for headroom before dispatching — rather than absorbing a THROTTLED error and retrying into an empty bucket.

Search filters come from an allowlist. Shopify silently ignores filter fields it does not recognise: price:<50 filters correctly, while variants.price:<50 is dropped and returns unfiltered products with HTTP 200 and no error at all. An agent would then relay those results as though they satisfied the filter. Every field name the query builder emits is drawn from a fixed list verified against the live API, and values are escaped and quoted.

Stock figures are reported honestly. Shopify returns totalInventory: null for untracked products, which is not the same as zero in stock, so the domain model keeps a separate inventoryTracked flag. Live data also showed a product reporting totalInventory: 0 while its variants stood at -31, -19 and -23; the summary states the oversell rather than repeating the zero.

Throttling arrives as HTTP 200. The Admin API reports throttling as a successful HTTP response with errors[].extensions.code === "THROTTLED". The client checks the GraphQL error array and the HTTP status, and classifies each failure mode for retryability independently.

Tool failures are returned, not thrown. A thrown error becomes a JSON-RPC protocol error, which Copilot Studio surfaces as an opaque connector failure the agent cannot reason about. Tool failures are returned as isError: true results carrying the reason, retryability, remediation and correlation ID, so the agent can explain the problem to the user.

Uniform response envelope. Every tool returns { summary, resultCount, truncated, data }. summary gives the agent a grounded sentence it can relay verbatim; truncated lets it state honestly that a figure is a bounded sample rather than a store-wide total.

Correlation without plumbing. Correlation IDs propagate through AsyncLocalStorage, so the domain layer carries no context parameters, yet every log line, telemetry item and Shopify call is attributable to the originating request.


Copilot Studio compatibility

  • Streamable HTTP only. Copilot Studio dropped SSE transport support in August 2025.
  • Custom connector with x-ms-agentic-protocol: mcp-streamable-1.0 on a POST operation.
  • HTTPS, terminated by App Service.
  • Accept header shim. The MCP specification requires clients to accept both application/json and text/event-stream. Power Platform connectors may forward only application/json, which the SDK rejects with HTTP 406 — surfacing in Copilot Studio as an unexplained connector failure. The MCP route widens the header (on rawHeaders, which is what the transport actually reads).
  • Tool metadata is validated at registration: snake_case names and descriptions long enough to drive orchestrator routing. Both input and output schemas are published.

Connector setup, the OpenAPI schema and Azure deployment steps are documented in a later milestone.


Testing

npm test              # all suites
npm run test:unit
npm run test:integration
npm run test:coverage

Integration tests drive the real Fastify instance via app.inject() — correlation hooks, security plugins, error handler, route wiring and the MCP SDK transport all execute; only the network is absent. Shopify is substituted at the transport seam, so retry sequencing, cost accounting and error mapping are exercised for real.


Scripts

Script Purpose
npm run dev Watch mode via tsx
npm run build Clean + compile to dist/
npm start Run the compiled server
npm run verify Typecheck + lint + test
npm run typecheck tsc --noEmit
npm run lint ESLint, zero warnings tolerated
npm run format Prettier

Troubleshooting

Symptom Cause and fix
Startup fails listing environment variables Working as designed. Fix every listed variable; all problems are reported at once.
/ready reports SHOPIFY_AUTHENTICATION_FAILED Wrong client credentials, app and store in different Shopify organizations, or a missing read scope.
/ready reports SHOPIFY_API_ERROR with a 404 hint SHOPIFY_STORE or SHOPIFY_API_VERSION is wrong; an unsupported version returns 404.
HTTP 406 from /mcp The client sent a narrow Accept header. The shim handles the common case; check for a proxy rewriting headers.
HTTP 401 from /mcp MCP_API_KEY mismatch. The connector must send it as x-api-key or Authorization: Bearer.
Frequent SHOPIFY_THROTTLED warnings The store's cost budget is under pressure. Raise SHOPIFY_MIN_COST_BUFFER or lower page sizes.
Logs are unreadable JSON locally Set LOG_PRETTY=true. It is rejected in production, where JSON is required.

Every error response and tool failure carries a correlation ID. Search Application Insights on customDimensions.correlationId to retrieve the full request trace, including each Shopify call, its cost and its outcome.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选