Skip to main content
Glama
rkrdevone-commits

stationery-inventory

Stationery Inventory MCP Server — Phase 0

One tool, one dataset, one job: prove Claude can call real code and get real data back. Everything here was actually run and tested before being handed to you — not just written and assumed to work.

Files

  • server.py — the MCP server. One tool: check_stationery_inventory.

  • inventory_data.csv — sample product data (edit freely; the server reads it fresh on every call, so changes show up immediately).

  • audit_log.jsonl — created automatically on first tool call. One JSON line per call: timestamp, tool name, input, output.

  • requirements.txt — pinned dependency.

Related MCP server: Alegra MCP

A version gotcha worth knowing (we hit this so you don't have to)

The MCP Python SDK went through a breaking rename: FastMCP (v1.x, what most tutorials online still show) became MCPServer (v2.x, what pip install mcp gives you today). This server uses the current v2 API:

from mcp.server.mcpserver import MCPServer
mcp = MCPServer("stationery-inventory")

If you ever copy MCP server code from an older tutorial and get ModuleNotFoundError: No module named 'mcp.server.fastmcp', this is why.

Setup

cd stationery-mcp-server
python3 -m venv venv
source venv/bin/activate        # Windows: venv\Scripts\activate
pip install -r requirements.txt

The SDK ships an inspector UI for exactly this:

mcp dev server.py

This opens a local web UI where you can call check_stationery_inventory directly and see the raw request/response — confirm it works here before adding the complexity of a full Claude conversation.

Wire it into Claude Desktop

Edit your Claude Desktop config file:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json

  • Windows: %APPDATA%\Claude\claude_desktop_config.json

Add (using the absolute path to this folder's server.py, and the absolute path to the python inside your venv):

{
  "mcpServers": {
    "stationery-inventory": {
      "command": "/absolute/path/to/stationery-mcp-server/venv/bin/python",
      "args": ["/absolute/path/to/stationery-mcp-server/server.py"]
    }
  }
}

Restart Claude Desktop fully (quit, not just close the window). Then ask it something like:

"Do we have the Under the Sea picture book in stock?"

If wired correctly, Claude will call the tool and answer from the real CSV data — including telling you it's out of stock (SKU BK-CH-002 is intentionally set to 0 in the sample data, to test that path).

Wire it into Claude Code

claude mcp add stationery-inventory -- /absolute/path/to/venv/bin/python /absolute/path/to/server.py

Then claude mcp list to confirm it's registered.

What "done" looks like for Phase 0

  • mcp dev server.py returns correct results for an in-stock item, an out-of-stock item, and a misspelled/partial product name.

  • Claude Desktop or Claude Code can call the tool in a live conversation and give a correct answer.

  • audit_log.jsonl has one entry per call, with sane input/output.

Deliberately NOT in scope for Phase 0

  • A real database or live inventory API (CSV is fine for now)

  • Multiple tools (add check_order_status etc. only after this one is solid)

  • Remote deployment / auth beyond local stdio

  • An orchestration framework on top

Each of those is a later module — adding them now would dilute the one thing Phase 0 is meant to prove: the full loop works.


Module 2: a real deployed agent (agent.py)

Phase 0 proved Claude inside Claude Desktop/Code can call your tool. Module 2 proves you can ship an independent, scriptable agent — the "one deployed agent" gap called out in your roadmap notes — built on the Claude Agent SDK, using this same MCP server as its only tool.

Additional setup

npm install -g @anthropic-ai/claude-code    # the CLI the SDK runs underneath
pip install claude-agent-sdk
export ANTHROPIC_API_KEY=your-key-here

Why the npm install is needed even for a Python project: the Claude Agent SDK (Python or TypeScript) is a thin wrapper around the Claude Code CLI — it spawns that CLI as a subprocess to run the actual agent loop. If you skip this step you'll hit CLINotFoundError the moment you run agent.py. Confirm it worked with claude --version.

Design choice: narrow scope on purpose

agent.py restricts allowed_tools to exactly one tool — mcp__stationery-inventory__check_stationery_inventory — and the system prompt explicitly refuses anything outside stock/pricing questions. This isn't a limitation to fix later; it's the point. Your notes cite a benchmark where a policy-constrained, tool-scoped agent hit 110/110 successful runs, while a fully autonomous version of the same task failed all 330 attempts. Every additional tool or open-ended instruction you add is a branch point where the agent can go wrong — add them deliberately, one at a time, not by default.

Run it

python agent.py "Is the Spiral Notebook A5 in stock?"
python agent.py                       # interactive mode, 'quit' to exit

Run the eval harness (eval.py)

Five test cases checking two things that actually matter for a production agent — not "did it sound plausible":

  1. Did it actually call the tool, or hallucinate an answer?

  2. Does the final answer contain the fact you know is true from the CSV?

python eval.py

This is a deliberately small seed of Module 5 (evaluation/observability) from your roadmap — introduced now because retrofitting eval discipline onto an agent later costs far more than building the habit from agent #1.

What we verified before handing this to you (and what we couldn't)

Tested in this environment, without a real API key:

  • The SDK's exact API surface (ClaudeAgentOptions, query, message types) matches what the code uses — checked against the installed package, not assumed from memory.

  • The Claude Code CLI dependency is real and required — confirmed via CLINotFoundError existing in the SDK and installing the CLI to resolve it.

  • The full pipeline — CLI spawn → MCP subprocess launch → tool discovery → API call — works end to end. With a placeholder key, the session initialized, the MCP server showed status: connected, and the tool registered as exactly mcp__stationery-inventory__check_stationery_inventory, confirming the wiring is correct up to the authentication boundary.

Not tested here (needs your real key, on your machine):

  • An actual model response and tool call with real data

  • The eval.py pass/fail results

Run eval.py first thing after setup — if all 5 cases pass, Module 2 is done.

Module 3: tool-scoped subagents (orchestration/)

Module 2 proved one deployed agent (agent.py) can call this server's single tool. Module 3 proves the same MCP server can sit underneath multiple delegated subagents instead of one flat agent — each subagent scoped to the minimum tools it needs, using the Claude Agent SDK's native AgentDefinition/Agent tool primitives (no LangGraph or other orchestrator here — that comparison is Module 4, see learning-notes/module-3-native-subagents-vs-langgraph.md... [see note below]).

  • inventory-checker — the only agent allowed to call this server's check_stationery_inventory tool.

  • order-status — read-only, calls a separate mock orders tool (orchestration/tools/orders_server.py; swap for a real order source later).

  • refund-calculatorzero tools. Computes only from what the parent agent supplies in its prompt; cannot look anything up or issue anything. This is the access-control pattern this module is meant to demonstrate.

Additional setup

pip install -r orchestration/requirements.txt

Reuses the claude-agent-sdk and CLI dependency already installed for Module 2 — no new external tooling needed.

Run it

cd orchestration
python run_orchestrator.py --dry-run     # no API key needed
python run_orchestrator.py               # live, needs ANTHROPIC_API_KEY

What "done" looks like for Module 3

  • --dry-run passes and resolves the real path to ../server.py

  • A live run routes an inventory question to inventory-checker and gets a real answer from this server's CSV

  • A live run routes an order question to order-status

  • A live run routes a refund question to refund-calculator, which computes a number without calling any tool

  • One explicit-invocation prompt ("Use the X agent...") works

Deliberately NOT in scope for Module 3

  • Real order data (still a mock tool)

  • A cross-provider orchestrator (LangGraph — that's the next module)

  • Actually issuing a refund (compute-only, by design)

Related MCP Connectors

Related MCP Servers