A remotely hosted MCP server that lets a frontline operations person diagnose stalled e-commerce orders and route the fix to the right team — without an engineer, and without the model ever deciding what's actually wrong. Diagnosis is deterministic TypeScript against a database snapshot; the model's job is to translate that diagnosis into a conversation and, where the action class allows it, into a routed escalation. All data is synthetic. Built as a paid take-home assessment.
Full design docs live in docs/: DATA_MODEL.md (entities and
invariants), BUILD_SPEC.md (implementation spec),
SYSTEM_DESIGN.md (the full design narrative),
DEMO_SCRIPT.md (rehearsed walkthrough with live-confirmed outputs),
AI_WORKLOG.md (how this was built, phase by phase).
- The model never determines correctness. The same order produces the same findings every call. There is no LLM call anywhere in the diagnosis path.
- Diagnosis never confers authority. Whether an action may execute is decided by its action class alone, independent of how well-evidenced the finding is. A conclusive diagnosis does not authorize a write.
https://commerce-ops-mcp-production-d2b0.up.railway.app
GET /health— public, no auth, reports{status, db, seeded_orders, version}.POST /mcp— the MCP endpoint (Streamable HTTP, stateless). RequiresAuthorization: Bearer <MCP_ACCESS_TOKEN>. The production token is not in this repo — retrieve it from wherever the person running this assessment shared it with you, or from the Railway project's variables if you have access.
npx @modelcontextprotocol/inspector@latest --cli \
https://commerce-ops-mcp-production-d2b0.up.railway.app/mcp \
--transport http \
--header "Authorization: Bearer $MCP_ACCESS_TOKEN" \
--method tools/listAdd to Claude Desktop's MCP config (Settings → Developer → Edit Config), substituting the real token:
{
"mcpServers": {
"commerce-ops": {
"url": "https://commerce-ops-mcp-production-d2b0.up.railway.app/mcp",
"headers": {
"Authorization": "Bearer <MCP_ACCESS_TOKEN>"
}
}
}
}Restart Claude Desktop after editing. If your Claude Desktop version's config format for remote HTTP servers differs from the above, its own MCP settings UI will show the field names it expects — the two pieces of information you need are the URL and the bearer header.
Requires Node 22+ and a local Postgres. Copy .env.example to .env and fill in a real
DATABASE_URL (the other four values can stay as generated placeholders for local dev, or
generate your own with openssl rand -hex 32).
npm install
npm run migrate && npm run seed # schema + deterministic 40-clean/10-dirty seed
npm run dev # http://localhost:3000curl localhost:3000/health should show db: ok and seeded_orders: 50. Run the test suite
with npm test (against whatever Postgres your local .env points at).
npm run test:isolatedOne command, no .env, no local Postgres, no manual steps. It brings up a throwaway Postgres
16 container (docker-compose.test.yml, port 5433, tmpfs storage so nothing touches disk and
nothing persists between runs, literal placeholder credentials), migrates it, seeds it, runs the
full suite against it, and tears the container down afterward whether the suite passed or
failed. This is the path a fresh clone or CI should use — it can't be pointed at the wrong
database because it never reads your .env at all; see scripts/test-isolated.sh. Requires
Docker running locally.
| Tool | Writes | Purpose |
|---|---|---|
list_open_exceptions |
No | Worklist of every order currently tripping a rule, bounded, severity/age-filterable |
find_order |
No | Look up an order by id, email, or phone |
diagnose_order |
No | Evidence-backed findings for one order, naming what can/can't be fixed and by whom |
preview_action |
No | Compute the exact diff one action would make for one finding, and mint a signed token for it |
apply_action |
Yes — the only one | Writes, only with a valid unexpired token bound to the exact diff previewed, idempotency-keyed |
get_audit_trail |
No | Every past write and review case for one order |
Intended chain: list_open_exceptions → diagnose_order → (if executable)
preview_action → apply_action → get_audit_trail. find_order substitutes for
list_open_exceptions when you already have a customer identifier instead of a worklist.
Whether an action may execute is decided by its class, never by how well-evidenced the finding is:
| Class | Executable | Covers |
|---|---|---|
EXTERNAL_EFFECT |
Never | Any call to a processor, carrier, or warehouse system |
FINANCIAL_STATE |
Never | Financial status, captures, voids, refunds, holds |
PHYSICAL_STATE |
Never | Stock allocation/reservation, fulfillment requeue |
POLICY_DECISION |
Never | Address correction, fraud clearance |
INTERNAL_RECORD |
Yes, gated by order value | Review cases, internal diagnosis notes |
Gate order is always class first, value second — a restricted class is refused at any
order value; the value threshold (POLICY_MAX_ORDER_VALUE_PAISE) only ever applies inside
INTERNAL_RECORD. Exactly two actions are executable: a_create_review_case and
a_attach_diagnosis. Every other resolving action — reconciling payment, reallocating stock,
requeuing fulfillment, correcting an address, clearing a fraud hold — escalates by design, no
matter how conclusive the diagnosis. The authority gate runs twice on every write: once at
preview_action, again at apply_action, because a token is never trusted alone.
Not built, on purpose: a frontend, authentication or user management, a complete commerce
backend, CI/CD, a product catalogue, returns or refunds, a webhook receiver, carrier
integration, multi-tenancy, or a review-case approval workflow (cases are created and routed;
nothing in this system works or resolves them). audit_log.actor and
review_cases.requested_by are caller-supplied strings by design — there is no auth layer to
derive identity from.
- No webhook event log. A missed fulfillment acknowledgement is inferred from the absence of a status transition past its tolerance window, not proven from a delivery record.
- Caller-supplied identity.
actorandrequested_byare trusted strings from the MCP caller, not an authenticated session — there is no user/auth layer in scope. - Review cases are terminal at
open. They're created and correctly routed; nothing in this system works, approves, or resolves them — that's a human process outside this system's boundary. - Inventory has no movement ledger. A discrepancy between
order_lines.allocated_qtyandinventoryis detected, but the system can't explain how it arose, only that it exists. - Seed timestamps are relative to
now, not fixed calendar dates. The tolerance-window rules (30 min / 4 h / 24 h) are evaluated against real elapsed time, so the seed data's "healthy" edge cases (an order seeded as "10 minutes past a capture, within the 30-minute window") will drift stale — and start reading as violations — if too much real time passes between seeding and use. Re-runnpm run seed(locally) orrailway ssh --service commerce-ops-mcp -- npm run seed(hosted) before a demo or test run if it's been more than a few hours since the last seed. This is inherent to testing tolerance-window logic against real time, not a bug. - Idempotency is check-then-act for a brand-new key under true simultaneous double-submission (the database's own unique constraint is the backstop for that narrow race, not independently load-tested here).
Each considered and rejected for a stated reason — recorded here so they aren't quietly reintroduced later:
- Materialised view for the worklist — staleness plus a refresh schedule, for a workload that needs neither.
- A separate exceptions database — findings are silences, not caught errors; there's no write-time moment to log one, and the existing read/write table split already marks where a replica would go.
- A reasoning agent inside the server — the client is already an agent; a second one doubles latency, splits retry ownership, and hides its reasoning from the operator.
- Agent-first diagnosis with a deterministic fallback — runs the slow, non-reproducible path first and the fast, testable one as backup. The correct order is rules first, model on the residue.
- A combined
fix_ordertool that diagnoses and writes in one call — collapses preview and apply, making confirmation cosmetic instead of real.