inventory-mcp-toolkit
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@inventory-mcp-toolkitwhich items are low on stock and need reordering?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
inventory-mcp-toolkit
A small, realistic warehouse/inventory system exposed through the Model Context Protocol (MCP) — a server, a standalone client, and a test suite, all in TypeScript. I built this to actually learn MCP by implementing one end to end, rather than just reading the spec. If you're in the same boat, this repo is meant to be read top to bottom.
What is MCP, and why does it matter?
Large language models are good at reasoning over text but can't, by themselves, look anything up or do anything in the real world. MCP is an open protocol (from Anthropic) that standardizes how an LLM-powered application ("host") talks to external tools and data sources ("servers") — things like "search this database," "read this file," "create this ticket."
Before MCP, every AI app that wanted to integrate with, say, a database or a SaaS API wrote its own bespoke integration. MCP gives that integration a common shape: a server exposes tools (functions a model can call), resources (data it can read), and prompts (reusable instruction templates) over a standard JSON-RPC interface. Any MCP-compatible host (Claude Desktop, an IDE assistant, your own agent) can then talk to any MCP server without custom glue code. Write the integration once, use it everywhere.
This repo is that integration, for a warehouse inventory system:
Server (
src/server.ts) — exposes 5 tools, 1 resource, and 1 prompt over inventory data stored in SQLite.Client (
src/client.ts) — a minimal MCP host that spawns the server, lists its capabilities, and calls each one, so you can see the protocol work without installing Claude Desktop.
Related MCP server: AllOurThings MCP Server
Architecture
spawns as a child process, talks over
stdin/stdout using JSON-RPC 2.0
┌────────────────────┐ ───────────────────────────────▶ ┌──────────────────────┐
│ MCP Client/Host │ │ MCP Server │
│ │ ◀─────────────────────────────── │ (src/server.ts) │
│ - src/client.ts │ tool results, resource │ │
│ (this repo's demo) │ contents, prompt messages │ 5 tools │
│ - or: Claude Desktop │ │ 1 resource │
│ - or: any MCP host │ │ 1 prompt │
└────────────────────┘ └───────────┬───────────┘
│
│ better-sqlite-style
│ synchronous queries
▼
┌──────────────────────┐
│ SQLite (node:sqlite) │
│ data/inventory.db │
│ │
│ suppliers │
│ warehouses │
│ items │
│ stock_levels │
│ reorder_requests │
└──────────────────────┘Each tool handler (src/tools/*.ts) is a small, pure function —
(db, args) => data — with no MCP-specific code in it. src/server.ts
is the only place that imports the MCP SDK's server classes; it wraps each
handler so errors come back as proper MCP tool results. That split is what
makes the handlers unit-testable without spinning up any protocol
machinery at all (see tests/tools.test.ts), while tests/e2e.test.ts
exercises the real protocol stack in-process.
The data model
Five tables, seeded by npm run seed (src/data/seed.ts):
Table | What it holds |
| Who you buy a SKU from, and their typical lead time |
| Physical locations stock lives in |
| SKUs: name, category, cost, price, supplier |
| Quantity on hand per (item, warehouse), plus reorder rules |
| An append-only log of reorder requests created by the |
The seed data: 5 suppliers, 4 warehouses, 20 SKUs across 5 categories
(Fasteners, Electronics, Packaging, Hand Tools, Safety), and 40
item/warehouse stock rows — deliberately a mix of healthy and low-stock
lines so list_low_stock_items has something real to show you.
Setup
Requires Node.js 22.5 or newer (for the built-in node:sqlite module —
see Design decisions below). No native build tools,
no Docker, no external database required.
git clone <this repo>
cd inventory-mcp-toolkit
npm install
npm run seed # creates data/inventory.db and fills it with demo dataRun the server + client together
The easiest way to see everything work is the included client, which spawns the server itself:
npm run dev:clientThis prints the list of available tools, reads the live inventory resource, calls every tool with example arguments, and prints the responses — including a deliberate "SKU not found" call so you can see error handling in action.
Run the server by itself
npm run dev:serverIt will sit there connected over stdio, waiting for a client (this is normal — it's not a web server, there's nothing to curl). Press Ctrl+C to stop it.
Build and run the compiled version
npm run build
npm run start:server # node dist/server.js
npm run start:client # node dist/client.jsRun the tests
npm testThe tools
All five tools validate their input with Zod schemas. If you pass bad arguments, the MCP SDK rejects the call before the handler ever runs — you get a structured validation error back, not a stack trace.
search_inventory
Search by item name or SKU substring, with an optional category filter.
Input:
{ "query": "cable", "limit": 5 }Output:
[
{
"sku": "EL-2001",
"name": "USB-C Cable 1m",
"category": "Electronics",
"unit_price": 6.99,
"total_quantity_on_hand": 258
}
]get_item_by_sku
Full detail for one exact SKU: pricing, supplier, and stock at every warehouse that carries it.
Input: { "sku": "EL-2001" }
Output:
{
"sku": "EL-2001",
"name": "USB-C Cable 1m",
"unit_cost": 1.5,
"unit_price": 6.99,
"supplier_name": "Cascade Electronics Supply",
"stock": [
{ "warehouse_code": "WH-EAST", "quantity_on_hand": 240, "reorder_threshold": 100, "reorder_quantity": 500 },
{ "warehouse_code": "WH-WEST", "quantity_on_hand": 18, "reorder_threshold": 100, "reorder_quantity": 500 }
]
}Call it with a SKU that doesn't exist and you get back a clean error
result (isError: true, message No item found with SKU "...") instead
of an exception.
list_low_stock_items
Every (item, warehouse) line currently below its reorder threshold,
ranked by how urgent the deficit is. Optional warehouse_code and
category filters.
Input: { "warehouse_code": "WH-WEST" }
Output: an array of entries like:
{
"sku": "EL-2003",
"item_name": "Micro Relay 12V",
"warehouse_code": "WH-WEST",
"quantity_on_hand": 12,
"reorder_threshold": 40,
"supplier_name": "Cascade Electronics Supply",
"lead_time_days": 10
}get_warehouse_summary
Roll-up stats for one warehouse, or all of them if warehouse_code is
omitted: distinct SKU count, total units on hand, total inventory value
at cost, and how many lines are low on stock.
Input: {}
Output:
[
{ "warehouse_code": "WH-EAST", "distinct_skus": 11, "total_units_on_hand": 14970, "total_inventory_value": 5779, "low_stock_count": 0 },
{ "warehouse_code": "WH-WEST", "distinct_skus": 9, "total_units_on_hand": 708, "total_inventory_value": 1072.5, "low_stock_count": 8 }
]create_reorder_request
The one write tool. Inserts a row into reorder_requests. If quantity
is omitted, it defaults to that stock line's configured
reorder_quantity, so a caller doesn't need to already know that number.
Input:
{ "sku": "SF-5003", "warehouse_code": "WH-WEST", "note": "running low before the holiday rush" }Output:
{
"id": 1,
"sku": "SF-5003",
"warehouse_code": "WH-WEST",
"quantity_requested": 150,
"status": "pending",
"requested_at": "2026-10-01 13:28:46",
"note": "running low before the holiday rush"
}The resource: inventory://snapshot
A live, point-in-time JSON view of every item's stock across every
warehouse, with a low_stock_count. Resources exist for data a client
wants to read directly — grounding context — without the overhead or
semantics of a tool call. It's computed fresh from the database on every
read, never cached, since a stale inventory snapshot would be actively
misleading.
The prompt: reorder_briefing
A reusable instruction template (optionally scoped to one warehouse) that
tells a model: check list_low_stock_items, check get_warehouse_summary
for context, rank by urgency (factoring in supplier lead time), and
propose — but don't yet execute — reorder requests. This is the MCP
pattern for encoding a workflow, as opposed to a single tool call.
Connecting it to Claude Desktop
Build the project (
npm run build) sodist/server.jsexists, and runnpm run seedat least once.Open Claude Desktop's config file:
macOS:
~/Library/Application Support/Claude/claude_desktop_config.jsonWindows:
%APPDATA%\Claude\claude_desktop_config.json
Add this server under
mcpServers(adjust the path to where you cloned this repo):
{
"mcpServers": {
"inventory-mcp-toolkit": {
"command": "node",
"args": ["C:\\path\\to\\inventory-mcp-toolkit\\dist\\server.js"]
}
}
}Restart Claude Desktop. You should see the hammer/tools icon show the five inventory tools, and you can ask things like "what's low on stock in WH-WEST?" or "give me a reorder briefing for the Dallas warehouse."
Design decisions
A few choices worth explaining rather than leaving implicit:
node:sqliteinstead ofbetter-sqlite3. Node 22.5+ ships a synchronous SQLite driver in core. Using it meansnpm installnever needs a native build toolchain (Python, a C++ compiler) on the contributor's machine — a common source of "it doesn't work on my machine" for SQLite-based Node projects. The tradeoff is a smaller, newer API surface, but everything this project needs (prepared statements, basic pragmas) is there.stdio transport, not HTTP. MCP supports both. stdio is the right choice for a local tool a host spawns and owns the lifecycle of — the classic Claude Desktop pattern. HTTP/SSE transports exist in the SDK for when a server needs to run remotely and serve multiple clients; this project doesn't need that.
Zod schemas on every tool input. The MCP SDK uses them to both generate the JSON Schema clients see and validate incoming arguments before your handler runs. That means a handler can trust its arguments are well-formed and focus on business logic — no manual
if (typeof x !== "string")checks scattered through the codebase.Pure handler functions, separate from the MCP wiring. Every tool's actual logic is a plain function of
(db, args) -> data, exported separately from its Zod shape.server.tsis the only file that touches the MCP SDK's server classes. This is what makestests/tools.test.tspossible without any protocol overhead.Errors as data, not exceptions, at the tool boundary. A handler throws a typed
NotFoundErrorfor an anticipated failure (unknown SKU, unknown warehouse);server.tscatches it and returns a cleanisError: trueresult. Anything unexpected is logged to stderr (never stdout — that's the protocol channel) and reported generically, so internal details never leak into a tool result.
How I'd extend this
Pagination on
search_inventoryandlist_low_stock_items— fine for 20 SKUs, not for 20,000.A
supplierresource template (inventory://supplier/{id}) so a client can look up supplier detail without a dedicated tool.An
update_stock_leveltool to simulate receiving a shipment or recording a sale, with the obvious transactional care around not letting stock go negative.Idempotency keys on
create_reorder_requestso a retried call from a flaky client doesn't double-create a request.A real event log — right now
reorder_requests.statusonly ever becomes"pending"; amark_reorder_fulfilledtool and a status history table would make this closer to a real purchasing workflow.Streamable HTTP transport as a second entry point, if this ever needed to be shared across multiple remote clients instead of spawned locally per host.
Project layout
src/
data/
schema.ts CREATE TABLE statements
seed.ts demo data loader (npm run seed)
tools/ one file per MCP tool, pure (db, args) -> data
resources/ inventory snapshot resource
prompts/ reorder_briefing prompt template
db.ts SQLite connection helper
errors.ts NotFoundError / ConflictError
toolResult.ts ok()/toolError() MCP result helpers
server.ts McpServer wiring + stdio entry point
client.ts standalone demo client
tests/
testDb.ts small, stable in-memory fixture
tools.test.ts unit tests against the pure handler functions
e2e.test.ts real client <-> server over an in-memory transportLicense
MIT — see LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
Search multi-merchant supply, checkout, and track orders via MCP.
Search inventory items and folders, low-stock alerts, jobs and purchase orders, and update stock.
Safety-stock & reorder planning, FBA-vs-FBM decision, and inventory health audit for sellers
Connects Amazon Seller Central and Amazon Advertising to any MCP client. Settlement-accurate P&L - every fee, refund and reimbursement as Amazon posted it - plus contribution margin and breakeven per product, per marketplace, per day. Full Sponsored Products, Brands and Display management: search terms, placements, keyword and competitor research, dayparting, automation rules. 110 tools: 78 read-only (P&L, PPC at every grain, SQP/Brand Analytics, inventory, forecasting), 28 staged writes (every change is a proposal you review and confirm before it reaches Amazon - there is no autonomous-write mode), and 4 confirmation & support tools. 4 additional AI Workforce tools appear once a client is connected.
Related MCP Servers
FlicenseNot gradedqualityCmaintenanceEnables interaction with ScanPower's inventory and shipment management system through a secure MCP interface. Supports retrieving inventory data, creating shipment plans, and managing logistics operations through natural language.2-- AlicenseNot gradedqualityCmaintenanceEnables cataloging and managing personal inventory (items, attachments) through natural language, allowing users to add, search, update, and retrieve item details and attachments via MCP tools.1MIT
- FlicenseNot gradedqualityDmaintenanceAn AI-powered inventory management system with a natural language interface, enabling CRUD operations on items and suppliers, stock transfers, and supplier management via MCP tools.-
- AlicenseNot gradedqualityBmaintenanceUnifies inventory data from four sources (Jikeyun, Supor factory, WeChat Excel, RPA) with timestamps, and exposes MCP tools for AI agents to query stock levels.8 npmISC