客服支持代理

面向电商场景的客服支持代理,通过关键词意图分类和RAG政策检索回答客户问题,并使用工具服务器完成订单查询、退款处理和订单取消。

  • by tealamenta
  • Python
  • 本地部署
  • 更新于
在 GitHub 查看 tealamenta/mcp-customer-support-agent

使用说明

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

  1. Overview
  2. Architecture
  3. Features
  4. Installation
  5. Quick Start
  6. Usage
  7. Tool Servers
  8. Project Structure
  9. Testing
  10. Tech Stack
  11. 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.

See Limitations and scope.


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 product
  • get_customer_orders(customer_email) - All orders of a customer
  • update_order_status(order_id, new_status) - Update status
  • get_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 to refunded (simulated payment)
  • cancel_order(order_id, reason) - Sets status to cancelled
  • send_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 mcp SDK 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_history is stored but no handler reads it.

References

License

MIT - see LICENSE.

Author

tealamenta - AI/ML Engineer