使用说明
MCP Customer Support Agent
Customer support agent for an e-commerce scenario: keyword-based intent classification, retrieval over policy documents (ChromaDB + sentence-transformers), and three tool servers that look up orders, process refunds and cancel orders in a SQLite database. Exposed as a CLI demo and a FastAPI service.
Table of Contents
- Overview
- Architecture
- Features
- Installation
- Quick Start
- Usage
- Tool Servers
- Project Structure
- Testing
- Tech Stack
- Limitations and scope
Overview
What is implemented
- A rule-based orchestrator (
mcp_agent/agent/orchestrator.py): 6 intents matched on English and French keywords, order-id extraction with regular expressions, templated responses - Three in-process tool servers (database, retrieval, actions), each with a JSON-schema tool description
- Retrieval over 3 markdown policy files (16 indexed sections) with ChromaDB and
all-MiniLM-L6-v2 - A seeded SQLite database (5 products, 5 customers, 15 orders, deterministic seed)
- FastAPI service (
/chat,/orders/{id},/health,/metrics) with in-memory latency and intent metrics - 80 pytest tests (79 unit tests mocking the database and ChromaDB, 1 integration test) and a GitHub Actions workflow (ruff + unit tests with an 80% coverage threshold)
What it is not
- There is no LLM in the loop: intents come from keyword rules and answers are templates or verbatim policy excerpts. "RAG" here means retrieval only, without generation.
- The "MCP servers" are plain Python classes called in-process. They follow the MCP tool-schema shape
(
DATABASE_TOOLS,RAG_TOOLS,ACTIONS_TOOLS) but no MCP transport, SDK or client is used. - Refunds, cancellations and e-mails are simulated: they update the local SQLite database and an in-memory action log, nothing is sent.
Architecture
User Message
│
▼
┌─────────────────────────────────────────────────┐
│ Agent Orchestrator │
│ (keyword intent classification + routing) │
└─────────────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│Database │ │ RAG │ │ Actions │
│ tool │ │ tool │ │ tool │
│ server │ │ server │ │ server │
└─────────┘ └─────────┘ └─────────┘
│ │ │
▼ ▼ ▼
SQLite ChromaDB Refund / cancel
orders policies (simulated e-mail)
The three tool servers are Python classes instantiated by the orchestrator; the actions server shares the database server's session so that a refund or cancellation is visible to the next lookup.
Features
| Feature | Intent | Description |
|---|---|---|
| Check order | check_order |
Order status, product, quantity, total, tracking number |
| Track package | track_package |
Tracking number and link, or "not shipped yet" message |
| Request refund | request_refund |
Eligibility check (status confirmed, shipped or delivered), then status set to refunded |
| Cancel order | cancel_order |
Allowed for pending and confirmed orders only |
| General question | general_question |
Returns the 2 most similar policy sections (no generation) |
| Fallback | unknown |
Routed to the policy search |
| Monitoring | – | Request count, average latency, intent distribution (/health, /metrics) |
Keywords are matched in English and French (e.g. refund / remboursement, cancel / annuler,
where is / où est), but the responses are in English only.
Installation
git clone https://github.com/tealamenta/mcp-customer-support-agent.git
cd mcp-customer-support-agent
pip install -e ".[dev]"
Requires Python 3.11+. No API key is needed; configuration is optional and read from environment
variables (see .env.example).
Quick Start
# 1. Seed the SQLite database (deterministic: 5 products, 5 customers, 15 orders)
python run.py seed
# 2. Index the knowledge base (downloads the ~90 MB all-MiniLM-L6-v2 model on first run)
python run.py index
# 3. Run the interactive demo
python run.py demo
Usage
Interactive demo
Output obtained on the seeded database (order #5 is confirmed, order #14 is delivered):
$ python run.py demo
You: What's the status of order #5?
Agent [check_order]:
Here's the status of your order #5:
- Product: Laptop Stand
- Quantity: 2
- Total: $59.98
- Status: CONFIRMED
- Order Date: 2026-09-27
You: I want a refund for order #14
Agent [request_refund]:
[REFUND PROCESSED]
- Order: #14
- Product: Smart Watch
- Refund Amount: $399.98
The refund will appear on your original payment method within 5-7 business days.
A confirmation email has been sent to alice@email.com.
[Actions: [{'action': 'refund', 'order_id': 14, 'amount': 399.98}]]
The order date depends on the day the database was seeded.
API server
python run.py api
# Swagger UI: http://localhost:8000/docs
| Method | Endpoint | Description |
|---|---|---|
| GET | / |
Service status |
| GET | /health |
Health + request count, average latency, error rate |
| GET | /metrics |
Full metrics (aggregates + last 10 requests) |
| POST | /chat |
{"message": "..."} → intent, response, actions, latency |
| GET | /orders/{id} |
Raw order record |
Metrics are kept in memory and written to metrics/metrics_<date>.json when the API shuts down.
Docker
docker compose up
The container seeds the database and builds the vector index at start-up (so the first start needs network access to download the embedding model), then serves the API on port 8000.
Tool Servers
All three are classes in mcp_agent/mcp_servers/, called directly by the orchestrator.
Database server (DatabaseMCPServer)
get_order(order_id)- Order details joined with customer and productget_customer_orders(customer_email)- All orders of a customerupdate_order_status(order_id, new_status)- Update statusget_order_for_refund(order_id)- Order +refund_eligible/refund_amount
RAG server (RAGMCPServer)
search_policies(query, n_results)- Nearest policy sections (ChromaDB)get_refund_policy()- Top 5 sections for "refund policy eligibility"get_shipping_info()- Top 3 sections for "shipping delivery tracking"
Actions server (ActionsMCPServer)
process_refund(order_id, reason)- Sets status torefunded(simulated payment)cancel_order(order_id, reason)- Sets status tocancelledsend_notification(email, subject, message)- Appends to the action log (no e-mail is sent)
Project Structure
mcp-customer-support-agent/
├── mcp_agent/
│ ├── agent/
│ │ └── orchestrator.py # Intent rules, routing, response templates
│ ├── api/
│ │ └── app.py # FastAPI endpoints
│ ├── config/
│ │ ├── settings.py # Configuration (environment variables)
│ │ ├── logging_config.py # Logging
│ │ └── tracking.py # In-memory metrics
│ ├── database/
│ │ ├── models.py # SQLAlchemy models
│ │ └── seed.py # Deterministic sample data
│ ├── mcp_servers/
│ │ ├── database_server.py # DB operations + tool schema
│ │ ├── rag_server.py # Retrieval operations + tool schema
│ │ └── actions_server.py # Refund / cancel / notification + tool schema
│ └── rag/
│ └── knowledge_base.py # ChromaDB + sentence-transformers
├── data/
│ └── knowledge/ # 3 markdown policy files (FAQ, refund, shipping)
├── tests/ # 80 tests (79 unit + 1 integration)
├── .github/workflows/ci.yml # ruff + unit tests (coverage threshold 80%)
├── .env.example
├── Dockerfile
├── docker-compose.yml
├── LICENSE
├── Makefile
└── run.py
data/database/ and data/chroma/ are generated by run.py seed / run.py index and are not versioned.
Testing
# Unit tests only (no ChromaDB, no network; what CI runs)
pytest tests/ -v -m "not integration" --cov=mcp_agent --cov-report=term-missing
# All tests, including the real ChromaDB + embedding-model test
pytest tests/ -v
Unit tests mock SQLAlchemy sessions, ChromaDB and the embedding function, except tests/test_seed.py,
which runs the seed and the database/actions servers against a temporary SQLite file. Measured locally
with the command above: 79 unit tests passed, 92% statement coverage of mcp_agent (the uncovered part
is mainly knowledge_base.py, which is only exercised by the integration test).
There is no evaluation of intent-classification accuracy or retrieval quality: no labelled dataset is included and no evaluation results are published.
Tech Stack
| Component | Technology |
|---|---|
| Agent | Rule-based orchestrator (keywords + regex) |
| Database | SQLite + SQLAlchemy 2 |
| Retrieval | ChromaDB + sentence-transformers (all-MiniLM-L6-v2) |
| API | FastAPI + Uvicorn |
| Monitoring | In-memory metrics + file logging |
| Quality | pytest, pytest-cov, ruff, GitHub Actions |
Limitations and scope
- No language model. Intent classification is a fixed keyword list with a priority order (refund > cancel > track > check order > general). A message such as "cancel the refund" is classified as a refund request; no accuracy has been measured.
- MCP in name only. Tool schemas are declared in MCP / function-calling format but nothing consumes
them: there is no MCP server process, transport or client. Wiring the classes to the
mcpSDK is future work. - Retrieval without generation. General questions return the raw text of the two closest policy sections. Retrieval quality has not been evaluated.
- Simulated side effects. Refunds, cancellations and e-mails only change the local SQLite database and an in-memory log. No payment or e-mail provider is integrated.
- Demo data. 15 synthetic orders; the Docker image re-seeds the database on every start.
- Single process, no authentication. One shared SQLAlchemy session, no concurrency control, no auth on the API; error tracking in the metrics is a placeholder (every request is recorded as a success).
- Conversation state is not used.
conversation_historyis stored but no handler reads it.
References
License
MIT - see LICENSE.
Author
tealamenta - AI/ML Engineer
