Developers · MCP

Connect Claude — or any MCP client — to your factory

QuarterMaster ships a hosted MCP server at https://api.quarter-master.co/mcp. Every request is authenticated as you — a personal API key for header-based clients (Claude Code, the Claude API), or a per-user OAuth sign-in for connector surfaces (claude.ai, Grok) — so every answer is scoped to your org and every action is attributed to you. Reads are grounded in the live ledger; the one write tool drafts a PO that always lands pending human approval.

1 · Create your personal API key

Any member can mint a personal key from a signed-in session. Personal keys act as you: their scopes are clamped to your current role at every request, and they can never carry admin or approve:purchase_orders — an agent can draft, only a human can approve. Revoke anytime with DELETE /v1/api-keys/{id}; keys die automatically if you leave the org.

terminal — mint a personal key (shown once)
curl -X POST https://api.quarter-master.co/v1/api-keys \
  -H "Authorization: Bearer $QM_JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Claude on my laptop",
    "personal": true,
    "scopes": ["read:inventory", "read:costing", "write:purchase_orders"]
  }'

# → { "key": "qm_live_1a2b3c4d_…", … }   ← save it; it is never shown again
export QM_MCP_KEY="qm_live_1a2b3c4d_…"

Just exploring? POST /v1/guest/session provisions a throwaway demo factory and returns a ready-made mcpApiKey for it — no account needed.

2 · Connect Claude Code

terminal — add the server
claude mcp add --transport http quartermaster https://api.quarter-master.co/mcp \
  --header "Authorization: Bearer ${QM_MCP_KEY}"

For a shared repo, commit a .mcp.json that references the env var — each teammate exports their own key and gets their own attribution:

.mcp.json — project scope, per-user credentials
{
  "mcpServers": {
    "quartermaster": {
      "type": "http",
      "url": "https://api.quarter-master.co/mcp",
      "headers": { "Authorization": "Bearer ${QM_MCP_KEY}" }
    }
  }
}

3 · Connect claude.ai or Grok — OAuth, no keys to paste

Connector surfaces can't carry a per-user header, so the server is also a full OAuth 2.1 authorization server (PKCE S256, dynamic client registration, rotating refresh tokens). Add the connector, and your browser opens a QuarterMaster consent screen: sign in, pick the org and the scopes the agent gets, done. Each teammate connects as themselves.

claude.ai / Claude Desktop — Settings → Connectors → Add custom connector → paste https://api.quarter-master.co/mcp. Claude discovers the OAuth endpoints itself and walks you through the consent screen.

Grok — grok.com/connectors → New ConnectorCustom → paste https://api.quarter-master.co/mcp and complete the sign-in when prompted.

Grants clamp to your role on every request (a demotion applies immediately), can never carry admin or approve:purchase_orders, and die when you disconnect the connector, leave the org, or delete your account.

4 · Other clients

Claude API — pass the server in mcp_servers with authorization_tokenset to the user's key; your backend supplies each user's own token per request.

Claude Desktop (local stdio) — run the server as a local process against your own deployment; the key comes from the environment:

claude_desktop_config.json
{
  "mcpServers": {
    "quartermaster": {
      "command": "uv",
      "args": ["run", "python", "-m", "qm_api.mcp_server"],
      "env": {
        "QM_MCP_TRANSPORT": "stdio",
        "QM_MCP_API_KEY": "qm_live_…",
        "QM_DATABASE_URL": "postgresql+asyncpg://…"
      }
    }
  }
}

Run it locally

terminal — local dev
cd apps/api && uv sync
uv run uvicorn qm_api.main:app --reload        # API + MCP on :8000

# demo key in one line (jq):
curl -s -X POST localhost:8000/v1/guest/session \
  -H 'Content-Type: application/json' -d '{"deviceId":"my-laptop-01"}' | jq -r .mcpApiKey

claude mcp add --transport http qm-local http://localhost:8000/mcp \
  --header "Authorization: Bearer ${QM_MCP_KEY}"
Reference

The tools

57 tools over the same service layer the console runs on — ask qm_capabilities for the live catalog. Money fields (costs, prices, PO totals) additionally require the read:costing scope — without it the same tools answer with amounts withheld. Write tools only ever produce drafts or role-scoped, audited, reversible changes — PO approval, SOP release, ledger counts and deletions stay human acts inside QuarterMaster.

Entry points

qm_capabilitiesThe live tool catalog: what each tool answers and whether your credential can call itread:inventory
qm_dashboardThe console pipeline at a glance: verify / approve / receive / cut / gapsread:inventory
qm_searchGlobal fuzzy search: items, locations, POs, builds, suppliersread:inventory

Inventory & stock

qm_find_itemFind items by name, internal PN, or MPN (fuzzy)read:inventory
qm_stock_lookupOn-hand, on-order and per-location stock for an itemread:inventory
qm_item_detailsThe item 360: stock, locations, suppliers, reorder policy, where-used, blocked buildsread:inventory
qm_item_historyRecent inventory-ledger transactions for one item (the append-only audit trail)read:inventory
qm_recent_activityThe org-wide ledger tail: everything that moved lately, by whom, from which sourceread:inventory
qm_low_stockItems at or below their reorder pointread:inventory
qm_locationsEvery stock location (with ids) with distinct items and total units heldread:inventory
qm_create_locationsCreate stock locations, whole nested layouts in one call (dup-guarded; custody kinds refuse)write:items
qm_update_locationRename, re-kind or re-parent a location (custody roots rename-only; cycles refuse)write:items
qm_lotsTracked lots: code, part, status, on-hand qty, dims, received dateread:inventory
qm_inventory_valueOn-hand inventory value at moving-average cost, total and by item typeread:costing

Building

qm_bom_linesList every BOM, or one assembly's full released parts list with per-line stockread:inventory
qm_get_buildableHow many units are buildable now, and which parts limit the next unitsread:inventory
qm_build_statusActive builds; per-build kit % and blocking shortagesread:inventory
qm_bom_costLive per-unit cost roll-up for a released BOM revisionread:costing
qm_eco_impact_previewWhat swapping a part costs in scrap and delayread:costing

Purchasing

qm_po_statusPOs with full line items; targeted queries include receipt historyread:inventory
qm_track_inboundOpen inbound shipments with carrier, tracking, ETAread:inventory
qm_replenishment_queueOpen replenishment cases: shortage, status, drafted POread:inventory
qm_suppliersSupplier directory with terms and items suppliedread:inventory
qm_supplier_threadsRFQ / outreach email threads with suppliers, with latest-reply snippetread:costing
qm_draft_poDraft a purchase order — lands pending human approval, never auto-issueswrite · personal key requiredwrite:purchase_orders

Conversion & cutting

requires the conversions module (on by default)
qm_get_cut_optionsMake-vs-buy and producible-from-stock for a cut partread:costing
qm_cut_queueCutting work orders in flight: plan, status, location, input lotread:inventory
qm_remnantsOn-hand remnant offcuts: dims, qty, age, locationread:inventory
qm_trace_lotLot genealogy: where a lot came from and what was cut from itread:inventory
qm_mass_balanceConversion mass balance for a period: yield %, scrap, ghostread:costing

SOPs

requires the SOP module (on by default)
qm_sop_templatesReleased executable SOP templates with kind and step countread:inventory
qm_sop_stepsOne SOP's steps (released, or the newest draft when nothing is released — labelled) plus the planning I/O of the revision an I/O edit would targetread:inventory
qm_sop_executionsSOP runs: progress, holds, deviations; per-step traveler detail for one runread:inventory
qm_sop_draft_stepsWrite a lint-validated step list onto a DRAFT SOP revision — releasing it stays a human actwrite · personal key requiredwrite:sop_templates
qm_set_sop_ioReplace a DRAFT revision's declared planning inputs/outputs (refused on revisions whose material steps compute them)write · personal key requiredwrite:sop_templates

Item master

qm_set_item_kindFlip an exactly-named item between buy (purchased) and make (manufactured); audited, lot tracking auto-enableswrite · personal key requiredwrite:items
qm_update_item_procurementEdit a part's capability set (canBuy/canMake/canSubcontract), its default procurement, and design authority — same coherence rules as the consolewrite · personal key requiredwrite:items
qm_item_packagesThe sealed package formats a part ships in — content qty, tare weight, barcoderead:inventory
qm_create_item_packageDeclare one package format for a part ("25 m roll", "reel/5000"); barcodes stay unique org-widewrite · personal key requiredwrite:items
qm_update_item_packageCorrect one package format, or remove it (delete=true) — a format holds no stock of its ownwrite · personal key requiredwrite:items

The security model, in six lines

· Every request re-verifies the credential: revocation, membership and role are checked live.
· Personal keys and OAuth grants are clamped to your current role — demotion applies on the next request.
· OAuth tokens are short-lived and rotate on refresh; a replayed refresh token revokes the whole grant family.
· Writes require a personal key and always land pending approval — agents draft, humans confirm.
· Every mutation carries the actor and an idempotency key; retries never duplicate.
· No key, no answer: unauthenticated calls get 401 with WWW-Authenticate, and requests are rate-limited per IP and per key.