darian.lagman
WORK / 01

Stockman / En Login

Shipped

A full-stack inventory + decision-support SaaS with an LLM/MCP assistant layer. Inventory CRUD, sellables/packages, purchase orders, vendor logic, dashboards, CSV import/export, and an assistant that calls the same APIs the UI does.

Role
Sole engineer & designer
Timeline
2024 – present
Status
Shipped
Stack
ReactSpring BootMySQLDockerMCPTypeScript
Stockman / En Login hero

Problem

A small distributor I knew was running a real business out of a stack of CSV files: a spreadsheet for inventory, another for purchase orders, exports from Shopify for sales, and a kit-of-parts breakdown that lived in someone's head. It worked at a few hundred SKUs. It stopped working at a few thousand.

Stockman replaces the spreadsheet stack with a single workspace: inventory CRUD, sellables and kit/package modeling, purchase orders, vendor logic, dashboards, CSV import/export with diff preview, and an LLM assistant that can answer "what should I reorder before Tuesday?" through an MCP layer over the same APIs the UI uses.

Architecture

Diagram
Stockman runtime path
React UI
Spring API
MySQL
Assistant
Forecasts
RBAC gate

The assistant routes through the same API and role checks as the UI.

[ React SPA ] ──HTTPS──▶ [ Spring Boot API ]
                              │  ├──▶ [ MySQL ]
                              │  ├──▶ [ Redis (sessions) ]
                              │  └──▶ [ MCP Server ] ──▶ [ Claude / OpenAI ]
                              ▲
                              └─ session-based auth · RBAC

A few decisions worth defending:

  • Sessions over JWT. Back-office app, no mobile clients, server-side revocation matters more than statelessness.
  • Spring Boot over Node. Strong typing, mature data-access, and team familiarity outweigh "JS everywhere."
  • MCP over a custom function-calling shim. Separates assistant capability from API authn/authz; same backend, two interfaces.
  • Docker Compose for dev parity. One docker compose up boots MySQL, Redis, the API, and the MCP server.

Features

Inventory CRUD with sellables/packages

Kits, bundles, and parent-child relationships modeled as a graph, not a flat list. The UI shows the graph; the API enforces invariants.

CSV import/export with diff preview

Never blindly upsert; show the user the changeset before commit. A single bad column shouldn't quietly rewrite live data.

Purchase orders + vendor logic

Vendors-per-product, lead-time-aware suggested orders, multi-vendor SKUs without duplication.

Dashboards & analytics

Turnover, dead stock, margin by SKU. The numbers come from the same store the assistant queries.

LLM/MCP assistant layer

The assistant calls the same /api/v1/inventory/* endpoints with role-scoped permissions. It can never do something the user can't.

Forecasting integration

Pulls demand forecasts from the engine in the next case study to power reorder suggestions.

Deep dive — the MCP layer

The assistant doesn't get its own database connection. It gets tools that proxy to the same Spring Boot endpoints, with the user's session attached. A representative tool:

// tools/stockman.ts
export const reorder_recommendations = tool({
  name: "reorder_recommendations",
  description:
    "Returns SKUs that should be reordered based on stock, lead time, " +
    "and demand forecast. Permission-scoped to the caller's role.",
  inputSchema: z.object({
    horizon_days: z.number().int().min(1).max(180).default(30),
    vendor_id: z.string().uuid().optional(),
  }),
  handler: async (input, ctx) => {
    const session = ctx.session; // forwarded from the MCP client
    return api.fetch("/api/v1/recommendations/reorder", {
      session,
      query: input,
    });
  },
});

What I'd do differently

  • I'd start with Postgres. MySQL was fine; nothing about Stockman needs it specifically.
  • I'd commit to MCP from day one rather than retrofitting it; the early "send the whole DB schema in a system prompt" experiments were a dead end.
  • The forecasting engine should have been a separate service from week one, not week ten.
Outcomes
~3,000
SKUs handled
across design partners
<120ms
p50 list latency
Spring Boot + MySQL
8
Deployed envs
dev · staging · per-tenant
2
Design partners
pre-launch