All help

SupplySynk MCP server

SupplySynk exposes its inventory domain to AI agents (Claude, ChatGPT, Cursor, custom agents) over the Model Context Protocol.

  • Endpoints (Streamable HTTP, stateless JSON responses):
    • POST https://<app>/api/mcp/<business-slug> — the business's own address (AI-1). Shown in Settings → Connected apps and Developers. Connecting here needs no organization picker: the address names the business.
    • POST https://<app>/api/mcp — the shared address. Still works for existing connections and API keys; OAuth sign-in asks which business.
  • Auth (either):
    • OAuth 2.1 sign-in — add the address to Claude / ChatGPT / Cursor / VS Code and sign in; no key to copy. The person chooses an access level (below) and which changes to allow.
    • API key from Developers → API Keys: Authorization: Bearer ssk_… (or HTTP Basic clientId:clientSecret) — for server-side agents and automation.
  • Protocol versions: 2025-06-18, 2025-03-26, 2024-11-05

Authorization model

OAuth-connected agents act as the signed-in member and can do only what both that member's role (viewer → owner) and the scopes they approved allow, further limited by the connection's access level (D-9, D-23). The business is fixed at consent (org_id claim; with a per-business address the token is also audience-bound to that address, and a token for business A is refused at business B's address). Membership is re-checked on every request, so removing someone or lowering their role takes effect immediately. Actions are audited under the user with the app's name (via: oauth, clientName), e.g. "Asha via Claude"; approved actions also record approvedBy.

Access levels (AI-2)

Chosen on the consent screen; changed or revoked in Settings → Connected apps. Connections made before levels existed count as Everyday changes.

LeveltierWhat the app can do
Look onlyreadRead tools only; every write scope is dropped from the connection.
Everyday changesactRoutine changes run (stock adjustments, draft POs, counts, orders). High-impact changes wait for approval: sending a purchase order (submit_purchase_order, create_purchase_order with submit: true), price changes (update_product touching prices or tax), orders with a discount or a price other than the product's (create_order), payment status (update_order with paymentStatus, mark_order_paid, create_counter_sale, mark_counter_sale_paid), emailing a shopper (mark_order_ready_for_pickup), deletes and cancellations (destructive tools), returns, bulk import, publishing the store or a store page (publish_store_page, unpublish_store_page), payment options, and anything that acts on Square (sync_square_stock, refresh_square_payments, catalog/location changes).
Ask me firstapprovalEvery change waits for approval.

Approval queue (AI-3)

A change that must wait is not run. The tool returns

{ "status": "pending_approval", "approvalId": "…", "summary": "Adjust stock of Club Whites Shirt (M) at Shop floor by +10, reason \"found in back room\"", "expiresAt": "…", "message": "Sent for approval: …" }

Owners and admins get an email (at most one digest an hour per business) and an item in the bell; an owner, admin or manager approves or rejects it on the Approvals page (phone-friendly). Approving runs the same tool once, as the app acting for the member who asked, recorded "approved by"; the approver must also be allowed to do it. Rejecting records a reason. Requests expire after 7 days. The app checks back with get_pending_action (or list_pending_actions); result holds what the tool returned, error why it failed. AI apps and API keys can never approve. REST: GET /api/v1/approvals, GET /api/v1/approvals/{id}, POST /api/v1/approvals/{id}/approve, POST /api/v1/approvals/{id}/reject.

  • Statuses: pending → executing (one approver claimed it and it is running) → approved (ran; result) or failed (error); or rejected (rejectReason) / expired. A run still executing after 10 minutes is marked failed ("didn't finish; check and retry").
  • The connection must still allow it. Disconnecting the app (by the person, an owner/admin, or a password reset) or lowering it to Look only turns its waiting requests down (rejectReason: connection removed / access lowered). Approving re-checks that the consent still exists, the level isn't Look only and every scope the app held when it asked is still granted.
  • Summaries never hide money: price, cost, discount, tax and payment changes are listed first with their values; order and PO lines show unit prices, costs and discounts.
  • App names are self-reported (dynamic registration). Unless every return address is a known MCP client host (claude.ai, chatgpt.com, …), Approvals, the digest email and the activity log show "Name (unverified, host)".
  • Limits: the same call again within 10 minutes returns the waiting request (duplicate: true); at most 50 waiting requests per app, person and business (too_many_pending).
  • Owners and admins see every AI app connected to the business (GET /api/v1/ai/connections, Settings → Connected apps) and can lower a member's level (PATCH) or disconnect it (POST /api/v1/ai/connections/disconnect); only the member can raise it. A level that isn't one of the three counts as Ask me first.

OAuth details: discovery via RFC 9728 (/.well-known/oauth-protected-resource) and RFC 8414 (/.well-known/oauth-authorization-server); dynamic client registration (RFC 7591, public clients, PKCE required; loopback / custom-scheme callbacks registered as native per RFC 8252); access tokens are 1-hour JWTs audience-bound to the address used (/api/mcp or /api/mcp/<slug>; each business address is its own RFC 8707 resource, created on first use), refresh tokens 30 days; 1000 requests/hour per user per app. Clients registering without scopes get read-only access.

Protections: the consent screen labels apps whose return address isn't a known MCP client host as unverified (names are self-reported) and shows where access is sent; change permissions are opt-in per scope; disconnecting an app (Settings → Connected apps) or resetting your password revokes its tokens and cuts it off on the next request; registration is rate limited (10/hour/IP) and unused registrations are pruned daily.

API keys work as below.

LayerWhat it does
API key → organizationThe key alone decides the tenant. On a tenant host (acme.supplysynk.com / custom domain) the key must belong to that tenant.
Scopes → toolstools/list returns only tools whose scope the key holds; tools/call re-checks, and every service re-checks again.
ServicesSame code path as the REST API and dashboard: strict input allowlists, tenant-checked references, audit log (api_key:<id>), webhooks, stock invariants.
Rate limitThe key's own limit (default 1000/hour), shared across all servers → HTTP 429.

Create a dedicated key per agent with the narrowest scopes it needs (e.g. the Read Only bundle for a reporting assistant).

Tools

AreaToolsScope
Productslist_products, get_product, find_product_by_barcode, get_ai_image_allowance / create_product, update_product, delete_product, add_product_variant, add_product_photo_from_url, generate_product_image, describe_product_photoread:products / write:products
Spreadsheet importget_product_import_format, preview_product_import, import_productswrite:products
Inventorylist_inventory_levels, list_inventory_movements, list_inventory_lots, trace_lot / adjust_inventoryread:inventory / write:inventory
Replenishmentget_reorder_suggestions / create_reorder_purchase_ordersread:reports / write:purchase_orders
Insightsget_sales_insights, get_attention_items, get_weekly_summary, list_merch_suggestionsread:reports, read:inventory
Merchandisingcreate_product_reorder_draft / pin_store_product, exclude_store_productwrite:purchase_orders / write:products (manager and up)
Orderslist_orders, get_order / create_order, update_order, confirm_order, fulfill_order, cancel_order, return_order_itemsread:orders / write:orders
Counter billing (guide)get_counter_setup, get_counter_sale / create_counter_sale, mark_counter_sale_paid, cancel_counter_saleread:orders / write:orders (staff and up)
Online orders (pickup)get_online_order_details / mark_order_paid, mark_order_ready_for_pickup, mark_order_picked_upread:orders / write:orders (staff and up)
Purchasinglist_purchase_orders, get_purchase_order / create_purchase_order, submit_purchase_order, receive_purchase_order, cancel_purchase_orderread:purchase_orders / write:purchase_orders
Transferslist_transfers, get_transfer / create_transfer, ship_transfer, receive_transfer, cancel_transferread:inventory / write:inventory
Stock countslist_stock_counts, get_stock_count / create_stock_count, record_stock_count, complete_stock_count, cancel_stock_countread:inventory / write:inventory
Square (details)get_square_status, get_square_sync_log, get_square_sales_summary / retry_square_event, refresh_square_payments, review_square_catalog, link_square_catalog, sync_square_stock, map_square_locationsread: read:locations, read:orders, read:reports; write: write:orders, write:products, write:inventory, manage:integrations
Online storeget_storefront_settings, get_payment_settings, list_storefront_keys / set_storefront_published, update_payment_settings, create_storefront_key, revoke_storefront_keyread:settings / write:settings (owner or admin)
Store pages (guide)list_store_pages, get_store_page / draft_store_page, create_store_page, update_store_page / publish_store_page, unpublish_store_pageread:settings / write:products (drafts; manager and up) / write:settings (publish; owner or admin). Publishing and unpublishing are high impact: they always wait for a person's approval from an "act" connection. Drafts send the version they read. AI apps and API keys can't add or change customer quotes (testimonial sections); only people signed in to SupplySynk can
Marketinglist_contacts, get_marketing_results / create_contact, create_buy_link, generate_post_caption, create_post_draft, erase_contactread:contacts (staff and up; not a default read scope), read:marketing / write:marketing (staff and up), manage:marketing (erase; managers and up). High impact (waits for approval on an "act" connection): erase_contact, and create_buy_link to another website. Drafts only: nothing is published to social networks
GST documents (guide)list_gst_documents / issue_gst_invoice, create_credit_noteread:orders / write:orders
Tax (guide)get_tax_settings, preview_tax / update_tax_settingsread:settings, read:products / write:settings (owner or admin)
Master datalist_/get_/create_/update_/delete_ × customer(s), supplier(s), location(s), category/categoriesread:* / write:*
Task-shaped (AI-5)find_product_stock, sales_summary / restock_low_items, receive_delivery, count_stockread:products, read:reports / write:purchase_orders, write:inventory
Approvals (AI-3)list_pending_actions, get_pending_actionread:settings

Tools carry MCP annotations (readOnlyHint, destructiveHint, idempotentHint) so clients can ask for confirmation before destructive calls. create_* tools for orders, purchase orders and transfers accept idempotencyKey for safe retries.

Business-rule failures (insufficient stock, invalid state, not found, missing scope) come back as tool results with isError: true and a structured { error: { code, message, details } }, so the model can explain or recover.

Connecting

With OAuth (recommended for people): add your business's address, https://<app>/api/mcp/<business-slug> (copy it from Settings → Connected apps), as a remote MCP server / custom connector in your client and sign in when prompted.

claude mcp add --transport http supplysynk https://<app>/api/mcp/<business-slug>

With an API key — Claude Code

claude mcp add --transport http supplysynk https://<your-workspace>/api/mcp \
  --header "Authorization: Bearer ssk_..."

Claude Desktop / other clients (mcp.json style):

{
  "mcpServers": {
    "supplysynk": {
      "type": "http",
      "url": "https://<your-workspace>/api/mcp",
      "headers": { "Authorization": "Bearer ssk_..." }
    }
  }
}

Raw HTTP

curl -s https://<your-workspace>/api/mcp \
  -H "Authorization: Bearer ssk_..." -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_reorder_suggestions","arguments":{}}}'

Security notes

  • Browser requests with a cross-site Origin are rejected (DNS-rebinding protection).
  • No session state is kept between requests; revoking a key takes effect on the next call.
  • Request bodies are capped at 1 MB and 50 JSON-RPC messages.
  • OAuth sign-in uses the platform host (/api/mcp or /api/mcp/<slug> on your main app URL); API keys also work on tenant subdomains and custom domains, and at a business address only for that business.
  • Not yet supported: Client ID Metadata Documents (AI-6; needs the @better-auth/cimd plugin, planned in a follow-up), server-initiated notifications, resources/prompts.