Skip to main content
Glama
skvertl

SQLite Shop MCP Server

by skvertl
README.md
# SQLite Shop MCP Server 🛍️

Безопасный, высокопроизводительный сервер **MCP (Model Context Protocol)** на Python для подключения AI-агентов (Claude Desktop, Cursor, Antigravity, Gemini CLI) к реляционной базе данных интернет-магазина (`shop.db`).

Сервер работает локально через стандартный ввод/вывод (**stdio**), реализует **двухуровневую защиту от изменений (строгий Read-Only)**, поддерживает автопагинацию, понятную обработку ошибок для самоисправления агентов и сопровождается 100% покрытием тестами.

---

## 🌟 Ключевые возможности

1. **Многоуровневая безопасность (Strict Read-Only)**:
   - **Физический уровень (SQLite Engine)**: база открывается через URI `file:shop.db?mode=ro`. Любая попытка записи физически блокируется C-библиотекой SQLite (`OperationalError: attempt to write a readonly database`).
   - **Лексический уровень (AST & Token Validator)**: запросы анализируются до передачи в базу. Разрешены только `SELECT`, `WITH` (CTE) и `EXPLAIN`. Любые деструктивные операции (`INSERT`, `UPDATE`, `DELETE`, `DROP`, `ALTER`, `CREATE`, `ATTACH`, `PRAGMA writable`) и цепочки запросов через точку с запятой немедленно отклоняются.
2. **Умный дизайн инструментов (4 Tools)**:
   - `get_database_schema`: полный каталог всех таблиц, типов, первичных/внешних ключей, количества строк и предметных подсказок.
   - `describe_table`: подробная схема конкретной таблицы.
   - `get_sample_data`: предварительный просмотр записей таблицы без написания SQL.
   - `execute_query`: безопасное выполнение произвольного SQL с автоматической пагинацией (`page`, `page_size`), защитой от переполнения контекста (до 1000 строк) и замером времени выполнения.
3. **Дружелюбная обработка ошибок (Self-Correction)**:
   - Никаких «сырых» питоновских стектрейсов наружу.
   - При ошибке обращения к несуществующей колонке сервер подсказывает список доступных колонок в таблице, позволяя модели мгновенно самоисправиться.
4. **Портативность**:
   - Никаких захардкоженных абсолютных путей. Путь определяется автоматически относительно проекта либо через переменную окружения `SHOP_DB_PATH`.
5. **Тестирование и Docker**:
   - 51 автотест `pytest` (безопасность, база данных, интеграция, все 8 задач из ТЗ).
   - Готовые `Dockerfile` и `docker-compose.yml`.

---

## 🏗️ Архитектура

```
[ AI Agent: Claude / Cursor / Antigravity ]
                   │  (stdio JSON-RPC)
                   ▼
           [ server.py ] (MCPServer stdio transport)
                   │
     ┌─────────────┴─────────────┐
     ▼                           ▼
[ src/security.py ]       [ src/db.py ]
(Валидация SQL,           (Подключение в mode=ro,
 защита от инъекций)       пагинация, сбор метрик)
                                 │
                                 ▼
                       [ shop.db (mode=ro) ]
```

### Схема базы данных `shop.db`

```
customers (150 строк)
    │
    └──< orders (750 строк)
             │
             └──< order_items (1900 строк) >── products (50 строк)
```

---

## 🚀 Быстрый старт

### 1. Установка зависимостей (Install)

Требуется Python 3.10+:

```bash
# Клонируйте репозиторий или перейдите в папку проекта
cd HW_MCP

# Установите зависимости
pip install -r requirements.txt
```

### 2. Конфигурация (Configure)

По умолчанию сервер ищет файл `shop.db` в корне проекта. 
При необходимости путь можно переопределить через переменную окружения:

```bash
# Windows (PowerShell)
$env:SHOP_DB_PATH = "C:\path\to\shop.db"

# Linux / macOS
export SHOP_DB_PATH="/path/to/shop.db"
```

### 3. Запуск сервера (Run)

Сервер запускается в режиме stdio:

```bash
python server.py
```

---

## 🤖 Подключение к AI-агентам (Connect to Agent)

### Claude Desktop

Добавьте конфигурацию в файл настроек Claude Desktop:
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`

```json
{
  "mcpServers": {
    "sqlite-shop": {
      "command": "python",
      "args": [
        "C:\\Users\\user\\OneDrive\\BackToTheFuture\\HW_MCP\\server.py"
      ],
      "env": {
        "PYTHONUNBUFFERED": "1"
      }
    }
  }
}
```

### Cursor

В Cursor перейдите в **Settings > Features > MCP > Add New MCP Server**:
- **Name**: `sqlite-shop`
- **Type**: `command`
- **Command**: `python C:\Users\user\OneDrive\BackToTheFuture\HW_MCP\server.py`

Либо создайте в корне проектного воркспейса файл `.cursor/mcp.json`:

```json
{
  "mcpServers": {
    "sqlite-shop": {
      "command": "python",
      "args": ["server.py"]
    }
  }
}
```

### Antigravity / Gemini CLI

Добавьте секцию в `mcp_config.json`:

```json
{
  "mcpServers": {
    "sqlite-shop": {
      "command": "python",
      "args": ["server.py"]
    }
  }
}
```

---

## 🛠️ Описание инструментов (MCP Tools)

### 1. `get_database_schema`
Возвращает полную структуру всех таблиц, типы данных колонок, первичные и внешние ключи, количество строк и пояснительные заметки к данным.

### 2. `describe_table(table_name: str)`
Возвращает детальную схему колонок и ограничений выбранной таблицы (`customers`, `products`, `orders`, `order_items`).

### 3. `get_sample_data(table_name: str, limit: int = 10)`
Возвращает образцы строк из таблицы для предварительного анализа формата данных.

### 4. `execute_query(query: str, page: int = 1, page_size: int = 50)`
Выполняет безопасный SQL-запрос на чтение.
- **Параметры:**
  - `query` *(string, обязательный)*: SQL-запрос (`SELECT`, `WITH ... SELECT`, `EXPLAIN`).
  - `page` *(int, по умолчанию: 1)*: номер страницы.
  - `page_size` *(int, по умолчанию: 50, макс: 1000)*: количество строк на странице.
- **Формат ответа:**
  ```json
  {
    "rows": [
      { "id": 1, "first_name": "Арина", "email": "..." }
    ],
    "page": 1,
    "page_size": 50,
    "total_rows_in_page": 50,
    "has_more": true,
    "execution_time_ms": 1.24
  }
  ```

---

## 📊 Решение 8 контрольных задач из ТЗ

Все запросы проверены на реальных данных `shop.db`:

| № | Вопрос из ТЗ | SQL-запрос через `execute_query` | Ответ агента |
|---|---|---|---|
| **1** | *Show me all available tables and explain what information each table contains.* | Вызов `get_database_schema()` | 4 таблицы: `customers` (150 клиентов), `products` (50 товаров), `orders` (750 заказов), `order_items` (1900 позиций). |
| **2** | *How many customers are from Germany?* | `SELECT COUNT(*) FROM customers WHERE phone LIKE '+49%'` | **0 клиентов**. (В таблице нет колонки `country`, а все телефоны начинаются на `+7`). |
| **3** | *Which country has the most customers?* | `SELECT SUBSTR(phone, 1, 2) as code, COUNT(*) as c FROM customers GROUP BY code` | **Россия (+7)** — 150 клиентов (100% базы). |
| **4** | *Who is the customer who spent the most money?* | `SELECT c.first_name, c.last_name, c.email, ROUND(SUM(o.total_amount), 2) as spent FROM customers c JOIN orders o ON c.id = o.customer_id WHERE o.status != 'cancelled' GROUP BY c.id ORDER BY spent DESC LIMIT 1` | **Дмитрий Харитонов** (`dmitriy.kharitonov845@mail.ru`) — **701 780.00 руб.** |
| **5** | *What are the top 5 best-selling products?* | `SELECT p.name, SUM(oi.quantity) as qty, ROUND(SUM(oi.quantity * oi.unit_price), 2) as rev FROM products p JOIN order_items oi ON p.id = oi.product_id JOIN orders o ON o.id = oi.order_id WHERE o.status != 'cancelled' GROUP BY p.id ORDER BY qty DESC LIMIT 5` | 1. Эспандер плечевой (93 шт., 110 670 руб.)<br>2. Увлажнитель воздуха AirFresh (92 шт., 394 680 руб.)<br>3. Блендер погружной 800W (84 шт., 267 960 руб.)<br>4. Ботинки кожаные (83 шт., 704 670 руб.)<br>5. Фен профессиональный (83 шт., 455 670 руб.) |
| **6** | *What are the top 3 product categories by revenue?* | `SELECT p.category, ROUND(SUM(oi.quantity * oi.unit_price), 2) as rev FROM products p JOIN order_items oi ON p.id = oi.product_id JOIN orders o ON o.id = oi.order_id WHERE o.status != 'cancelled' GROUP BY p.category ORDER BY rev DESC LIMIT 3` | 1. **Электроника** — 17 060 760 руб.<br>2. **Бытовая техника** — 5 506 570 руб.<br>3. **Одежда и обувь** — 3 085 470 руб. |
| **7** | *How much revenue did we generate in 2025?* | `SELECT COALESCE(ROUND(SUM(total_amount), 2), 0.0) FROM orders WHERE order_date >= '2025-01-01' AND order_date < '2026-01-01' AND status != 'cancelled'` | **0.00 руб.** (Все заказы в магазине созданы в **2026** году: с 17.02.2026 по 22.08.2026). |
| **8** | *Which customer placed the most orders?* | `SELECT c.first_name, c.last_name, c.email, COUNT(o.id) as cnt FROM customers c JOIN orders o ON c.id = o.customer_id GROUP BY c.id ORDER BY cnt DESC LIMIT 1` | **София Яковлев** (`sofiya.yakovlev284@yandex.ru`) — **16 заказов**. |

### Проверка безопасности (Safety Requirement)

Запрос агента:
> *Delete all cancelled orders.*

Ответ MCP-сервера:
```json
{
  "error": true,
  "error_type": "PermissionDenied",
  "message": "PermissionDenied: Modifying or destructive operations are not permitted (read-only server). Statement starts with 'DELETE'."
}
```
База данных остается в полной сохранности.

---

## 🧪 Запуск автоматических тестов

В проекте реализован полный набор тестов на базе `pytest`:
- `tests/test_security.py` — проверка блокировки деструктивных выражений, SQL-инъекций и цепочек запросов.
- `tests/test_db.py` — проверка физического `mode=ro`, схемы, пагинации и подсказок при ошибках.
- `tests/test_server.py` — интеграционные тесты вызова инструментов и валидация всех 8 задач ДЗ.

```bash
pytest tests/ -v
```

Результат:
```text
============================= 51 passed in 0.87s ==============================
```

---

## 🐳 Запуск в Docker

Сборка и запуск контейнера:

```bash
# Сборка образа
docker build -t sqlite-shop-mcp .

# Запуск с монтированием базы
docker run -i --rm -v $(pwd)/shop.db:/app/shop.db:ro sqlite-shop-mcp
```

Или через `docker-compose`:

```bash
docker-compose run --rm sqlite-shop-mcp
```

---

## 📁 Структура репозитория

```text
HW_MCP/
├── .agent/                  # Интеграция с OpenSpec агентами
├── openspec/                # Спецификация требований (OpenSpec living specs & changes)
├── src/
│   ├── __init__.py
│   ├── config.py            # Разрешение путей и настроек SQLite URI
│   ├── security.py          # Валидатор SQL-запросов (Read-Only enforcement)
│   └── db.py                # Слой SQLite (mode=ro, пагинация, сбор схем)
├── tests/
│   ├── test_security.py     # Тесты безопасности SQL
│   ├── test_db.py           # Тесты слоя БД и пагинации
│   └── test_server.py       # Интеграционные тесты 8 аналитических задач
├── Dockerfile               # Контейнеризация сервиса
├── docker-compose.yml
├── mcp_config_example.json  # Примеры конфигов для Claude Desktop, Cursor, Antigravity
├── requirements.txt         # Зависимости Python
├── server.py                # Главная точка входа MCP-сервера
├── shop.db                  # База данных SQLite интернет-магазина
└── README.md                # Полная документация проекта
```

---

## 📜 Лицензия

MIT License.