Skip to main content
Glama
Kusubhavani

AI Sales Query Agent MCP Server

by Kusubhavani
README.md
# AI Sales Query Agent
An AI-powered sales analytics API that converts **natural-language questions into SQL** and executes them against a SQLite sales database through a secure **MCP-style database server**.

The application is built with **FastAPI** and supports both an optional Claude API integration and a deterministic offline SQL planner.

---

# ๐Ÿ—๏ธ Architecture

```text
                         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                         โ”‚       Client        โ”‚
                         โ”‚  Swagger / Browser  โ”‚
                         โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                    โ”‚
                                    โ”‚ POST /query
                                    โ–ผ
                         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                         โ”‚      FastAPI        โ”‚
                         โ”‚       main.py       โ”‚
                         โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                    โ”‚
                                    โ–ผ
                         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                         โ”‚      SQLAgent       โ”‚
                         โ”‚      agent.py       โ”‚
                         โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                    โ”‚
                       โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                       โ”‚                         โ”‚
                       โ–ผ                         โ–ผ
              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”       โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
              โ”‚ ClaudeSQLAgent โ”‚       โ”‚ LocalSQLPlanner  โ”‚
              โ”‚  Claude API    โ”‚       โ”‚   Offline Mode   โ”‚
              โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜       โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                       โ”‚                         โ”‚
                       โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                    โ”‚
                                    โ”‚ Generated SQL
                                    โ–ผ
                         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                         โ”‚      MCPServer      โ”‚
                         โ”‚   mcp_server.py     โ”‚
                         โ”‚                     โ”‚
                         โ”‚ list_tables()       โ”‚
                         โ”‚ describe_schema()   โ”‚
                         โ”‚ execute_query()     โ”‚
                         โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                    โ”‚
                             Read-only SQL
                                    โ”‚
                                    โ–ผ
                         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                         โ”‚   SQLite Database   โ”‚
                         โ”‚    data/sales.db    โ”‚
                         โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
```

---

# ๐Ÿ”„ How It Works

The application follows this flow:

```text
User Question
     โ”‚
     โ–ผ
POST /query
     โ”‚
     โ–ผ
SQLAgent
     โ”‚
     โ”œโ”€โ”€ Claude API
     โ”‚
     โ””โ”€โ”€ LocalSQLPlanner
     โ”‚
     โ–ผ
Generated SQL
     โ”‚
     โ–ผ
MCPServer
     โ”‚
     โ”œโ”€โ”€ Validate SQL
     โ”œโ”€โ”€ Check read-only operation
     โ”œโ”€โ”€ Reject dangerous keywords
     โ””โ”€โ”€ SQLite Authorizer
     โ”‚
     โ–ผ
SQLite Database
     โ”‚
     โ–ผ
Query Results
     โ”‚
     โ–ผ
JSON Response
```

---
# ๐Ÿ›ก๏ธ Database Security

The `execute_query()` method uses multiple security checks.

### 1. Only SELECT queries

The server accepts:

```sql
SELECT ...
```

and:

```sql
WITH ... SELECT ...
```

Write operations are rejected.

### 2. Multiple statements are rejected

For example:

```sql
SELECT * FROM customers;
DROP TABLE customers;
```

is rejected.

### 3. Dangerous keywords are blocked

The application checks for operations such as:

```text
INSERT
UPDATE
DELETE
DROP
ALTER
CREATE
PRAGMA
ATTACH
DETACH
```

### 4. SQLite Authorizer

The application also uses SQLite's built-in authorizer callback.

This provides database-level protection against:

* INSERT
* UPDATE
* DELETE
* DROP
* ALTER
* CREATE
* Other restricted database operations

Therefore, SQL execution is protected by both application-level validation and the SQLite engine.

---

# ๐Ÿค– SQL Generation

The application supports two modes.

## Claude Mode

If `ANTHROPIC_API_KEY` is configured:

```text
ANTHROPIC_API_KEY
       โ”‚
       โ–ผ
ClaudeSQLAgent
       โ”‚
       โ–ผ
Claude API
       โ”‚
       โ–ผ
Generated SQL
       โ”‚
       โ–ผ
MCPServer
```

The Claude agent receives the database schema and instructions for generating safe SQL.

---

## Offline Mode

If `ANTHROPIC_API_KEY` is not configured:

```text
User Question
      โ”‚
      โ–ผ
LocalSQLPlanner
      โ”‚
      โ–ผ
Pattern Matching
      โ”‚
      โ–ผ
SQL Query
      โ”‚
      โ–ผ
MCPServer
```

The offline planner supports common sales queries including:

* Customer counts
* Revenue calculations
* Category revenue
* Top-N products
* Regional aggregations
* Group-by queries
* Products that were never ordered
* Basic filtering
* Aggregations

This means the project can run without an API key or internet connection.

---

# ๐Ÿ“Š Database Schema

The project uses SQLite.

Database location:

```text
data/sales.db
```

# ๐Ÿ“ Project Structure

```text
partnr-sales-agent/
โ”‚
โ”œโ”€โ”€ app/
โ”‚   โ”œโ”€โ”€ __init__.py
โ”‚   โ”œโ”€โ”€ main.py
โ”‚   โ”œโ”€โ”€ agent.py
โ”‚   โ””โ”€โ”€ mcp_server.py
โ”‚
โ”œโ”€โ”€ data/
โ”‚   โ””โ”€โ”€ sales.db
โ”‚
โ”œโ”€โ”€ tests/
โ”‚   โ””โ”€โ”€ test_query.py
โ”‚
โ”œโ”€โ”€ generate_db.py
โ”œโ”€โ”€ evaluator.sh
โ”œโ”€โ”€ Dockerfile
โ”œโ”€โ”€ docker-compose.yml
โ”œโ”€โ”€ requirements.txt
โ”œโ”€โ”€ .env.example
โ”œโ”€โ”€ .gitignore
โ””โ”€โ”€ README.md
```

---

# โš™๏ธ Requirements

### Required

* Python 3.10+
* pip
* SQLite

### Optional

* Docker Desktop
* Docker Compose
* Anthropic API key

---

# ๐Ÿš€ Installation

## Windows PowerShell

### Step 1 โ€” Open the project

### Step 2 โ€” Create virtual environment

```powershell
python -m venv .venv
```

### Step 3 โ€” Activate virtual environment

```powershell
.\.venv\Scripts\Activate.ps1
```
> **Important:** `source .venv/bin/activate` is a Linux/macOS command. Do not use it in Windows PowerShell.

### Step 4 โ€” Upgrade pip

```powershell
python -m pip install --upgrade pip
```

### Step 5 โ€” Install dependencies

```powershell
pip install -r requirements.txt
```

---



# ๐Ÿ—„๏ธ Generate the Database

If `data/sales.db` does not exist, run:

```powershell
python generate_db.py
```

Verify:

```text
data/
โ””โ”€โ”€ sales.db
```

---

# โ–ถ๏ธ Run the Application

Start the FastAPI development server:

```powershell
uvicorn app.main:app --reload
```

You should see:

```text
Uvicorn running on http://127.0.0.1:8000
```

The API is now running at:

```text
http://127.0.0.1:8000
```

---

# ๐Ÿ“– API Documentation

FastAPI automatically generates interactive documentation.

Open:

```text
http://127.0.0.1:8000/docs
```

You can use Swagger UI to test the API without Postman.

Alternative documentation:

```text
http://127.0.0.1:8000/redoc
```

---

# ๐Ÿ”Ž API Usage

## POST `/query`

The endpoint accepts a natural-language question.

### Request

```json
{
  "question": "What is the total number of customers?"
}
```

### Example Response

```json
{
  "sql": "SELECT COUNT(*) AS total_customers FROM customers;",
  "results": [
    {
      "total_customers": 500
    }
  ],
  "chart_data": {
    "labels": [
      "500"
    ],
    "values": [
      500
    ]
  }
}
```

---

# ๐Ÿงช Example Queries

### 1. Total Customers

```text
What is the total number of customers?
```

### 2. Technology Revenue

```text
What is the total revenue from the Technology category?
```

### 3. Top Products

```text
What are the top 5 products by revenue?
```

### 4. Regional Sales

```text
What is the total sales amount by region?
```

### 5. Never Ordered Products

```text
Which products have never been ordered?
```

### 6. Unsupported Question

```text
What is the weather today?
```

The unsupported question should return:

```text
HTTP 400
```

with an explanatory error message.

---

# ๐Ÿงช Testing

Run the complete test suite:

```powershell
pytest tests/ -v
```

The tests cover:

* API endpoint
* Response format
* Customer count
* Revenue queries
* Complex SQL joins
* Unsupported questions
* MCP security
* SQL injection protection
* `list_tables()`
* `describe_schema()`
* Read-only SQL execution

All tests use the offline planner and therefore do not require an API key.

---

# ๐Ÿ“‹ End-to-End Evaluation

The repository contains:

```text
evaluator.sh
```

The script executes predefined questions against the API.

### Git Bash

```bash
chmod +x evaluator.sh
./evaluator.sh
```

### PowerShell

If you are using Git Bash on Windows:

```bash
./evaluator.sh
```

You can also test all queries manually using:

```text
http://127.0.0.1:8000/docs
```

---

# ๐Ÿณ Docker

Docker can be used instead of installing Python dependencies locally.

## Build and start

```powershell
docker compose up --build -d
```

If your system uses the older Docker Compose command:

```powershell
docker-compose up --build -d
```

Check the containers:

```powershell
docker compose ps
```

View logs:

```powershell
docker compose logs -f api
```

Open:

```text
http://localhost:8000/docs
```

---

## Run Tests in Docker

```powershell
docker compose exec api pytest tests/ -v
```

---

## Stop Docker

```powershell
docker compose down
```

---

# โŒ Error Handling

The application does not guess when a question cannot be answered.

For unsupported questions, the agent raises:

```text
UnanswerableQuestionError
```

The API converts this into:

```text
HTTP 400 Bad Request
```

Example:

```text
Question:
What is the weather today?

Response:
400 Bad Request
```

This prevents unrelated questions from producing meaningless SQL.

---

# ๐Ÿ”’ Security Architecture

The security model follows defense in depth:

```text
Natural Language Question
          โ”‚
          โ–ผ
       SQLAgent
          โ”‚
          โ–ผ
     Generated SQL
          โ”‚
          โ–ผ
   SQL Validation
          โ”‚
          โ”œโ”€โ”€ Single statement
          โ”œโ”€โ”€ SELECT / WITH only
          โ”œโ”€โ”€ Forbidden keyword check
          โ”‚
          โ–ผ
   SQLite Authorizer
          โ”‚
          โ”œโ”€โ”€ Reject writes
          โ”œโ”€โ”€ Reject DDL
          โ””โ”€โ”€ Reject restricted actions
          โ”‚
          โ–ผ
      SQLite DB
```

The important principle is:

> **The AI agent generates SQL, but it never directly controls the database.**

---

# ๐Ÿงฉ Components

## `app/main.py`

Responsible for:

* FastAPI application
* `/query` endpoint
* Request validation
* Agent orchestration
* Response formatting
* Error handling

## `app/agent.py`

Responsible for:

* Natural-language processing
* SQL generation
* Claude integration
* Offline SQL planning
* Unsupported-question detection

## `app/mcp_server.py`

Responsible for:

* Database connection
* Table listing
* Schema inspection
* SQL validation
* Read-only enforcement
* SQLite authorizer

## `generate_db.py`

Responsible for:

* Creating the SQLite database
* Generating customers
* Generating orders
* Generating products
* Generating order items

## `tests/test_query.py`

Responsible for:

* API tests
* SQL tests
* Security tests
* Schema tests

---

# ๐Ÿ› ๏ธ Technology Stack

| Technology       | Purpose                    |
| ---------------- | -------------------------- |
| Python           | Application development    |
| FastAPI          | REST API                   |
| SQLite           | Database                   |
| Anthropic Claude | Optional AI SQL generation |
| MCP-style Server | Secure database gateway    |
| Pydantic         | Data validation            |
| Pytest           | Testing                    |
| Docker           | Containerization           |
| Docker Compose   | Container orchestration    |

---

# ๐Ÿ“ˆ Example End-to-End Flow

For the question:

```text
What is the total revenue from the Technology category?
```

The application performs:

```text
1. User sends question
          โ†“
2. FastAPI receives /query
          โ†“
3. SQLAgent analyzes question
          โ†“
4. SQL is generated
          โ†“
5. MCPServer validates SQL
          โ†“
6. SQLite authorizer checks operation
          โ†“
7. Query executes
          โ†“
8. Results are returned
          โ†“
9. Chart data is generated
```

Example SQL:

```sql
SELECT
    SUM(p.price * oi.quantity) AS total_revenue
FROM order_items oi
JOIN products p
    ON p.id = oi.product_id
WHERE p.category = 'Technology';
```

---

# ๐Ÿ”ฎ Future Improvements

Potential improvements include:

* Ollama/local LLM integration
* Additional LLM providers
* PostgreSQL support
* Authentication
* Rate limiting
* Query caching
* Conversation history
* Advanced SQL generation
* Automatic chart selection
* Frontend dashboard
* Production logging
* Monitoring
* Streaming responses

---

# ๐ŸŽฏ Project Objective

The main objective of this project is to demonstrate a secure architecture for querying structured sales data using natural language.

Instead of manually writing SQL:

```text
"What is the total revenue from Technology?"
```

the user can ask a natural-language question and receive a structured result.

```text
User
 โ”‚
 โ–ผ
FastAPI
 โ”‚
 โ–ผ
SQL Agent
 โ”‚
 โ–ผ
MCPServer
 โ”‚
 โ–ผ
SQLite
 โ”‚
 โ–ผ
Sales Result
```

The architecture keeps **AI-generated SQL separate from database execution**, making the system easier to test, secure, and extend.

---

# ๐Ÿ‘ฉโ€๐Ÿ’ป Running the Project โ€” Quick Start

For an existing local setup, these are the only commands normally required:

```powershell
cd D:\partnr-sales-agent

.\.venv\Scripts\Activate.ps1

uvicorn app.main:app --reload
```

Then open:

```text
http://127.0.0.1:8000/docs
```

Test:

```json
{
  "question": "What is the total number of customers?"
}
```

---

# ๐Ÿ“„ License

This project is intended for educational, development, and demonstration purposes.