Shopify Mcp — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited Shopify Mcp (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Production-ready Shopify Admin MCP server for Google Cloud Run. It exposes an authenticated MCP endpoint at /mcp and safe Shopify Admin GraphQL tools for products, orders, inventory, discounts, and service health.
/mcp request must include Authorization: Bearer <MCP_API_KEY> or a valid OAuth access token issued by this server.MCP_LOCAL_NO_AUTH=true disables MCP auth for loopback requests only and should be used only with HOST=127.0.0.1.Origin is validated when present. Set MCP_ALLOWED_ORIGINS to a comma-separated list for browser clients.confirm=true.ALLOW_WRITE_TOOLS=true.read_productswrite_productsread_ordersread_inventorywrite_inventoryread_discountswrite_discountsread_locations.env file.Copy .env.example to .env for local development:
cp .env.example .envRequired values:
SHOPIFY_SHOP_DOMAIN=your-store.myshopify.com
SHOPIFY_ADMIN_ACCESS_TOKEN=replace-with-shopify-admin-api-token
SHOPIFY_API_VERSION=2026-04
MCP_API_KEY=some-secret-key
ALLOW_WRITE_TOOLS=falseOptional values:
PUBLIC_BASE_URL=https://your-cloud-run-service-url
OAUTH_AUTHORIZATION_PASSWORD=owner-login-password-for-chatgpt-linking
OAUTH_TOKEN_SECRET=random-oauth-signing-secret
MCP_ALLOWED_ORIGINS=https://your-client.example.com
SHOPIFY_REQUEST_TIMEOUT_MS=20000
SHOPIFY_MAX_RETRIES=2
MCP_LOCAL_NO_AUTH=falseKeep ALLOW_WRITE_TOOLS=false unless you intentionally want this MCP server to edit Shopify data.
PUBLIC_BASE_URL is recommended in Cloud Run so OAuth discovery metadata uses the exact public service URL. OAUTH_AUTHORIZATION_PASSWORD is the password you enter in the browser when ChatGPT links the app. OAUTH_TOKEN_SECRET signs short-lived OAuth access tokens.
./scripts/install-local-deps.sh
npm run devThe normal local/dev service listens on 0.0.0.0 using process.env.PORT, defaulting to 8080, and requires Authorization: Bearer <MCP_API_KEY> on /mcp.
Test health:
curl http://localhost:8080/healthTest MCP initialization:
curl http://localhost:8080/mcp \
-H "Authorization: Bearer $MCP_API_KEY" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1.0.0"}}}'List tools:
curl http://localhost:8080/mcp \
-H "Authorization: Bearer $MCP_API_KEY" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'Use this mode when connecting this local Shopify MCP server to ChatGPT through Secure MCP Tunnel. It keeps the MCP server on loopback and does not expose it publicly.
.envcp .env.example .envFill these values in .env:
SHOPIFY_SHOP_DOMAIN=your-store.myshopify.com
SHOPIFY_ADMIN_ACCESS_TOKEN=your-shopify-admin-token
SHOPIFY_API_VERSION=2026-04
ALLOW_WRITE_TOOLS=falseFor local tunnel mode, MCP_API_KEY, PUBLIC_BASE_URL, OAUTH_AUTHORIZATION_PASSWORD, and OAUTH_TOKEN_SECRET are not used by the tunnel connector because the local server runs with loopback-only no-auth. Keep real values out of git.
./scripts/install-local-deps.shThis installs Node dependencies into project-local node_modules/ and creates .local/bin and .local/downloads for local tools. It does not install npm packages globally.
./scripts/run-local-tunnel.shThis starts:
http://127.0.0.1:8080/mcpIn this mode:
HOST defaults to 127.0.0.1.PORT defaults to 8080.MCP_LOCAL_NO_AUTH=true is set by the script./mcp accepts unauthenticated requests only from loopback.tunnel-clientDownload tunnel-client from OpenAI Platform tunnel settings or from the latest public release:
https://github.com/openai/tunnel-client/releases/latestPlace the binary in the project-local tool directory:
mkdir -p .local/bin .local/downloads
mv /path/to/tunnel-client .local/bin/tunnel-client
chmod +x .local/bin/tunnel-client
./.local/bin/tunnel-client --helpDo not commit the binary or downloaded archives. .gitignore excludes .local/, tunnel-client*, and common archive formats.
Use your real tunnel ID:
./.local/bin/tunnel-client init \
--sample sample_mcp_remote_no_auth \
--profile shopify-local \
--tunnel-id YOUR_TUNNEL_ID \
--mcp-server-url http://localhost:8080/mcpIf you keep tunnel-client on your PATH, the same command is:
tunnel-client init --sample sample_mcp_remote_no_auth --profile shopify-local --tunnel-id YOUR_TUNNEL_ID --mcp-server-url http://localhost:8080/mcpThen run it:
./.local/bin/tunnel-client run --profile shopify-localKeep both processes running:
./scripts/run-local-tunnel.sh./.local/bin/tunnel-client run --profile shopify-localThe Shopify MCP server uses port 8080. If tunnel-client also tries to bind its health/admin server to port 8080, edit the generated shopify-local tunnel-client profile and set its health listener to loopback port 8081.
Use the config format generated by your tunnel-client version. The setting is typically named like this:
health:
listen_addr: 127.0.0.1:8081or:
[health]
listen_addr = "127.0.0.1:8081"After changing it, restart tunnel-client run --profile shopify-local.
YOUR_TUNNEL_ID tunnel/profile that maps to shopify-local.Choose No Authentication because the tunnel already authenticates the OpenAI-to-tunnel path, and the local Shopify MCP server uses local .env credentials while accepting only loopback requests from tunnel-client.
Port 8080 already in use
Find and stop the process:
lsof -nP -iTCP:8080 -sTCP:LISTENOr run the local server on another loopback port:
PORT=8082 ./scripts/run-local-tunnel.shThen re-run tunnel-client init with:
--mcp-server-url http://localhost:8082/mcp`tunnel-client` health server is using port 8080
Edit the generated tunnel-client profile and set:
health.listen_addr = 127.0.0.1:8081Then restart tunnel-client run --profile shopify-local.
ChatGPT cannot discover tools
Check each layer:
curl http://127.0.0.1:8080/health
./.local/bin/tunnel-client doctor --profile shopify-local --explainMake sure ./scripts/run-local-tunnel.sh and tunnel-client run --profile shopify-local are both still running. If you recently changed tools, refresh the ChatGPT connector or recreate it so the tool schema is rediscovered.
In local tunnel mode, full OAuth login routes are disabled, but the server still exposes OAuth protected-resource metadata so tunnel-client doctor can validate the MCP target. ChatGPT should still be configured with No Authentication for this local tunnel profile.
Missing env vars
./scripts/run-local-tunnel.sh checks:
SHOPIFY_SHOP_DOMAINSHOPIFY_ADMIN_ACCESS_TOKENSHOPIFY_API_VERSIONFill them in .env. Do not commit .env.
Dependency install blocked by system Python or `externally-managed-environment`
This repo is a Node.js app. Use:
./scripts/install-local-deps.shIt installs into local node_modules/ and does not use global Python packages. If a separate tunnel-client helper asks for Python packages, create a local venv instead of installing globally:
python3 -m venv .venv
. .venv/bin/activate
python -m pip install --upgrade pipAll write tools support preview mode. For example, this returns the proposed change without writing to Shopify:
{
"productId": "gid://shopify/Product/123",
"seoTitle": "New SEO title",
"seoDescription": "New SEO description",
"confirm": false
}To write, both conditions are required:
ALLOW_WRITE_TOOLS=true in the service environment."confirm": true to the tool call.If either condition is missing, the server does not write.
For update_inventory_quantity, a confirmed write first reads the current available quantity at the given location and sends it as Shopify's compare value with an idempotency key. If inventory changed between preview and confirm, Shopify returns a user error instead of silently overwriting the new value.
Enable required Google APIs:
gcloud services enable run.googleapis.com secretmanager.googleapis.com cloudbuild.googleapis.comCreate Secret Manager secrets:
printf "replace-with-shopify-admin-api-token" | gcloud secrets create shopify-admin-token --data-file=-
printf "some-secret-key" | gcloud secrets create mcp-api-key --data-file=-
printf "owner-login-password" | gcloud secrets create oauth-authorization-password --data-file=-
openssl rand -base64 32 | gcloud secrets create oauth-token-secret --data-file=-Deploy to Cloud Run:
gcloud run deploy shopify-mcp-cloud-run \
--source . \
--region me-west1 \
--allow-unauthenticated \
--set-env-vars SHOPIFY_SHOP_DOMAIN=your-store.myshopify.com,SHOPIFY_API_VERSION=2026-04,ALLOW_WRITE_TOOLS=false,MCP_ALLOWED_ORIGINS=https://chatgpt.com \
--set-secrets SHOPIFY_ADMIN_ACCESS_TOKEN=shopify-admin-token:latest,MCP_API_KEY=mcp-api-key:latest,OAUTH_AUTHORIZATION_PASSWORD=oauth-authorization-password:latest,OAUTH_TOKEN_SECRET=oauth-token-secret:latestCloud Run is marked --allow-unauthenticated so MCP clients can reach the service, but the /mcp endpoint still requires the bearer token.
After the first deploy, get the public service URL:
SERVICE_URL=$(gcloud run services describe shopify-mcp-cloud-run \
--region me-west1 \
--format='value(status.url)')Then set it as PUBLIC_BASE_URL so OAuth metadata is stable:
gcloud run services update shopify-mcp-cloud-run \
--region me-west1 \
--update-env-vars PUBLIC_BASE_URL="$SERVICE_URL"https://your-cloud-run-service-url/mcp.OAUTH_AUTHORIZATION_PASSWORD.The server exposes OAuth discovery at:
/.well-known/oauth-protected-resource/.well-known/oauth-authorization-server/oauth/register/oauth/authorize/oauth/tokensearch_products and get_orders paginate through all matching Shopify pages. Use query to narrow results.
health_checkshopify_admin_graphqlsearch_productsget_productupdate_product_seoupdate_product_descriptionupdate_product_tagsupdate_product_statuscreate_productadd_product_mediaupdate_product_mediadelete_product_mediareorder_product_mediareplace_product_mediaappend_variant_mediadetach_variant_mediaupdate_product_metafieldsget_ordersget_inventory_by_skuupdate_inventory_quantitycreate_discount_code~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.