Skip to main content
Glama
Sr-Stark08

patagon-inventory-mcp

by Sr-Stark08

patagon-inventory-mcp

CI TypeScript MCP License: MIT

An MCP (Model Context Protocol) server that lets an AI agent manage inventory through natural language — check stock, register entries and exits, and flag low-stock items — without ever inventing a number.

It is a small, open-source extraction of patterns I use in production at Patagon, a B2B platform where AI agents run day-to-day operations for SMBs over WhatsApp. It uses sample data only.

"Se gastaron 10 cascos en la faena norte" → the agent calls register_movement → stock goes 38 → 28, and if the same WhatsApp message is delivered twice, it is still counted once.


Why this exists

When an LLM manages real inventory, three things go wrong:

Problem

How this server handles it

The model hallucinates stock figures

Every number comes from a tool. Tool descriptions and the stock_report prompt tell the model to never estimate.

Messages get retried or duplicated (webhooks, flaky networks)

register_movement is idempotent: same idempotency_key → same result, stock unchanged. Reusing a key for a different movement is rejected.

The model claims success when something failed

Invalid operations return an explicit MCP tool error (isError: true) with a code the model can reason about: NOT_FOUND, INSUFFICIENT_STOCK, INVALID_QUANTITY, KEY_CONFLICT.

Related MCP server: Korral StoreLink MCP

Architecture

flowchart LR
    U[User on WhatsApp / chat] --> H[MCP host<br/>Claude Desktop, Claude Code,<br/>or a custom agent]
    H <-->|MCP over stdio| S[patagon-inventory-mcp]
    S --> T[Tools]
    S --> R[Resources]
    S --> P[Prompts]
    T & R --> D[(InventoryStore<br/>domain logic)]

The domain logic (src/inventory.ts) knows nothing about MCP, so it can be unit-tested in isolation and reused behind any transport. src/server.ts is a thin MCP layer on top of it.

What it exposes

Tools (model-controlled)

Tool

Description

list_products

List products, optionally filtered by SKU or name

get_stock

Current stock of one product, with status ok / low / out

register_movement

Register an in or out movement — idempotent via idempotency_key

list_low_stock

Products below their minimum, with units missing

get_movement_history

Recent movements, newest first

Tools carry MCP annotations (readOnlyHint, idempotentHint) so hosts can decide which calls need user confirmation.

Resources (application-controlled)

  • inventory://products — full catalog snapshot

  • inventory://products/{sku} — resource template with listing and SKU autocompletion

Prompts (user-controlled)

  • stock_report — a reusable, tested instruction for a short team report, grounded in tool data

Quick start

Requires Node.js 22+.

git clone https://github.com/Sr-Stark08/patagon-inventory-mcp.git
cd patagon-inventory-mcp
npm install
npm run build

Try it in the MCP Inspector

npm run inspect

Use it from Claude Desktop

Add this to your claude_desktop_config.json:

{
  "mcpServers": {
    "patagon-inventory": {
      "command": "node",
      "args": ["/absolute/path/to/patagon-inventory-mcp/dist/index.js"]
    }
  }
}

Use it from Claude Code

claude mcp add patagon-inventory -- node /absolute/path/to/patagon-inventory-mcp/dist/index.js

Run with Docker

docker build -t patagon-inventory-mcp .
docker run -i --rm patagon-inventory-mcp

Tests

npm test
  • Unit tests for the domain rules: idempotency, key conflicts, no negative stock, input validation, minimum-stock crossing.

  • End-to-end tests that connect a real MCP Client to the server over an in-memory transport and exercise tools, resource templates and prompts through the protocol.

CI (GitHub Actions) runs type-checking, tests and the build on Node 22 and 24, then builds the Docker image and smoke-tests it over stdio.

Project structure

src/
  inventory.ts   # domain logic: products, movements, idempotency, validation
  server.ts      # MCP layer: tools, resources, prompts
  index.ts       # stdio entry point
tests/
  inventory.test.ts
  server.test.ts

Limitations and next steps

  • Data is in memory with sample products; a restart resets it. The store is isolated so it can be swapped for PostgreSQL or SQLite without touching the MCP layer.

  • Only the stdio transport is wired up. A Streamable HTTP entry point would enable remote deployments.

  • No authentication — intended for local use by an MCP host.

Author

Jorge Fraile Pereira — AI Agent Developer, founder of Patagon. Anthropic Academy: Building with the Claude API · Model Context Protocol (Intro & Advanced) · Claude Code in Action.

License: MIT

Available Tools

5 tools
get_movement_historyMovement historyA
Read-only

Most recent stock movements, newest first. Optionally filter by SKU.

ParametersJSON Schema
NameRequiredDescriptionDefault
skuNoFilter by product SKU
limitNoMax movements to return (default 20)

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

readOnlyHint=true already tells the agent this is a safe read, lowering the disclosure burden. The description adds the sort order (newest first) and that SKU filtering is optional, both genuinely useful traits beyond the annotations, but says nothing about pagination behavior or result size beyond what the schema's limit already encodes.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences with zero filler, and the core behavior (most recent movements, newest first) is front-loaded ahead of the optional filter detail.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple read-only, two-parameter list tool with full schema coverage and no output schema, the description covers purpose, ordering, and the optional filter adequately. Only a brief note on result volume or truncation behavior would make it fully complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so both parameters (sku, limit) are already documented in the schema, and the baseline is 3. The description's "Optionally filter by SKU" only restates the schema rather than adding syntax, format, or edge-case meaning.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ("Most recent stock movements") plus the ordering ("newest first"), which an agent can distinguish from get_stock or list_low_stock. It does not explicitly name a sibling alternative, but the scope is unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

"Optionally filter by SKU" implies that unfiltered retrieval is the default path and filtering is the narrow case, which is reasonable usage guidance. There is no explicit statement of when to prefer this over get_stock or list_low_stock, so guidance remains implied rather than stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_stockGet stockA
Read-only

Get the current stock of one product by SKU. Always use this before telling a user how much stock there is — never guess.

ParametersJSON Schema
NameRequiredDescriptionDefault
skuYesProduct SKU, e.g. EPP-CASCO

TDQS

A3.7/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The readOnlyHint=true annotation already tells the agent this is a safe, non-mutating read, so the description's job is lighter. It adds the useful behavioral rule that stock must be fetched rather than inferred, but says nothing about what happens for an unknown SKU or what the returned value looks like.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences, both front-loaded: the capability first, then the operating rule. Every clause earns its place with no filler or repetition.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, so the description is the only source for return-value expectations, and it leaves the response shape (a raw quantity vs. a stock object) and missing-SKU behavior unspecified. For a single-parameter read tool this is adequate but not complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and there is only one parameter, so the schema already carries the semantics fully (SKU with an example, EPP-CASCO). The description only restates 'by SKU' and adds no format or lookup nuance, making the baseline 3 appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource ('Get the current stock') and narrows scope to 'one product by SKU', which cleanly separates it from sibling listing tools like list_products and list_low_stock. It does not name those siblings explicitly, so it falls just short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

'Always use this before telling a user how much stock there is — never guess' gives an explicit when-to-use trigger plus a prohibition, which is stronger than typical usage guidance. It does not, however, mention alternatives (e.g. use list_low_stock when you need many products at once), so it stops short of 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_low_stockList low stockA
Read-only

List products whose stock is below their configured minimum.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A3.7/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

readOnlyHint=true already declares the safety profile, so the description only needs to add context. It contributes the notion of a per-product configured minimum (the threshold semantics), but says nothing about ordering, pagination, limits, or how products without a configured minimum are treated.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single sentence with zero filler, front-loading the verb and the narrowing condition. Nothing to trim.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter, read-only list tool with no output schema and annotations covering safety, the description is sufficient to invoke correctly. It only misses edge-case behavior such as products with no configured minimum or result ordering.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so the baseline is 4. There is no argument syntax the description could clarify, and it correctly avoids inventing any.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb ('List') plus resource ('products') and a precise filter criterion ('stock below their configured minimum'), which cleanly separates it from the sibling list_products. It does not explicitly name the sibling, so it stops short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The threshold criterion strongly implies the use case (surfacing items needing restocking), but the description never states when to prefer this over list_products/get_stock or any exclusions. Usage is implied rather than guided.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_productsList productsA
Read-only

List inventory products, optionally filtered by a text query that matches SKU or name.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryNoText to search in SKU or product name

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

readOnlyHint=true already establishes this as a safe read, so the description's burden is lower. It usefully adds that the query matches SKU or name, but says nothing about result size, ordering, or pagination behavior, which matters for a listing tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One sentence, front-loaded with the core action and the optional modifier trailing it. Every clause earns its place with no padding.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple single-parameter read tool with readOnlyHint and no output schema, the description covers what is needed to invoke it correctly. The only real gap is sibling disambiguation, which is minor given how little this tool needs to explain.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and the single parameter is fully documented in the schema, so the baseline is 3. The description's 'matches SKU or name' largely restates the schema's own parameter description rather than extending it.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb (List) and resource (inventory products) with the filtering scope, so the operation is unambiguous. It does not, however, distinguish itself from the sibling list_low_stock, which is also a product-listing tool, so an agent gets no help choosing between the two from the text alone.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It implies when the tool applies by noting the filter is optional, which tells the agent it can call this with no arguments for a full listing. It names no alternatives and gives no explicit when-to-use versus get_stock or list_low_stock guidance, so usage is only implied.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

register_movementRegister stock movementA
Idempotent

Register a stock entry (in) or exit (out). Idempotent: send the same idempotency_key when retrying so the movement is never counted twice.

ParametersJSON Schema
NameRequiredDescriptionDefault
skuYesProduct SKU
noteNoOptional context, e.g. 'north site crew'
typeYes'in' adds stock, 'out' removes stock
quantityYesWhole number of units
idempotency_keyYesUnique key for this real-world movement, e.g. the chat message ID. Reuse it only for retries.

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare idempotentHint=true and destructiveHint=false, so the safety and dedup profile is known without the description. The description does add a practical detail – reuse the key only for retries, not for new movements – which is genuinely useful context beyond the annotation. It says nothing about what happens on a rejected/duplicate call or any authorization requirements.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences, no filler. The core purpose is front-loaded and the idempotency caveat follows immediately, which is exactly the right ordering for a mutation tool.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Covers purpose, direction, and the idempotency contract for a 5-parameter write tool whose schema is fully documented and whose annotations carry the safety profile. With no output schema, return values needn't be explained, but the description is silent on failure/duplicate-key behavior and on whether 'out' quantities are validated.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, including an explicit explanation of idempotency_key and the in/out enum, so the schema already carries the parameter meaning. The description restates the in/out semantics and key reuse rather than adding new syntax or constraints, so the baseline of 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (register) and resource (stock movement) plus the two directional modes (in/out), which maps directly onto the 'type' enum. It doesn't name or contrast with the sibling read tools (get_stock, get_movement_history), but the write-vs-read distinction is obvious from the verb.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides real guidance for the retry case ('send the same idempotency_key when retrying'), which is the main usage pitfall for a movement-recording call. It says nothing about when to prefer this tool over siblings or any preconditions such as stock availability for 'out'.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 5 tool updatesv1.0.0
    • First observedget_movement_history
    • First observedget_stock
    • First observedlist_low_stock
    • First observedlist_products
    • First observedregister_movement

TDQS

A4/5.0

Scored across 5 tools

Disambiguation5/5

Each tool has a distinct purpose: list_products (catalog browsing), get_stock (single SKU stock), list_low_stock (filtered subset), register_movement (writes), and get_movement_history (audit trail). The overlap between list_products and list_low_stock is clearly differentiated by the minimum-threshold filter, and get_stock is explicitly scoped to a single SKU.

Naming Consistency5/5

All five tools follow a uniform verb_noun snake_case pattern (list_products, get_stock, register_movement, list_low_stock, get_movement_history). Verbs are meaningful and consistent across the set.

Tool Count5/5

Five tools is well-scoped for a focused inventory server, and each one covers a distinct read or write operation without redundancy. Nothing feels padded or missing at the count level.

Completeness4/5

Core inventory lifecycle is covered: browse catalog, check stock, record in/out movements idempotently, surface low stock, and review history. Minor gaps exist around product creation/updating and configuring minimum thresholds, but these are workable around and don't block main workflows.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • -
    license
    Not graded
    quality
    Not graded
    maintenance
    Enables AI agents to interact with Skulabs inventory management system through comprehensive tools for managing products, orders, customers, and analytics. Supports voice agents like Retell AI and desktop applications like Claude for natural language inventory operations.
    -
  • F
    license
    Not graded
    quality
    C
    maintenance
    Enables stock assessment and replenishment by exposing three deterministic tools: inspect stock positions, raise replenishment orders, and check order status. Includes an auditable local client and follows a security-first design with limited API surface.
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables AI agents to query and operate on ERP data through a unified tool interface, working across CSV exports, SFTP drop folders, SQLite/ODBC, and optional enterprise APIs. It supports purchase orders, vendors, and inventory lookups while keeping agent-facing tools consistent regardless of backend.
    MIT
  • F
    license
    Not graded
    quality
    B
    maintenance
    Enables AI-powered warehouse inventory management through MCP, with tools to search products, check stock levels, receive and issue stock, and add products under human-in-the-loop confirmation.
    -