Two CSVs in. Cash decisions out.
stockcash is an MCP server for small Amazon FBA and Shopify / DTC sellers (roughly $10k–$250k/month, 100–1,500 SKUs) and for agencies running several stores. It reads your own inventory and order exports, runs deterministic arithmetic, and returns markdown or JSON. In local mode it makes zero network calls while analysing and stores its memory on your machine.
demo_report first. It needs no files and prints a full 32-SKU report so you can judge the output.Quickstart
- Install and build (Node 20+).
- Add the server to your MCP client.
- Call
validate_exportswith your two files. - Call
analyze_inventory, then decide what to mark down and reorder.
git clone <your-repo> stockcash-mcp
cd stockcash-mcp
npm install
npm run build
# no files needed:
npm run demo
Install
Hosted — one URL
Sign in, copy your API key, and add one endpoint. No build, no clone.
claude mcp add --transport http stockcash https://mcp.yourdomain.com/mcp \ --header "Authorization: Bearer sc_live_..."
JSON clients use the same URL with a header. Get the key from your dashboard. Hosted calls are metered against your account.
Claude web/mobile: add the same URL and approve the OAuth prompt after signing in. CLI clients can keep using the API key header.
Hosted tools cannot read paths on your computer. Send the CSV content as inventoryCsv / ordersCsv text; store profiles keep thresholds and snapshot history, not file paths.
Local — stdio
Claude Desktop / Cursor / most clients
{
"mcpServers": {
"stockcash": {
"command": "node",
"args": ["/absolute/path/to/stockcash-mcp/dist/index.js"],
"env": { "STOCKCASH_LICENSE": "sc_..." }
}
}
}
OpenCode
{
"mcp": {
"servers": {
"stockcash": {
"type": "local",
"command": ["node", "/absolute/path/to/stockcash-mcp/dist/index.js"]
}
}
}
}
HTTP mode
npm run build node dist/index.js --http --port 8787 # POST http://127.0.0.1:8787/mcp (stateless Streamable HTTP) # GET http://127.0.0.1:8787/health
The local build needs no key on the Free plan. Paste a license key only when you want snapshots, digest or more credits. The hosted URL is separate and always authenticated with an API key.
Tools & credits
| Tool | Credits | Free | Pro |
|---|---|---|---|
demo_report | 0 | ✓ | ✓ |
validate_exports | 0 | ✓ | ✓ |
store_profile | 0 | 1 store | ∞ |
license | 0 | ✓ | ✓ |
cash_summary | 1 | ✓ | ✓ |
trend_report | 1 | ✓ | ✓ |
snapshot | 1 | — | ✓ |
analyze_inventory | 2 | ✓ | ✓ |
dead_stock_report | 2 | ✓ | ✓ |
reorder_plan | 2 | ✓ | ✓ |
bundle_ideas | 2 | ✓ | ✓ |
weekly_digest | 3 | — | ✓ |
portfolio_digest | 5 | — | ✓ |
Only successful calls are charged. A failed call returns an error and costs nothing.
The recommended weekly loop
store_profile {action:"save", name:"cedar-clay", inventoryPath:"...", ordersPath:"..."}
snapshot {store:"cedar-clay"} # once, baseline
weekly_digest {store:"cedar-clay"} # every Monday
CSV format
Only four inventory columns and three order columns are strictly required. Header aliases are matched case- and punctuation-insensitively.
Inventory
| Field | Required | Accepted headers |
|---|---|---|
| SKU | yes | sku, seller_sku, MSKU, item_sku, ASIN, fnsku |
| On hand | yes | quantity, qty, on_hand, available, AFN Fulfillable Quantity |
| Inbound / on order | optional | inbound, in_transit, on_order, open_po, AFN Inbound |
| Unit cost | strong | unit_cost, cost, landed_cost, cogs, average_cost |
| Price | strong | price, sale_price, list_price, selling_price |
| Title / category / lead time | optional | title, product_name, category, lead_time_days |
Orders
| Field | Required | Accepted headers |
|---|---|---|
| Order date | yes | date, order_date, purchase_date, shipped_date |
| SKU | yes | same aliases as inventory |
| Units | yes | quantity, qty, units, units_sold, ordered_units |
| Revenue | optional | revenue, sales, item_total, order_total |
| Order id | optional | order_id, amazon_order_id, order_number |
How the math works
| Metric | Formula |
|---|---|
| Velocity | units sold in window ÷ windowDays |
| Days of cover | units on hand ÷ velocity (never if no sales) |
| Cash tied | units on hand × unit cost — money already spent, not a loss |
| Status | out · low ≤30d · healthy 30–120d · slow 120–180d · overstock still selling >180d · dead zero sales |
| Break-even discount | 1 − (unit cost ÷ price) |
| Reorder quantity | velocity × (lead time + targetCoverDays) − on hand − on order |
| Bundle capacity | min(dead units on hand, partner units sold in window) |
| Trend | window split in half: declining ≤−30% · rising ≥+30% · stopped · long-dead · new · stable |
“Today” is the newest order date in the export, so old files still produce sane reports. Pass referenceDate to pin it. A single typo date years in the future cannot drag the window forward when the file straddles the machine clock.
What it deliberately does not do
- No price-elasticity model: it shows the lift you need, not a prediction of units sold.
- No fees, shipping or storage beyond unit cost. Put them in your cost column for a landed-cost view.
- No forecasts from other sellers’ data. Every number comes from your rows.
Data safety rails
- Zero SKU matches prints a warning as the first insight instead of silently reporting everything as dead.
- Impossible dates (
2026-02-31, month 13, hour 25) are rejected rather than rolled into the next month. - Semicolon or tab exports get an explicit hint, not just “column not found”.
- European numbers (
1.234,56,12,5) parse correctly alongside US ones. - SKUs differing only by case are merged, with a note.
- Invalid thresholds are clamped into a consistent order so statuses cannot invert.
- Cash budget: pass
availableCash; rows that do not fit are marked deferred, never hidden.
License activation
A license is a signed key, not an account. The server carries a public key and verifies the signature locally. No billing API is contacted at runtime.
Or set it once in the MCP server environment:
// claude_desktop_config.json / opencode.json "env": { "STOCKCASH_LICENSE": "sc_..." }
What the key contains
- plan (
pro) - credit allowance for the period
- period start and hard expiry
- a customer/subscription id so credit ledgers never mix
Commands
# see plan, credits, limits license {} # activate license {"action":"activate","key":"sc_..."} # go back to free on this machine license {"action":"deactivate"}
Store the key in ~/.stockcash-mcp/license.json automatically when you activate through the tool, or in the environment when you prefer config-as-code.
Credits & limits
| Free | Pro | |
|---|---|---|
| Credits / period | 20 | 1,000 |
| SKUs | 50 | ∞ |
| Store profiles | 1 | ∞ |
| Snapshot / digest | — | ✓ |
| Portfolio | — | ✓ |
The ledger lives in ~/.stockcash-mcp/credits.json and is per license id. A new period or a newly activated key starts a fresh ledger. Top-ups raise the allowance for the current period only.
Privacy
- Inputs are files you download from your own store. Nothing is scraped.
- The server makes zero network calls — no API keys to leak.
- Profiles, snapshots and credit ledgers are plain JSON under
~/.stockcash-mcp/. Read, back up or delete them yourself. - No third-party accounts, no OAuth, no ToS surface, no regulated data.
Troubleshooting
“Required column sku not found”
Call validate_exports. It prints every header detected, which canonical column each maps to, and the first parsed rows. If the header row looks semicolon- or tab-separated you will get a specific hint.
Everything shows as dead
Usually the two files do not share an identifier (inventory uses SKU, orders use ASIN). The first insight warns when zero order rows matched. Check ordersWithoutInventory in the same report.
“Not enough credits”
Run license {} to see the balance and period end. Top up or wait for renewal. Failed calls are never charged.
“The Free plan does not include snapshot”
That is the paid boundary: state. Static analysis is free; remembering last week needs Pro. The error names the required feature.
A number looks wrong
Open the full report: it prints every formula and every assumption, plus parsing notes with row numbers. Cash tied is on-hand × unit cost; if your cost column includes fees, the number includes them.