Skip to main content
Glama

shop-mcp

shop-mcp lets AI agents browse products, look up orders and place orders through five MCP tools. Clients connect to /mcp over Streamable HTTP with their own API keys. Order creation reserves stock in a transaction; both write tools accept idempotency keys for retries.

The server uses strict TypeScript, the official MCP SDK, Express, PostgreSQL with Prisma, Redis, Zod, pino and prom-client.

60-second quickstart

Start Docker Desktop or Docker Engine with Compose, then run:

docker compose up --build -d --wait
docker compose exec -T server npm run --silent key:create -- --scope read_write
curl http://localhost:3000/healthz

The first build may take longer than a minute. Startup applies migrations and fills a fresh database with 40 products in five categories and 200 orders from the last 90 days. Restarts keep the existing data. Prices are decimal strings in USD.

The key command prints your key once. Save it; Postgres stores only its SHA-256 hash.

Compose works with local defaults. To change them, copy .env.example to .env and edit it. In PowerShell, use Copy-Item .env.example .env. The example's DATABASE_URL and REDIS_URL connect from your machine; Compose supplies its own URLs using service names.

You can also create a read-only key or set a customer's quota:

docker compose exec -T server npm run --silent key:create -- --scope read
docker compose exec -T server npm run --silent key:create -- --scope read_write --limit 120

Related MCP server: Agentic MCP Microservices

Connect Claude Code

Replace PASTE_KEY_HERE with your key:

claude mcp add --transport http shop http://localhost:3000/mcp --header "Authorization: Bearer PASTE_KEY_HERE"

Or add this block to your private .mcp.json, which this repo ignores:

{
  "mcpServers": {
    "shop": {
      "type": "http",
      "url": "http://localhost:3000/mcp",
      "headers": { "Authorization": "Bearer ${SHOP_API_KEY}" }
    }
  }
}

Set SHOP_API_KEY in your shell before starting Claude Code, then check the connection with /mcp.

To try all five tools, ask Claude to list products and create an order for one item in stock. Give it a future delivery timestamp and an idempotency key. Then ask it to get the order, search orders and mark the order paid. Repeat the create call with the same arguments and key: it should return the same order.

Connect Claude Desktop

mcp-remote forwards Claude Desktop's local MCP traffic to the HTTP server. Install Node.js 20.19+ on the client machine, add this block to claude_desktop_config.json, and restart Desktop:

{
  "mcpServers": {
    "shop": {
      "command": "npx",
      "args": [
        "-y", "mcp-remote",
        "http://localhost:3000/mcp",
        "--allow-http",
        "--transport", "http-only",
        "--header", "Authorization:${SHOP_AUTH_HEADER}"
      ],
      "env": { "SHOP_AUTH_HEADER": "Bearer PASTE_KEY_HERE" }
    }
  }
}

If Desktop cannot launch npx on Windows, set "command": "cmd" and prepend "/c", "npx" to args. Keep the header argument as Authorization:${SHOP_AUTH_HEADER}, without a space after the colon, so Windows passes it correctly.

The config lives at %APPDATA%\Claude\claude_desktop_config.json on Windows and ~/Library/Application Support/Claude/claude_desktop_config.json on macOS. It contains your key, so keep it out of Git. For managed customer installations, review and pin the bridge version.

Connection options are documented in Claude Code MCP and the mcp-remote README.

Verify the running server

These commands create a key and run the smoke test against the container:

export SHOP_API_KEY="$(docker compose exec -T server npm run --silent key:create -- --scope read_write)"
docker compose exec -T -e SHOP_API_KEY server npm run smoke
curl http://localhost:3000/metrics
docker compose logs server

PowerShell equivalent:

$env:SHOP_API_KEY = docker compose exec -T server npm run --silent key:create -- --scope read_write
docker compose exec -T -e SHOP_API_KEY server npm run smoke
Invoke-RestMethod http://localhost:3000/healthz
Invoke-WebRequest http://localhost:3000/metrics

The smoke test calls all five tools through the SDK HTTP client, repeats a write and checks Prometheus metrics. It leaves one paid order in the demo database. A different response on the repeated call fails the test; the integration suite also checks row counts and stock to confirm that the retry did not write twice.

Results and errors

The tool reference lists every argument, result shape and error code. Strict Zod schemas validate inputs. Each result carries a typed JSON object in structuredContent and a JSON copy in content for older clients. Output schemas cover both successful results and errors with code, message and optional details.

Invalid arguments and rejected shop operations return MCP isError: true. Authentication, rate-limit, session and protocol failures return an HTTP error. Its JSON-RPC error.data holds the same error object. Client errors omit stack traces, SQL and internal identifiers.

Creating an order reserves stock. Status updates allow exactly these transitions:

created -> paid -> packed -> shipped -> delivered
created -> cancelled
paid    -> refunded

Every other transition is rejected, including an update to the current status. Cancellation and refund update the status only; they do not restock items or process payments.

Keys and rate limits

Each request to /mcp checks the Bearer key against its hash in Postgres, even within an existing session. A read key can call the three read tools; read_write permits all five. To revoke a key, set its revokedAt to the current time through a trusted database admin connection.

Missing credentials return missing_api_key. Unknown and revoked keys share invalid_api_key so the response does not reveal whether a key exists. Server logs distinguish them with auth_reason. The creation command prints only the key, without its database id or hash.

The default rate limit is 60 HTTP requests per minute with a burst of 10. Handshakes, notifications and session termination count toward it. Each key's rateLimitPerMinute column sets its quota. Redis updates the token bucket in one Lua script using Redis server time; a 429 response includes Retry-After and details.retry_after_seconds.

Retrying writes

Both write tools accept idempotency_key. Reuse it with the same arguments to get the stored response; changing the arguments returns idempotency_conflict. Omitting it allows a retry to write again. Reusing it with another API key or tool is a separate operation.

After validation, the server hashes a canonical form of the arguments, excluding idempotency_key. Defaults are applied before hashing, and array order matters. Redis stores the tool, argument hash, response and expiry for 24 hours. A retry keeps the original expiry. Once that window ends, another call can write again. Failed shop operations roll back and are not cached.

There is a gap between committing an order in Postgres and saving its response in Redis. The server commits a WriteReceipt in the same transaction as the order, protected by a transaction-scoped advisory lock. If the process dies in that gap, or Redis loses its cache, a retry can recover the response from Postgres. Receipts share the 24-hour expiry and are removed by periodic cleanup.

If Redis is unavailable, MCP requests return 503 dependency_unavailable, including writes: rate limiting fails closed. A dependency failure after a Postgres commit can still leave a completed write behind an error response. Retry with the original idempotency key to recover its result.

Logs and health

/healthz reports Postgres and Redis separately and remains available when either service fails. Request logs go to stderr as JSON with request_id, key_id, tool, duration_ms and outcome. Authentication failures add auth_reason. Logs omit request bodies, customer details, credentials and raw idempotency keys.

/metrics exposes these Prometheus metrics:

  • shop_mcp_requests_total{tool,outcome}

  • shop_mcp_request_duration_seconds{tool}

  • shop_mcp_rate_limit_rejections_total

Sessions and public hosting

Each session has its own SDK server and transport, bound to the client's key. Session ids are random. By default, an idle session expires after 30 minutes and the server accepts up to 1,000 sessions. Responses use JSON; standalone GET/SSE requests receive 405, as allowed by Streamable HTTP. There is no resumable event store. A restart invalidates sessions, so clients must initialize again. Run one instance, or use sticky routing for replicas.

To host it publicly, point a DNS name and a TLS reverse proxy at port 3000. Replace the local database credentials and set ALLOWED_ORIGINS to exact trusted browser origins. Native MCP clients can connect without an Origin header; browser requests from unlisted origins are rejected.

Expose /mcp and use proxy route rules to keep /metrics and /healthz on an operator network. Compose binds Postgres and Redis ports to loopback. Keep both named volumes and back up Postgres. You will need to provision the public hostname and TLS certificate.

Development and tests

Use Node.js 20.19+; the Docker image runs Node 22. Prisma 6 is pinned. Dependency overrides select patched releases of deepmerge-ts and Vitest's worker dependency.

npm ci
docker compose up -d postgres redis
# Copy .env.example to .env to connect from your machine.
npm run db:generate
npm run db:migrate
npm run db:seed
npm run check
npm test
npm run test:integration
npm run build
npm start

npm test checks all 49 pairs of order states. The integration suite uses real Postgres, Redis and SDK HTTP clients. It checks scopes, authentication and revocation, session isolation, idempotency conflicts and recovery, concurrent stock reservations, rate-limit recovery and dependency failures. Tests create their own fixture rows and remove them afterward. Run them against a development database. CI runs both suites and builds the image.

shop-mcp-stdio is a second binary for local debugging only. After a build, set SHOP_API_KEY, DATABASE_URL and REDIS_URL, then run npm run start:stdio. It uses the same tools and key checks. Production clients connect over HTTP. The project has no REST commerce endpoints, payment integrations or OAuth flows.

License: MIT.

Related MCP Connectors

Related MCP Servers