korral-mcp

korral-mcp

MCP server that lets a Duvo agent talk to Korral's StoreLink API, enabling category buyers to offload daily stock checking, replenishment ordering, and order tracking tasks.

Category
Visit Server

README

korral-mcp

MCP server that lets a Duvo agent talk to Korral's StoreLink store-ordering/stock-tracking API, so category buyers can offload the daily "check on-hand vs. POS, decide if a store will go empty, raise a replenishment order" detective work described in CLAUDE.md.

Files

  • server.py — the MCP server (FastMCP). Exposes 3 tools to the agent.
  • storelink.py — mock StoreLink API (FastAPI), standing in for the real Korral system during local dev.
  • key_manager.py — loads per-store X-Korral-Store-Key values from keys.json and resolves store_id ↔ key.
  • keys.json — store_id → key map (git-ignored; seeded with test keys for local dev).
  • .env / .env.example — STORELINK_API_URL and STORELINK_KEYS_FILE config.
  • requirements.txt — mcp, fastapi, uvicorn, httpx, python-dotenv, pydantic.

Running locally

pip install -r requirements.txt

# terminal 1: mock StoreLink API
uvicorn storelink:app --reload --port 8000

# terminal 2: MCP server (reads STORELINK_API_URL=http://localhost:8000/v1 from .env)
python server.py

Tools exposed

  • check_stock_status(store_id, sku, fields=None) — on-hand units and/or recent sales for a SKU (fields: subset of ["on_hand", "hours_of_stock_left"], defaults to both; field names match the result keys, and an unknown field is rejected with a clear error rather than silently ignored). Requesting hours_of_stock_left returns units_sold_last_24h, hourly_sales_rate, and hours_of_stock_left (all derived from trailing-24h POS). units_sold_last_24h is returned explicitly so the "gap between on-hand and last 24h of sales" comparison a buyer makes is a direct subtraction, not something the agent has to reconstruct from the rate. hours_of_stock_left is None when there are no recent sales to project from.
  • raise_replenishment(store_id, sku, quantity) — raises a replenishment order, returns order_id and status.
  • get_replenishment_status(store_id, order_id) — looks up the status of a previously raised order.

StoreLink endpoints not exposed as tools

The mock implements the full StoreLink surface these tools need, but several endpoints from the spec are deliberately not wrapped as MCP tools at all:

Endpoint Why it's not a tool
GET /v1/stores (list stores) The agent operates on a store_id the buyer already gave it; browsing the full store list isn't part of the "is this SKU about to run out" workflow and would let the agent wander into stores/regions it has no reason to touch.
GET /v1/stores/{store_id} (store detail) Same reasoning — store metadata (name, address, etc.) isn't needed to check stock or raise a reorder.
GET /v1/skus/{sku} (SKU detail) Only used internally to resolve a SKU's supplier_id for the (now-removed) lead-time enrichment. With that scoped out, there's no current caller for this data, so it's not exposed.
GET /v1/suppliers/{supplier_id} (supplier detail) Same as above — was only reachable via the SKU lookup, and had no other use.

These aren't gaps — they're the API's general-browsing/reference endpoints, and the tool surface is intentionally narrowed to the three actions a buyer's agent actually needs: check status, reorder, track the order. If a future workflow needs store or supplier metadata, adding it back is a deliberate decision, not something we forgot.

Key decisions and tradeoffs

Only 3 tools, not a 1:1 wrapper of the StoreLink API. StoreLink exposes 8 endpoints (stores list, store detail, inventory, POS, replenishment create/status, SKU detail, supplier detail). We deliberately don't expose store list/detail, raw SKU lookup, or raw supplier lookup as tools — the agent's job is narrowly "is this SKU about to run out at this store, and if so, reorder it," not general StoreLink browsing. Fewer tools means a smaller, easier-to-reason-about surface for the agent and less risk of it wandering into unrelated data. Tradeoff: if a future workflow needs store metadata or supplier browsing, those tools don't exist yet and would need to be added deliberately (a feature, not a gap we forgot).

check_stock_status takes an optional fields list instead of being two separate tools. On-hand and hours-left both start from the same inventory lookup, and a caller sometimes only wants on-hand (cheap) without paying for the POS call (more expensive, more data). One tool with optional fields avoids duplicating the store/key/HTTP plumbing across near-identical tools, while still letting the caller avoid unnecessary calls. Tradeoff: the tool's contract is slightly more complex than a plain single-purpose tool (caller needs to know the fields values), but this is a small, well-documented list.

Supplier lead-time enrichment (time_to_replenish) was scoped out. An earlier draft had check_stock_status also return the SKU's supplier lead time, so the agent could compare "hours left" vs. "time to replenish" and judge urgency in one call. We pulled it back out: the comparison logic (small gap between hours-left and lead-time ⇒ trigger reorder) is exactly the kind of decision that should stay explicit and reviewable rather than buried in a tool's return value, and until that policy is defined, a lead-time field would just be unused surface area on the tool. Tradeoff: the agent (or buyer) currently has to know or separately determine supplier lead time to judge urgency — there's no shortcut for that yet. Revisit once the reorder-trigger policy is actually decided.

storelink.py is a real HTTP mock (FastAPI + uvicorn), not an in-process fake. server.py talks to it over httpx exactly as it would talk to the real StoreLink API, including the X-Korral-Store-Key auth header and per-store key validation (a mismatched key/store_id pair gets a 401). This exercises the real integration path (auth, HTTP errors, JSON shapes) rather than a shortcut that would need to be re-validated when pointed at the real API. Tradeoff: more moving parts locally (two processes) than a pure in-memory fake.

Keys are per-store, loaded from keys.json, not baked into server.py. StoreLink keys are scoped to a single store and rotated weekly by Korral IT. key_manager.py centralizes key lookup so a key rotation only means updating keys.json, not code, and one MCP server instance can serve a buyer who covers multiple stores.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured