Shop MCP

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:

  • A fresh data encryption key (DEK) is generated per stored credential via AWS KMS GenerateDataKey, with the workspace id bound as KMS encryption context. KMS binds decrypt requests to the same context, so a ciphertext belonging to workspace A cannot be decrypted under workspace B's scope even if the row is misrouted.
  • DEKs are wrapped by a customer master key (CMK) in AWS KMS. The plaintext DEK only exists in memory inside the runtime, cached for at most 60 seconds, then evicted.
  • Postgres only ever stores ciphertext + the wrapped DEK + the KMS key ID. A database dump on its own is useless — you'd also need KMS access with the correct encryption context.
  • 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 at the start of each transaction. Even if a query is missing an explicit filter, RLS rejects rows from other workspaces.

The dashboard and MCP runtime connect to Postgres as a dedicated tenant role that does not have the BYPASSRLSprivilege. Explicitly cross-tenant paths — Stripe webhooks, scheduled jobs, the admin console — use a separate owner-role connection and always perform an authorisation check before any query. There is no code path that reads one workspace's data while serving a request for another.

The MCP runtime resolves the workspace from the token in the MCP URL 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.

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 and MCP runtime hosting (iad1, US East)
  • Neon — Postgres database (us-east-1)
  • Upstash — Redis rate limiting, workflow queues, and short-lived caches
  • AWS KMS — credential encryption keys
  • Stripe — billing
  • Sentry — error tracking (PII-scrubbed)
  • Sequenzy — transactional email and verification
  • Cloudflare — DNS and edge TLS for shop-mcp.app

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-mcpreference 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/connections.
  • 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.
  • Short-lived provider caching. Repeated Xero read-only tool calls can be cached briefly by workspace and connection id to avoid duplicate provider usage; writes bypass cached reads immediately.
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/connections usage before turning writes on.

Data loss prevention & incident response

ShopMCP maintains internal data loss prevention and incident response policies that map these controls to day-to-day operations: data classification, scoped MCP keys, temporary result storage, retention sweeps, log scrubbing, incident severity, evidence handling, and breach notification.

The public commitments are reflected in this security page, the DPA, the data deletion page, and our security contact. We notify affected customers of a Personal Data Breach without undue delay and within the DPA timeframe.

Compliance roadmap

ShopMCP is pre-audit. Many of the technical controls commonly required by SOC 2 (envelope encryption, tenant isolation, audit logging, least-privilege IAM, dependency scanning) are already in production; formal audit preparation starts once the business reaches sustained paying usage. We will publish the timeline and any interim attestations here as they land.

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.