Skip to main content
Glama

ecommerce-mcp-server

An MCP (Model Context Protocol) server exposing e-commerce customer support tools to LLM agents: product catalog search (hybrid full-text + vector retrieval with cross-encoder reranking), order status/tracking, cart management, a FAQ knowledge base, and human escalation via support tickets.

Built on FastAPI + the official mcp Python SDK, served over Streamable HTTP, with API-key auth and per-client rate limiting applied as middleware. Retrieval runs on Postgres + pgvector -- no separate vector database to run or pay for. See SYSTEM_DESIGN.md for the reasoning behind that and the retrieval pipeline design (RRF fusion + cross-encoder rerank -- the same pattern used in the author's "Ledger" RAG project). See HLD_LLD.md for the full system design -- high-level component/deployment views and low-level module, data-model, and state-machine detail across the whole project.

Architecture

Client (LLM agent / chat app)
        │  HTTP + X-API-Key
        ▼
 ApiKeyAuthMiddleware → RateLimitMiddleware → RequestLoggingMiddleware
        │
        ▼
   FastAPI app (src/mcp/server.py)
        │  mounted at /mcp
        ▼
   FastMCP server (src/mcp/registry.py)
        │  tool calls
        ▼
   src/tools/*  →  src/services/*  →  Postgres (products, FAQ)
                          │            + in-memory (cart, orders, tickets)
                          ▼
                  src/rag/* (hybrid search: Postgres full-text search +
                             pgvector, RRF fusion, cross-encoder rerank)

Each layer has one job:

Layer

Responsibility

src/mcp/

MCP server setup, tool registration, session helpers

src/tools/

Thin, testable functions with the docstrings/signatures the agent sees as tool schemas

src/services/

Business logic + data access, written against a small repository-style interface

src/rag/

Hybrid (Postgres full-text + pgvector) search with cross-encoder reranking

src/db/

SQLAlchemy async engine/session, ORM models, seed script

src/models/

Pydantic schemas shared across the app

src/middleware/

Auth, rate limiting, request logging

src/core/

Config, logging setup, exceptions, seed data

What's Postgres-backed vs. still in-memory, and why: products and faq_entries are real Postgres tables (see migrations/versions/) because those are exactly the two things the RAG layer searches over, and search quality is the part of this project meant to demonstrate real retrieval engineering. cart, order, and ticket services are intentionally still in-memory -- they're written against the same small async interface (get_by_id, list_all, ...) the product service used to use, so Postgres-backing them later is adding a new implementation and swapping an import, not a redesign. That seam is deliberate, not an oversight; see src/services/cart_service.py's docstring.

Related MCP server: Commerce MCP Server

Tools exposed

Tool

Description

search_products

Hybrid search over the catalog (name, description, tags) with category/price/stock filters

get_product_details

Full details for a single product by ID

check_order_status

Current status of an order

track_shipment

Carrier + tracking number + ETA for a shipped order

cancel_order

Cancel an order still in a cancellable state

request_order_return

Request a return for a delivered order

view_cart / add_to_cart / remove_from_cart / update_cart_quantity

Session-scoped cart management

answer_faq

Hybrid search over the FAQ knowledge base

escalate_to_human

Create a support ticket for human follow-up

Getting started

With Docker Compose (Postgres + Redis provisioned for you):

cp .env.example .env   # edit at minimum: MCP_API_KEYS, GROQ_API_KEY

docker compose up -d postgres redis
docker compose run --rm app alembic upgrade head
docker compose run --rm app python -m src.db.seed
docker compose up app

Without Docker (Python 3.11+, and a Postgres instance with the vector extension installable -- e.g. CREATE EXTENSION vector; once as a superuser):

pip install -e ".[dev]"
cp .env.example .env
# point DATABASE_URL at your Postgres instance

alembic upgrade head        # creates products/faq_entries with FTS + HNSW indexes
python -m src.db.seed        # loads src/core/mock_data.py + computes embeddings
python server.py
# -> FastAPI on http://localhost:8000
#    health check:  GET  /health   (no auth required)
#    MCP endpoint:  POST /mcp      (requires X-API-Key header)

The first call that touches search (search_products / answer_faq) will lazily download the embedding and reranker models (sentence-transformers/all-MiniLM-L6-v2 and cross-encoder/ms-marco-MiniLM-L-6-v2 by default, both configurable via .env) -- expect a one-time delay on cold start in a fresh environment. python -m src.db.seed also needs these models, since it computes each row's embedding before writing it.

Testing

pytest                          # full suite
pytest tests/unit                # fast, no DB or model downloads
pytest --cov=src --cov-report=term-missing

tests/unit/test_pg_search.py covers the Reciprocal Rank Fusion logic in isolation (pure functions, no DB). The retrieval SQL itself (full-text search, pgvector distance queries, filter_products) is covered against a real Postgres instance rather than mocked -- tsvector/vector query behavior isn't meaningfully testable without one. This was verified during development against a live Postgres 16 + pgvector 0.6 instance: the migration applied cleanly, the generated search_vector columns and HNSW indexes were created as expected, and search_products/answer_faq returned correctly ranked results end-to-end.

Roadmap / not yet implemented

  • Postgres-backed cart/order/ticket services (see the "what's Postgres-backed" note above -- same seam the product service used before this pass)

  • Redis-backed rate limiting for multi-instance deployments (currently in-process token buckets)

  • Groq-powered intent routing / query rewriting ahead of the MCP tool layer

  • Session hardening (auth token expiry, per-session tool scoping)

  • Kubernetes manifests (deploy/k8s/) -- Dockerfile + Compose exist, k8s is the next step once there's a cluster to target

Related MCP Connectors

Related MCP Servers