# SLC MCP Protocol

This file is published to <a href="https://mcp.g3nretailstack.com/slc/PROTOCOL.md" target="_blank" rel="noopener noreferrer">https://mcp.g3nretailstack.com/slc/PROTOCOL.md</a>.

## Usage patterns (headless)
- Stack-wide SOPs & operations catalog: <a href="https://doc.g3nretailstack.com/story/operations.html" target="_blank" rel="noopener noreferrer">/story/operations.html</a>.
- Super-usecase scenarios + QA status: <a href="https://doc.g3nretailstack.com/story/super-usecases.html" target="_blank" rel="noopener noreferrer">/story/super-usecases.html</a>.
- This protocol stays contract-only; use the catalogs for workflow expectations.

## Base URL
- API Gateway: `https://api.g3nretailstack.com/slc`
- Health check: invoke `{"action":"ping"}` — no auth required.

## Auth + tenancy
- Header auth is canonical (x-session-guid or x-api-key via USM/UAS/OFM).
- Every tenant call requires orgcode in the request body.
- SLC uses an action-dispatch model (not REST routes): `{"action":"<action>", ...body}`.

## Roles
- Read: `slc_view`, `slc_manage` (owner implied).
- Write: `slc_manage` (owner implied).

## Channel Types
- **Standard:** Full product lifecycle — create/update/archive products on Shopify. Tags, options, pricing, inventory, weight, captions, handles.
- **Custom:** Variant-level updates only — price, inventory, cost, barcode, metafields.

## Endpoint inventory (action-based)

| Action | Auth | Description |
| --- | --- | --- |
| `channel/create` | slc_manage | Create inactive channel with config |
| `channel/get` | slc_view | Get channel (credentials redacted) |
| `channel/list` | slc_view | List channels for org |
| `channel/update` | slc_manage | Update name/config |
| `channel/activate` | slc_manage | inactive → active |
| `channel/pause` | slc_manage | active → paused |
| `channel/resume` | slc_manage | paused → active |
| `channel/deactivate` | slc_manage | any → inactive |
| `channel/delete` | slc_manage | Permanent removal (requires confirmation) |
| `channel/export` | slc_view | Export config (credentials redacted) |
| `channel/force-sync` | slc_manage | Enqueue all explicit variants → SQS |
| `channel/force-sync-variant` | slc_manage | Enqueue specific variants → SQS |
| `channel/stats` | slc_view | Sync stats for period (1h/6h/24h/7d/30d/90d) |
| `variants/add` | slc_manage | Batch add to inclusion list |
| `variants/remove` | slc_manage | Batch remove from inclusion list |
| `variants/list` | slc_view | Paginated list of included variants |
| `facility/bind` | slc_manage | Bind facility to channel |
| `facility/unbind` | slc_manage | Unbind facility (blocks primary) |
| `credentials/rotate` | slc_manage | Rotate Shopify token (tests connectivity first) |
| `audit/variant-history` | slc_view | Query audit by variant GUID (GSI2, paginated) |
| `audit/shopify-history` | slc_view | Query audit by Shopify variant ID (GSI3, paginated) |
| `audit/sku-history` | slc_view | Query audit by SKU across channels (GSI4, paginated) |
| `channel/uninstall` | slc_manage | Revoke Shopify token + deactivate channel |
| `channel/purge-maps` | slc_manage | Delete all Shopify ID maps for channel |
| `oauth/install-url` | slc_view | Generate Shopify OAuth install URL |
| `oauth/exchange` | slc_manage | Exchange OAuth code for token |
| `vacuum/org` | slc_manage | Delete all channel data for org (requires confirmation) |
| `ping` | none | Health check |

## OAuth Lambdas (separate, not action-dispatch)

| Method | Path | Auth | Description |
| --- | --- | --- | --- |
| GET | /slc | none | Shopify app install entry point — redirects to Shopify OAuth |
| GET | /slc/oauth/callback | none | Shopify token exchange callback — completes OAuth flow |

## State Machine

```
inactive → activate → active → pause → paused → resume → active
                       ↓                                    ↓
                  deactivate                           deactivate
                       ↓                                    ↓
                    inactive                             inactive
                       ↓
                    delete (requires confirmation phrase)
```

## Background Lambdas

| Lambda | Trigger | Purpose |
| --- | --- | --- |
| SlcSyncWorker | SQS (slc-sync-queue) | Outbound sync (custom + standard) |
| SlcEvaluator | EventBridge | Change detection → SQS enqueue |
| SlcInbound | EventBridge | Shopify order/restock → ICS stock card |
| SlcCredentialHealth | Scheduled (daily 06:00 UTC) | Shopify API connectivity check |
| SlcDriftDetect | Scheduled (daily 07:00 UTC) | Sync drift detection |

## Error codes

| Tag | HTTP | Error Code | Retryable |
| --- | --- | --- | --- |
| `invalid-input` | 400 | `slc.validation_failed` | No |
| `validation-error` | 400 | `slc.validation_failed` | No |
| `not-found` | 404 | `slc.not_found` | No |
| `forbidden` | 403 | `slc.forbidden` | No |
| `invalid-state` | 409 | `slc.conflict_state` | No |
| `credential-test-failed` | 422 | `slc.validation_failed` | No |
| `internal-error` | 500 | `slc.internal-error` | Yes |

## Idempotency & retries
- All **GET / list / resolve / search** calls are safe to retry with identical inputs (read-only, no side effects).
- **POST mutations** that accept `expected_revision` use optimistic concurrency: on `409 conflict` or `428 expected-revision-required`, re-read the record, obtain the current `revision`, and retry with the updated value.
- Creates are generally **not** idempotent. Prefer caller-provided `code` (where supported) and verify existence before retrying a failed create.
- Bulk or scheduled jobs that accept an `idempotency_key` will de-duplicate within the documented time window.

## Known pitfalls
- **Missing `expected_revision`**: most state-changing operations require it; omitting it returns `428` with the current revision in `error.details`.
- **Stale revision**: reading a record, waiting, then writing with an outdated `revision` triggers `409`. Always use the latest revision from the most recent read.
- **Pagination cursors**: `next_token` is opaque JSON. Do not modify, decode, or persist cursors across sessions — they may change format between deploys.
- **Anti-enumeration 404**: some org-scoped reads return `404` even when the record exists, if the caller is not associated with the org. Treat `404` as ambiguous; verify caller association before assuming "not found".


_Build MONDAY-1776194870 • 2026-04-14T19:27:50.000Z • [© 1999 Microhouse Systems Inc. All rights reserved.](https://doc.g3nretailstack.com/common/copyright-license.html)_
