Skip to main content
Glama
HillmaDa

Korral StoreLink MCP

by HillmaDa
README.md
# Korral StoreLink MCP

A small FastMCP server and custom local client for Korral's stock-assessment and
replenishment workflow. The StoreLink API is represented by a deterministic stub, as
permitted by the assignment; no real Korral system or customer data is used.

## What works

The MCP server exposes exactly three Pydantic-validated tools:

- `inspect_stock_position`: returns on-hand units, POS units sold over the requested
  window, `gap = max(POS - on-hand, 0)`, and whether the gap exceeds the threshold.
- `raise_replenishment`: creates one order for an explicit positive quantity and returns
  its StoreLink order ID and status.
- `get_replenishment_status`: retrieves the status of a known order.

The surface deliberately omits raw POS transactions, arbitrary StoreLink requests,
store/SKU administration, supplier lookup, and credential tools. The supplied buyer
task does not need them, and exposing them would give an agent broader access without
improving the acceptance path. Assessment and replenishment remain separate so the
buyer audit can show the evidence before the consequential action.

The custom FastMCP client runs the validated cases in
[`examples/step2_buyer_case`](examples/step2_buyer_case), writes one readable JSON file
per run under `audit_trails/`, and prints redacted StoreLink call evidence. Technical
events are JSON logs on stderr for FDE troubleshooting.

## Local setup and demo

Python 3.12+ and [uv](https://docs.astral.sh/uv/) are required.

```bash
uv sync --all-groups
export KORRAL_STORE_KEYS_JSON='{"47":"demo-key-47","102":"demo-key-102"}'
uv run korral-demo
```

Run the strict `> 6` boundary case:

```bash
uv run korral-demo --case examples/step2_buyer_case/strict_threshold.json
```

The command prints the assessment, order, status, and redacted stub calls. Its
`audit_path` points to the saved buyer audit. Demo keys are synthetic and must not be
reused outside local development.

## Run the MCP service

HTTP is the packaged default:

```bash
export KORRAL_STORE_KEYS_JSON='{"47":"demo-key-47","102":"demo-key-102"}'
uv run korral-mcp
```

The Streamable HTTP endpoint is `http://localhost:8000/mcp`. Set
`MCP_TRANSPORT=stdio` for a local stdio client. Other configuration is documented in
`.env.example`; production should set `KORRAL_STORE_KEYS_FILE` to a Secret
Manager-backed mounted JSON file rather than placing key values in process arguments.

## Test and quality commands

```bash
uv run pytest
uv run ruff check .
uv run ruff format --check .
uv run mypy
```

Tests use only the StoreLink stub. They prove the mixed and threshold cases, the narrow
tool surface, Pydantic rejection of a non-positive order, in-flight key rotation, failed
refresh, missing-store credentials, audit persistence for successful and failed runs,
ambiguous-write review without retry, and secret redaction.

## Recording checklist

The submission recording should show the complete acceptance evidence in this order:

1. Run `uv run korral-demo` and point out the three MCP tool calls.
2. Show store 47's gap of 8, its 8-unit accepted order, and store 102's no-order result.
3. Open the printed `audit_path` and show the evidence, decision, order, and outcome.
4. Run `uv run pytest` and show the credential rotation, missing credential, and
   uncertain-write tests passing.
5. Show the successful `docker build -t korral-storelink-mcp:local .` command and briefly
   explain the Korral-owned deployment path in `DEPLOYMENT.md`.

## Container

```bash
docker build -t korral-storelink-mcp:local .
docker run --rm -p 8000:8000 \
  -e KORRAL_STORE_KEYS_JSON='{"47":"demo-key-47","102":"demo-key-102"}' \
  korral-storelink-mcp:local
```

The container runs as a non-root user. For a containerized demo client, mount an audit
directory and override the command with `korral-demo`. See [`DEPLOYMENT.md`](DEPLOYMENT.md)
for the customer deployment and rotation story.

## Assumptions and deliberate shortcuts

- The example gap and order quantity are both `max(last-24h sales - on-hand, 0)`.
- A gap must be strictly greater than six; exactly six does not order.
- StoreLink responses and authentication rejection are stubbed. The credential and
  retry boundary is real, but a real `httpx` adapter awaits Korral's complete schemas.
- An explicit authentication rejection is retried once after reloading the secret.
  Ambiguous writes must be reviewed and are never blindly retried.
- The custom client is deterministic; no LLM is needed. A future agent may interpret
  language, but calculation, validation, credentials, and writes stay deterministic.
- MCP client authentication and a production audit sink must be agreed with Korral IT.