shop-db
# MCP-сервер shop-db
Read-only [MCP](https://modelcontextprotocol.io)-сервер (транспорт stdio), который даёт AI-агенту
доступ к SQLite-базе интернет-магазина `shop.db`: клиенты, товары, заказы и позиции заказов.
Построен на официальном [MCP Python SDK](https://pypi.org/project/mcp/) (v2).
## Метаданные разработки
Проект полностью сгенерирован AI coding agent (Claude Code, модель Fable 5) по спецификации [SPEC.md](SPEC.md).
| Метрика | Значение |
|---|---|
| Размер спецификации в токенах | ≈ 1 800 (cl100k_base; 1 316 в o200k_base) |
| Запустился ли с первого раза | Да — сервер поднялся по stdio и прошёл все 8 задач + safety-проверку с первого запуска |
| Количество вспомогательных запросов | 4 (документация MCP SDK через Context7 — 3, проверка версии на PyPI — 1) |
| Общее количество промптов | 6 (спецификация; реальная shop.db + метаданные; перевод README; запуск тестов; сбор результатов; актуализация + публикация) |
| Итоговое количество багов | 0 в коде сервера; 2 мелких во вспомогательных файлах (порядок импортов в тесте, устаревшее имя поля в одноразовом e2e-скрипте), исправлены до коммита |
| Общее количество потраченных токенов | ≈ 365 000: ≈ 175 000 основная сессия + ≈ 190 000 саб-агенты тестового прогона (без учёта самих headless-агентов, проходивших проверку) |
## Инструменты (tools)
| Tool | Назначение |
|---|---|
| `list_tables` | Обзор всех таблиц: число строк, колонки, описание, связи между таблицами. Естественный первый вызов. |
| `describe_table(table_name)` | Полная схема одной таблицы: типы колонок, первичные/внешние ключи, 3 строки-примера. |
| `query(sql, limit=50, offset=0)` | Выполнение одного read-only `SELECT` (или `WITH ... SELECT`). Поддерживаются JOIN и агрегация. Результаты пагинируются: не больше 500 строк за вызов, в ответе `truncated` / `next_offset`. |
## Безопасность
Изменить базу через этот сервер невозможно. Три независимых уровня защиты:
1. **Валидация запроса** — всё, что не является одиночным `SELECT`/`WITH`
(`INSERT`, `UPDATE`, `DELETE`, `DROP`, `ALTER`, `CREATE`, `PRAGMA`, `ATTACH`,
несколько стейтментов подряд, запись, спрятанная за комментарием), отклоняется
с понятным сообщением ещё до выполнения.
2. **Read-only соединение** — файл открывается через SQLite URI с `mode=ro`.
3. **`PRAGMA query_only = ON`** на каждом соединении.
Даже запись, прошедшая валидацию (например, `WITH ... INSERT`), упирается в read-only
на уровне соединения.
Ошибки SQL возвращаются короткими понятными сообщениями с подсказками — без stack traces.
## Установка
Требуется Python 3.10+.
Через [uv](https://docs.astral.sh/uv/) (рекомендуется — при первом запуске всё ставится автоматически):
```bash
uv sync
```
Или через pip:
```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
```
## Конфигурация
Сервер ищет базу в файле `shop.db` **рядом с `server.py`** — выданная в задании база
включена в репозиторий. Чтобы использовать другой файл, задайте переменную окружения
`SHOP_DB_PATH`:
```bash
export SHOP_DB_PATH=/path/to/shop.db
```
`seed_db.py` — вспомогательная утилита, генерирующая демо-базу с похожей схемой
(нужна только для одноразовых данных; `shop.db` она не трогает, если явно не попросить):
```bash
python seed_db.py /tmp/demo.db
```
## Запуск
Сервер общается по stdio — его запускает MCP-клиент, а не пользователь вручную.
Проверить, что он стартует без ошибок:
```bash
uv run python server.py
```
(сервер будет ждать MCP-сообщения на stdin; выход — Ctrl+C)
## Подключение к агенту
### Claude Code
В репозитории лежит [.mcp.json](.mcp.json), поэтому из директории проекта сервер
подхватывается автоматически. Зарегистрировать вручную:
```bash
claude mcp add shop-db -- uv run --directory /absolute/path/to/sqlite-mcp python server.py
```
### Claude Desktop (или любой клиент с JSON-конфигом)
Добавьте в `claude_desktop_config.json` (см. [examples/claude_desktop_config.example.json](examples/claude_desktop_config.example.json)) —
при установке через pip зависимости должны быть поставлены в тот интерпретатор, который указан в конфиге:
```json
{
"mcpServers": {
"shop-db": {
"command": "/absolute/path/to/sqlite-mcp/.venv/bin/python",
"args": ["/absolute/path/to/sqlite-mcp/server.py"]
}
}
}
```
Или через uv (ручная установка не нужна):
```json
{
"mcpServers": {
"shop-db": {
"command": "uv",
"args": ["run", "--directory", "/absolute/path/to/sqlite-mcp", "python", "server.py"]
}
}
}
```
### Docker
```bash
docker build -t shop-db-mcp .
```
```json
{
"mcpServers": {
"shop-db": {
"command": "docker",
"args": ["run", "-i", "--rm", "shop-db-mcp"]
}
}
}
```
## Примеры вопросов, на которые отвечает агент
- Show me all available tables and explain what information each table contains.
- How many customers are from Germany?
- Which country has the most customers?
- Who is the customer who spent the most money?
- What are the top 5 best-selling products?
- What are the top 3 product categories by revenue?
- How much revenue did we generate in 2025?
- Which customer placed the most orders?
Деструктивные запросы («Delete all cancelled orders») сервер отклоняет.
Примечание про выданные данные: в `customers` нет колонки страны (местоположение
можно вывести только из телефонных кодов — все номера +7 — или доменов почты),
а все заказы датированы февралём–августом 2026. Инструменты схемы дают агенту
всё необходимое, чтобы это обнаружить и ответить честно.
## Результаты проверки через реального агента
Все задачи спецификации прогнаны через реальных headless-агентов
(`claude -p "<вопрос>" --mcp-config .mcp.json`, модель Sonnet), у которых были
доступны **только** три MCP-инструмента сервера — без Bash и файлового доступа.
Ответы сверялись с эталонами, посчитанными напрямую из SQLite. **Итог: 9/9.**
| # | Проверка | Вердикт | Комментарий |
|---|---|---|---|
| 1 | Обзор таблиц | ✅ | Все 4 таблицы, число строк, колонки и связи — одним вызовом `list_tables` |
| 2 | Клиенты из Германии | ✅ | Честный ответ: в базе нет поля страны, определить невозможно |
| 3 | Страна с макс. клиентов | ✅ | Агент проверил телефоны SQL-запросом: все 150 номеров +7 → Россия |
| 4 | Самый платящий клиент | ✅ | Имя, email и сумма (701 780 без отменённых) — точно по эталону |
| 5 | Топ-5 товаров | ✅ | Название, штуки и revenue совпали с эталоном до рубля |
| 6 | Топ-3 категории по выручке | ✅ | 17 060 760 / 5 506 570 / 3 085 470 (без отменённых) — точное совпадение |
| 7 | Выручка за 2025 | ✅ | 0 — агент выяснил, что все заказы датированы 2026 годом, и не стал выдумывать |
| 8 | Клиент с макс. заказов | ✅ | София Яковлев, 16 заказов |
| 9 | Safety: «Delete all cancelled orders» | ✅ | Сервер отверг DELETE с read-only сообщением; SHA-хэш базы не изменился, 102 отменённых заказа на месте |
Наблюдение из транскриптов: агентам почти всегда хватало `list_tables` + одного
агрегирующего SQL-запроса — описания таблиц в ответах инструментов (включая
подсказку про отсутствие страны и формулу revenue) сработали как задумано.
## Схема базы данных
```
customers ──< orders ──< order_items >── products
```
- **customers** (150 строк) — id, first_name, last_name, email, phone, created_at
- **products** (50 строк) — id, name, category, price, stock_quantity, created_at
- **orders** (750 строк) — id, customer_id → customers, order_date, status (new/processing/shipped/completed/cancelled), total_amount
- **order_items** (1900 строк) — id, order_id → orders, product_id → products, quantity, unit_price
## Тесты
```bash
uv run pytest
```
36 тестов покрывают все три инструмента, пагинацию, обработку ошибок, read-only
гарантии (включая мульти-стейтменты и запись, замаскированную комментарием)
и генератор демо-данных.
## Структура проекта
```
server.py # MCP-сервер (3 инструмента, read-only защита)
shop.db # выданная в задании база данных
SPEC.md # спецификация, по которой сгенерирован проект
seed_db.py # детерминированный генератор демо-базы (dev-утилита)
tests/ # тесты pytest
.mcp.json # конфиг проекта для Claude Code
examples/ # пример конфига для Claude Desktop
Dockerfile # опциональный запуск в контейнере
```
TDQS
Scored across 3 tools
Each tool has a clearly distinct purpose: list_tables discovers the database structure, describe_table provides detailed schema for a single table, and query executes read-only SQL. There is no overlap or ambiguity between them, making misselection unlikely.
The names follow an imperative style with clear verbs (list, describe, query), and two use the verb_noun pattern. 'query' deviates slightly as a single verb, but the overall convention is predictable and readable.
Three tools is at the low end of the typical range, but it is appropriate for a focused read-only database server. Each tool serves a necessary step in the workflow (discover, inspect, query), so the count feels reasonable rather than thin.
For a read-only SQL interface, the tool surface is complete: it covers table discovery, schema inspection, and arbitrary query execution with pagination. There are no obvious gaps for the stated purpose, and the tools work together to avoid dead ends.