Skip to main content
Glama
KeeperSolutions

Sneaker Catalog MCP Server

README.md
# Sneaker Catalog — Data Layer, MCP Server & Agent

A small e-commerce catalog for sneakers, built in three layers:

1. **Data layer** — Postgres + pgvector, with keyword and semantic search over products.
2. **MCP server** — exposes the catalog as tools (`search`, `get_product_details`, `get_stock`) over the [Model Context Protocol](https://modelcontextprotocol.io).
3. **Agent** — a CLI chat agent that connects to the MCP server and answers questions about the catalog purely by calling those tools.

## Architecture

```
┌─────────────┐   MCP (stdio)   ┌──────────────┐        ┌────────────────┐
│  agent/cli   │ ──────────────▶ │ mcp_server   │ ─────▶ │ Postgres +     │
│ (OpenAI llm) │ ◀────────────── │ (tool calls) │ ◀───── │ pgvector       │
└─────────────┘                 └──────────────┘        └────────────────┘
```

- `db/` — plain Python functions (`search_products`, `get_product`, `check_stock`, `filter_products`) that query Postgres directly.
- `mcp_server/` — wraps those functions as MCP tools using the official Python MCP SDK.
- `agent/` — a REPL that spawns the MCP server as a subprocess, converts its tool schemas to OpenAI's function-calling format, and runs the request → tool-call → result loop until the model has a final answer.

Search is hybrid: full-text keyword ranking (Postgres `tsvector`) blended with semantic similarity (OpenAI embeddings + pgvector cosine distance).

## Prerequisites

- Docker (recommended path), **or** Python 3.14 + a local Postgres with the `vector` extension
- An OpenAI API key with access to an embeddings model (`text-embedding-3-small`) and a chat model (e.g. `gpt-5-mini`)

## Quickstart (Docker)

```bash
make setup   # cp .env.example .env — then edit it: POSTGRES_* / OPENAI_API_KEY
make up      # starts Postgres, seeds the catalog
make logs    # confirm "Seed complete." (Ctrl+C to stop tailing)

make agent   # chat with the catalog
```

> `agent` (and `mcp-server`) depend on `seed`, so `make agent` re-runs the seed step first on every invocation. Seeding checks whether the catalog is already populated and skips instantly if so, so this is fast and harmless.

Type a question at the `> ` prompt. Type `exit` (or Ctrl+D) to quit.

### Other services

```bash
make mcp-server   # run the MCP server standalone, e.g. to attach MCP Inspector
make inspector    # run MCP Inspector against the containerized server
```

The MCP server uses stdio transport, so it only runs attached to a terminal — it's excluded from `make up`'s default services (behind the `tools` compose profile) to avoid a crash loop when started detached.

Run `make help` to see all available commands, or `make down` to stop everything.

## Quickstart (local, no Docker)

```bash
make venv       # create venv + install requirements
make db-up      # just the Postgres container
make seed-local # seed the catalog once

make agent-local  # chat with the agent
```

To test the MCP server directly with Inspector:

```bash
source venv/bin/activate
npx @modelcontextprotocol/inspector python -m mcp_server.server
```

## Project layout

```
db/
  schema.sql      products, variants, inventory, prices + pgvector/tsvector indexes
  seed.py         idempotent catalog seeding (embeds descriptions via OpenAI)
  search.py       hybrid keyword + semantic search
  product.py      get_product() — product + all variants
  stock.py        check_stock() — inventory by SKU
  filters.py      SQL filtering by category / price

mcp_server/
  server.py       MCP tool definitions (search, get_product_details, get_stock)

agent/
  cli.py          interactive chat agent (OpenAI function-calling + MCP client)

docker-compose.yml   db, seed, mcp-server, agent services
Dockerfile           shared image for seed / mcp-server / agent
Makefile             `make help` for all available commands
agent-start.sh       local (non-Docker) launcher for the agent
```

## Environment variables

| Variable | Used by | Notes |
|---|---|---|
| `POSTGRES_USER` / `POSTGRES_PASSWORD` / `POSTGRES_DB` | db, all Python services | |
| `POSTGRES_HOST` / `POSTGRES_PORT` | db connections | defaults to `localhost:5432`; Docker services override `POSTGRES_HOST=db` |
| `OPENAI_API_KEY` | seeding (embeddings), search (query embeddings), agent (chat) | |

See `.env.example` for a template.