Reference

Security

ShopMCP holds the keys to your ecommerce stack, so security is the floor, not a feature. Here's exactly what we do.

Credential encryption

Every credential you give us — Shopify offline tokens, Neto API keys, Google refresh tokens — is encrypted with envelope encryption before it touches Postgres:

  • Each workspace has its own data encryption key (DEK), generated on first connection.
  • DEKs are wrapped by a customer master key (CMK) in AWS KMS. The plaintext DEK only exists in memory inside the runtime, cached for 60 seconds, then evicted.
  • Postgres only ever stores ciphertext + the KMS key ID. A database dump on its own is useless — you'd also need KMS access.
  • KMS access is limited to a single IAM role used by the runtime, audited by AWS CloudTrail.

Tenant isolation

Every tenant-owned table carries a workspace_id. Postgres Row-Level Security policies gate every query by a session variable set in middleware. Even if a query is missing an explicit filter, RLS rejects rows from other workspaces.

The MCP runtime resolves the workspace from the bearer token on every request, opens an AsyncLocalStorage scope with the credentials, and runs the tool inside it. There is no shared module-level state holding credentials.

What we never store

  • Plaintext credentials — only KMS-wrapped ciphertext.
  • Tool call response bodies — we store metadata (tool name, latency, status) for billing and observability, but not the actual data the tool returned.
  • Chat conversations — those live entirely inside your chat client. We never see them.
  • Email or PII in error logs — Sentry is configured to scope users by workspace_id only, never by email.

GDPR

ShopMCP is GDPR-compliant out of the box. We support all the data subject rights:

  • Access: request a JSON export of everything we hold for your workspace.
  • Erasure: deleting a workspace removes all its connections, credentials, usage events, and audit logs within 30 days. Backups roll off within 60.
  • Portability: usage events and playbooks export as JSON.
  • Shopify GDPR webhooks: we honor customers/data_request, customers/redact, and shop/redact automatically.

A signed Data Processing Agreement is available on request — email legal@shop-mcp.app.

Subprocessors

The vendors that handle data on our behalf:

  • Vercel — dashboard hosting (US/EU edge)
  • Railway — MCP runtime hosting (US-East)
  • Neon — Postgres database (US-East/EU)
  • Upstash — Redis cache and rate limiting
  • AWS KMS — credential encryption keys
  • Stripe — billing
  • Sentry — error tracking (PII-scrubbed)
  • Grafana Cloud — metrics and traces (no PII)
  • Unosend — transactional and OTP email

Prompt injection & ecommerce tool safety

An LLM reading untrusted text — a support email, a product review, a return reason, even a page it was asked to summarise — can be tricked into issuing tool calls on the user's behalf. Mark Shust's well-known warning about Magento MCPs crystallises the threat model. ShopMCP takes it seriously and applies the same defences to every ecommerce module — Shopify, Neto, BigCommerce, Magento:

  • Allowlist-only tools. Every MCP tool is a hand-written handler bound to a specific upstream endpoint, verb, and field-level input schema. There is no generic call_rest, execute_graphql, or run_shell escape hatch. An LLM that has been prompt-injected cannot invent new endpoints — it can only invoke tools we shipped.
  • Read-only by default. Write tools are compiled into each module package but only registered when the runtime explicitly asks for them. For Magento and Neto, v1 passes allowWrites: false for every workspace. Write registration will later be per-workspace and opt-in, never a global switch.
  • Field-level write whitelists. When writes are on, each write tool accepts a hand-picked subset of fields via a strict Zod schema. For example, magento_update_product accepts only name, price, special_price, status, visibility, description, short_description, weight, qty, and is_in_stock — SKU rename, attribute_set_id, website_id, and bulk custom-attribute replacement are rejected before an HTTP request is built. Dangerous fields (password, role_id, is_active) are hard-coded forbidden regardless of the upstream ACL.
  • Upstream ACL is the final gate. For platforms that support it — Magento Integrations, Neto API users, Shopify custom app scopes — we ask the merchant to mint a token with the narrowest possible scope and walk them through it in the connect docs. Even if the runtime is compromised, the upstream platform rejects out-of-scope calls.
  • No TLS bypass, no auto-retry on 4xx. Upstream clients never disable certificate verification (the boldcommerce/magento2-mcp reference does this “for development” — we don't). 4xx responses surface immediately to the LLM so the user sees the failure; we do not retry injected-failure loops that could amplify damage.
  • Every call is audited. Each tools/call writes a row to usage_events with workspace, tool name, latency, and status, plus a Stripe meter event. Merchants can review every tool the LLM invoked under their token from /settings/api-keys.
  • Rate limiting. Every authenticated MCP request flows through an Upstash sliding-window limiter keyed by workspace. A runaway prompt-injected loop is capped by plan tier before it can exhaust the upstream API budget.
Defences lower the blast radius — they don't eliminate it. Treat any text from outside your company (support emails, reviews, shipping comments, user-generated content) as potentially hostile before you paste it into a chat alongside a write-capable tool call. Always start a new merchant read-only and review /settings/api-keys usage before turning writes on.

SOC 2 roadmap

SOC 2 Type I preparation kicks off after we have 50 paying customers. Type II follows once we've been operating with the controls in place for six months. Most of the controls are already in production — we just haven't gone through the audit yet.

Reporting a vulnerability

Email security@shop-mcp.app. We reply within 24 hours, acknowledge valid reports, and credit you in our changelog if you'd like.