Skip to main content
Glama
aleccritser

storelink-mcp

by aleccritser

StoreLink MCP Server

Python MCP server that exposes the hypothetical StoreLink grocery stocking API as agent tools over stdio.

StoreLink is used by a European grocer with locations across Europe, primarily in Czechia (also Slovakia and Austria in the mock data).

Tools

Built for a category buyer workflow: check on-hand vs last-24h POS, apply the delta rule, then replenish.

Tool

Description

list_stores

List stores

get_store

Store details

get_on_hand_vs_pos

Primary check: on-hand + POS + delta + product/case_pack

create_replenishment

Raise a replenishment order

get_replenishment

Order status

get_on_hand_vs_pos defaults the POS window to the last 24 hours when since is omitted. It returns delta = units_sold − available and a product block (case_pack, lead_time_days, etc.).

Buyer rule: if delta >= 6, call create_replenishment (prefer multiples of product.case_pack).

Related MCP server: storelink-mcp

Butter test scenario

Field

Value

SKU

8847291 — Madeta butter 250g

Store 47

Praha Budějovická — available 4, ~12 sold / 24h → delta 8 → replenish

Store 102

Brno Vaňkovka — available 18, ~5 sold / 24h → delta −13 → do not replenish

Setup

Requires Python 3.10+.

# From the project root
uv sync

Or with pip:

pip install -e .

Configuration

Variable

Default

Description

STORELINK_MODE

mock

mock = in-process fixtures; http = call the real API

STORELINK_BASE_URL

https://storelink.com

Base URL used in http mode

STORELINK_API_KEY

(unset)

Optional Bearer for non-store endpoints

KORRAL_GCP_PROJECT

(required in http)

GCP project where Korral stores weekly-rotated store keys

KORRAL_SECRET_PREFIX

korral-store-

Secret id = {prefix}{store_id} (e.g. korral-store-47)

STORELINK_AUDIT_LOG

logs/storelink-audit.ndjson

NDJSON audit log file path

STORELINK_THREAD_ID

(session id)

Optional agent/thread id for correlating a chat

STORELINK_BUYER_LOG

logs/buyer-activity.ndjson

Category-buyer activity (JSON lines)

STORELINK_BUYER_MD

logs/buyer-activity.md

Category-buyer activity (readable markdown)

Every tool requires a reason string (why the agent is calling it). That value is written into the audit trail as ground-truth rationale.

Korral per-store API keys

Korral IT rotates a per-store API key weekly in GCP Secret Manager. Store-scoped StoreLink calls send:

X-Korral-Store-Key: <key>

Secret naming: projects/{KORRAL_GCP_PROJECT}/secrets/{KORRAL_SECRET_PREFIX}{store_id}/versions/latest
Example for store 47: korral-store-47.

Auth behavior:

  1. Before any store-scoped request, the key is loaded (cached in memory).

  2. If the secret does not exist, the tool fails immediately with a clear message for the agent (no HTTP call):
    No Korral API key configured for store … Ask Korral IT to provision the weekly-rotated key.

  3. If StoreLink returns 401 (key rotated in flight), the cache is invalidated, the latest secret is fetched, and the request is retried once.

Store-scoped tools/endpoints: get_store, inventory/POS inside get_on_hand_vs_pos, create_replenishment, get_replenishment.
list_stores / SKU-supplier lookups do not require a store key.

Use Application Default Credentials (GOOGLE_APPLICATION_CREDENTIALS or workload identity) so the MCP process can read Secret Manager.

Audit logging

All audit events are standardized JSON (one object per line / NDJSON), written to stderr and appended to STORELINK_AUDIT_LOG.

Common envelope:

{
  "schema_version": "1.0",
  "event_type": "tool_call_started",
  "timestamp": "2026-08-01T17:00:00.123Z",
  "session_id": "…",
  "interaction_id": "…",
  "thread_id": "…",
  "service": "storelink-mcp",
  "level": "info",
  "payload": {
    "tool": "get_on_hand_vs_pos",
    "arguments": { "store_id": "47", "sku": "8847291", "reason": "…" },
    "reason": "Check store 47 butter sell-through before replenish decision"
  }
}

event_type

When

session_started / session_ended

Process lifetime

tool_call_started

Tool invoked

tool_call_succeeded

Tool returned (includes duration_ms, result_summary)

tool_call_failed

Tool error (includes error, and traceback for unexpected failures)

interaction_id is unique per tool invocation and shared across that call’s started/succeeded/failed events.

Category buyer activity feed

Alongside the engineer audit log, every agent action is also written in plain language for category buyers:

  • Why the agent acted (from the required reason argument)

  • What happened (stock checked, order placed, etc.)

  • Outcome (e.g. replenishment recommended when delta ≥ 6)

Files:

  • logs/buyer-activity.md — human-readable timeline

  • logs/buyer-activity.ndjson — same entries as structured JSON

MCP resource (not a tool — does not clutter the buyer agent loop):

  • buyer://activity — current-session feed in markdown

Example markdown entry:

### 2026-08-01T17:22:49Z — Checked on-hand vs recent sales for Madeta butter 250g at store 47

**Status:** Needs attention
**Why the agent did this:** Check butter delta at store 47 before replenish decision
**What happened:** In the last 24 hours, about 12 units sold; 4 units are still available on the shelf (gap/delta: 8).
**Outcome:** Sales are outpacing stock (delta 8 ≥ 6). A replenishment order is recommended.

Mock mode includes Czech/EU stores (plus scenario stores 47 and 102), metric SKUs including 8847291, inventory, POS history (CZK/EUR), and Central European suppliers.

Run

uv run storelink-mcp
# or
uv run python -m storelink_mcp.server

Cursor / Claude Desktop config

Add to your MCP settings (e.g. Cursor mcp.json):

{
  "mcpServers": {
    "storelink": {
      "command": "/Users/aleccritser/.local/bin/storelink-mcp-cursor",
      "env": {
        "STORELINK_MODE": "mock"
      }
    }
  }
}

Important: the project path contains a space (Duvo Task). Cursor splits the command field on spaces, so pointing command at .venv/bin/storelink-mcp under that folder fails with spawn /Users/aleccritser/Duvo ENOENT. Use the no-space launcher at ~/.local/bin/storelink-mcp-cursor (created by the project setup), or uv with the directory passed as a separate arg:

{
  "mcpServers": {
    "storelink": {
      "command": "/Users/aleccritser/.local/bin/uv",
      "args": ["run", "--directory", "/Users/aleccritser/Duvo Task", "storelink-mcp"],
      "env": {
        "STORELINK_MODE": "mock"
      }
    }
  }
}

For live HTTP against a StoreLink deployment:

{
  "mcpServers": {
    "storelink": {
      "command": "uv",
      "args": ["run", "--directory", "/absolute/path/to/Duvo Task", "storelink-mcp"],
      "env": {
        "STORELINK_MODE": "http",
        "STORELINK_BASE_URL": "https://storelink.com",
        "STORELINK_API_KEY": "your-api-key"
      }
    }
  }
}

Example agent flow

  1. get_on_hand_vs_pos("47", "8847291", reason="…") → read delta and product.case_pack

  2. If delta >= 6create_replenishment(..., reason="delta>=6 …")

  3. Optionally check store 102 (delta should stay below 6 — no order)

  4. get_replenishment(..., reason="…") → poll status

Available Tools

5 tools
create_replenishmentA

Raise a replenishment order when get_on_hand_vs_pos delta >= 6.

Call after get_on_hand_vs_pos when units_sold − available is at least 6. Prefer ordering in multiples of product.case_pack from that response.

Args: store_id: Store identifier (e.g. 47). sku: Product SKU to replenish (e.g. 8847291). quantity: Units to order (must be > 0; prefer case_pack multiples). reason: Why you are raising this order (required for audit). order_reason: Optional note stored on the replenishment order record. priority: One of low, normal, high, urgent (default normal).

ParametersJSON Schema
NameRequiredDescriptionDefault
skuYes
reasonYes
priorityNonormal
quantityYes
store_idYes
order_reasonNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.2/5.0
Behavior3/5

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

With no annotations, the description carries full responsibility for behavioral disclosure. It mentions the audit requirement ('reason... required for audit') and the priority options, but does not disclose side effects such as whether calling it multiple times creates duplicate orders, or if special permissions are needed. This is a moderate disclosure, not comprehensive.

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

Conciseness4/5

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

The description is well-organized with a purpose sentence, usage guideline, and an Args list. The first two sentences are slightly redundant ('delta >= 6' vs 'units_sold − available is at least 6'), but otherwise every sentence contributes important information. This is efficient and easy to scan.

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 6-parameter create tool with an output schema, the description covers all parameter semantics, the trigger condition, and even a default value. The only missing piece is a note about what happens on success (e.g., returns order ID), but the output schema covers return values, so the description is effectively complete for an agent to invoke it correctly.

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

Parameters5/5

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

The schema has zero description coverage, so the description must explain all parameters. It does this thoroughly: each parameter has an example, constraints (e.g., quantity > 0), and clarification that order_reason is optional, while priority lists its enum values and default. This fully compensates for the absent schema descriptions.

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

Purpose5/5

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

The description opens with 'Raise a replenishment order' – a specific verb + resource that clearly identifies the tool's action. It further distinguishes itself from siblings by tying to 'get_on_hand_vs_pos delta >= 6', making its purpose unique and unambiguous.

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?

The description gives explicit guidance: 'Call after get_on_hand_vs_pos when units_sold − available is at least 6' and 'Prefer ordering in multiples of product.case_pack from that response.' This provides a clear trigger condition and ordering preference, but it does not explicitly mention alternatives or when not to use the tool, so it falls just short of a 5.

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

get_on_hand_vs_posA

Current on-hand plus POS sales for a SKU at a store (category buyer view).

Returns inventory, last-24h POS (by default), delta = units_sold − available, and a product block (name, case_pack, supplier lead_time_days) for order sizing. If delta >= 6, call create_replenishment (prefer multiples of case_pack).

Args: store_id: Store identifier (e.g. 47 or 102). sku: Product SKU (e.g. 8847291 for Madeta butter 250g). reason: Why you are checking on-hand vs POS (required for audit). since: Optional ISO-8601 start of the POS window. If omitted, defaults to 24 hours ago (UTC).

ParametersJSON Schema
NameRequiredDescriptionDefault
skuYes
sinceNo
reasonYes
store_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.8/5.0
Behavior4/5

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

With no annotations provided, the description carries the full burden of disclosing behavioral traits. It explains the computation of delta (units_sold − available), the default time window, and what data is returned. It stops short of explicitly stating 'read-only' or listing side effects, but the 'get' verb and return-focused wording strongly imply a safe, non-mutating operation.

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?

The description is compact and efficient: a single paragraph with line breaks for readability, front-loaded with the core purpose, and every sentence adds value. It includes examples, return details, and an action rule without any filler.

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

Completeness5/5

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

Given that an output schema exists, the description still adds value by explaining what the outputs represent (delta, product block) and how they support order sizing. It also covers all four parameters thoroughly and includes actionable guidance. This is complete for the tool's complexity.

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

Parameters5/5

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

Schema description coverage is 0%, so the description must fully compensate. It does: store_id and sku get concrete examples, sku is tied to a product, reason is explained as required for audit, and since has its default behavior fully documented. Every parameter is meaningfully described beyond the raw schema.

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

Purpose5/5

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

The description clearly states the tool's purpose: to get current on-hand inventory and POS sales for a SKU at a store. It specifies the output components (inventory, POS, delta, product block) and clearly distinguishes itself from sibling tools like list_stores and create_replenishment by focusing on the inventory/sales view rather than store catalog or replenishment actions.

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

Usage Guidelines5/5

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

The description provides explicit guidance on when to use this tool and when to escalate: if delta >= 6, call create_replenishment. It also explains the default POS window (last 24h) and how to override it with 'since', giving clear usage context for the category buyer view.

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

get_replenishmentB

Get status of a replenishment order by store_id and order_id.

Args: store_id: Store identifier. order_id: Replenishment order id. reason: Why you are checking order status (required for audit).

ParametersJSON Schema
NameRequiredDescriptionDefault
reasonYes
order_idYes
store_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

B3.3/5.0
Behavior3/5

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

Annotations are absent, so the description carries the burden. It implies a read-only operation via 'Get' and adds the context that 'reason' is required for audit purposes. However, it does not explicitly state side effects, auth requirements, error behavior, or any other behavioral traits.

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?

The description is extremely concise: a one-sentence purpose followed by a structured Args list. Every sentence carries relevant information, and the format is front-loaded with the main purpose.

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?

With an output schema present, return values are covered. The description adequately explains the tool's purpose and parameters, but lacks context on when to use it relative to siblings and provides minimal behavioral detail. Overall, it is functional but not fully complete for a tool with sibling alternatives.

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 0%, so the description must define parameters. It provides brief but meaningful definitions for each parameter, and the 'reason' parameter is further contextualized as an audit requirement. However, the definitions are minimal and mostly echo the parameter names, offering limited additional semantic depth.

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 clearly states a specific verb ('Get') and resource ('status of a replenishment order'), and identifies the key identifying parameters (store_id, order_id). It is distinct from sibling tools by focusing on order status, but it does not explicitly differentiate itself from alternatives such as get_store or create_replenishment.

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

Usage Guidelines2/5

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

The description provides no guidance on when to use this tool versus alternatives. There is no mention of context, exclusions, or why this tool might be preferred over a sibling like get_store or get_on_hand_vs_pos.

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

get_storeA

Get details for a single store by store_id (e.g. 47 or store-001).

Args: store_id: Store identifier. reason: Why you need this store's details (required for audit).

ParametersJSON Schema
NameRequiredDescriptionDefault
reasonYes
store_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.2/5.0
Behavior3/5

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

With no annotations provided, the description must carry the transparency burden. It usefully discloses that the 'reason' argument is required for audit, and gives format examples for store_id. However, it does not explicitly state whether the operation is read-only, whether special permissions are needed, or any limitations on returned details, relying on the output schema to clarify output.

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?

The description is compact and front-loaded: one sentence states the core purpose, followed by a clean args list. Every sentence adds value with no redundancy or filler.

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?

Given the simple get-by-id operation, an output schema is present, and the description includes the audit requirement, the tool is well-specified. It could have strengthened contextual guidance by explicitly referencing list_stores for discovering store_ids, but this is a minor omission for a low-complexity tool.

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 schema has no descriptions (0% coverage), so the description provides essential meaning for both parameters. It clarifies 'store_id' with an example format and explains 'reason' as an audit requirement, going well beyond the bare schema field names.

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

Purpose5/5

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

The description clearly states the action ('Get details'), the resource ('a single store'), and the required identifier ('store_id'). The qualifier 'single' distinguishes it from sibling tool list_stores, making the purpose unambiguous.

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?

The description implies usage when you have a known store_id and need just that store's details, contrasting with list_stores. It does not explicitly mention alternatives or exclusions, but the by-id framing provides clear context for when this tool should be selected.

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

list_storesA

List all StoreLink stores (id, name, city, country code, active flag).

Args: reason: Why you are listing stores (required for audit).

ParametersJSON Schema
NameRequiredDescriptionDefault
reasonYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.2/5.0
Behavior3/5

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

With no annotations, the description must disclose behavioral traits itself. It mentions the reason parameter is 'required for audit', which implies logging side effects, but it doesn't describe authentication needs, rate limits, or whether the operation is strictly read-only. 'List' implies a safe read, but more detail would improve transparency.

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?

The description is extremely concise and front-loaded with the core purpose. The 'Args' line adds necessary parameter info without any fluff. Every sentence earns its place.

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

Completeness5/5

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

The tool is simple (one parameter, no nested objects) and has an output schema, so the description doesn't need to detail return values. It provides the required parameter's purpose, the tool's scope, and the key fields. This is sufficient for correct invocation.

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

Parameters5/5

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

Schema description coverage is 0%, so the description must compensate. It fully explains the only parameter: 'reason: Why you are listing stores (required for audit).' This adds meaning beyond the schema, clarifying both purpose and requiredness.

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

Purpose5/5

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

The description starts with a clear verb and resource: 'List all StoreLink stores' and enumerates the fields returned (id, name, city, country code, active flag). This distinguishes it from sibling tools like get_store, which targets a single store, and the replenishment tools.

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 description implies this is for listing all stores, but it does not explicitly state when to choose this over alternatives or mention any exclusions. The 'all' qualifier suggests bulk retrieval, but there is no direct guidance on when to use this vs. get_store or the replenishment tools.

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 updatesv0.1.0
    • First observedcreate_replenishment
    • First observedget_on_hand_vs_pos
    • First observedget_replenishment
    • First observedget_store
    • First observedlist_stores

TDQS

A4.1/5.0

Scored across 5 tools

Disambiguation5/5

Each tool targets a distinct action and resource: listing stores, getting store details, checking inventory/POS, creating a replenishment, and retrieving a replenishment. There is no overlap or ambiguity between them.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern (list_stores, get_store, get_on_hand_vs_pos, create_replenishment, get_replenishment). The naming is predictable and clear, with the compound 'get_on_hand_vs_pos' still fitting the convention.

Tool Count5/5

The server has 5 tools, which is well-scoped for its purpose of store and replenishment management. Each tool serves a necessary function without redundancy, making the set neither too thin nor too heavy.

Completeness4/5

The main workflow is covered: list/get stores, check inventory vs POS, create a replenishment, and check its status. A minor gap is the lack of a 'list_replenishments' tool or update/cancel capabilities, but the core lifecycle is functional.

Maintenance

ActivitySlowing
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    B
    maintenance
    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.
    -
  • F
    license
    A
    quality
    B
    maintenance
    MCP server for managing store replenishment, including listing stores, raising orders, checking order status, and viewing SKU reports with observability and key rotation handling.
    4
    -
  • F
    license
    Not graded
    quality
    B
    maintenance
    MCP server that enables AI platforms to search products, customers, and warehouses, and prepare and submit sales orders to a fixed ERP endpoint with per-session bearer authentication.
    -
  • A
    license
    A
    quality
    B
    maintenance
    An MCP server that enables Duvo agents to perform Korral category buyer tasks against the StoreLink API, including checking stock risk, reviewing sales, and raising replenishment orders.
    5
    MIT