shop
# Read-only MCP-сервер магазина
Python MCP-сервер по **stdio** для существующей SQLite-базы интернет-магазина (`data/shop.db`). Файл открывается только на чтение; изменяющий SQL отклоняется и не выполняется.
## Установка
Python 3.11+:
```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
```
## Запуск
Из корня проекта (путь к БД по умолчанию — `./data/shop.db`):
```bash
python -m shop_mcp
# или
shop-mcp
```
Другой путь к базе:
```bash
SHOP_DB_PATH=~/my/test-mcp-db/data/shop.db python -m shop_mcp
```
Процесс общается по MCP через stdin/stdout. Не вводите команды в этот терминал, пока к нему не подключён MCP-клиент.
## Конфиг MCP в Cursor
Добавьте запись `mcpServers` (Cursor Settings → MCP или `.cursor/mcp.json` в проекте). Транспорт — **stdio**.
В примерах путь `~/my/test-mcp-db` — этот репозиторий. `SHOP_DB_PATH` сервер раскрывает сам (`~` → домашний каталог). Если Cursor не раскрывает `~` в `command` / `cwd`, укажите там `/Users/<вы>/my/test-mcp-db/...`.
### Локально (venv)
```json
{
"mcpServers": {
"shop": {
"command": "~/my/test-mcp-db/.venv/bin/python",
"args": ["-m", "shop_mcp"],
"cwd": "~/my/test-mcp-db",
"env": {
"SHOP_DB_PATH": "~/my/test-mcp-db/data/shop.db"
}
}
}
}
```
Если после `pip install -e .` удобнее консольный скрипт:
```json
{
"mcpServers": {
"shop": {
"command": "~/my/test-mcp-db/.venv/bin/shop-mcp",
"cwd": "~/my/test-mcp-db",
"env": {
"SHOP_DB_PATH": "~/my/test-mcp-db/data/shop.db"
}
}
}
}
```
### Docker
Собрать образ один раз:
```bash
docker build -t shop-mcp .
```
```json
{
"mcpServers": {
"shop": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-v",
"~/my/test-mcp-db/data/shop.db:/data/shop.db:ro",
"-e",
"SHOP_DB_PATH=/data/shop.db",
"shop-mcp"
]
}
}
}
```
Флаг `-i` обязателен, чтобы stdio оставался подключённым. Монтируйте только `shop.db`; контейнер не должен перезаписывать файл на хосте.
## Инструменты
| Tool | Назначение |
|------|------------|
| `list_tables` | Пользовательские таблицы и краткое описание роли (`sqlite_sequence` не показывается) |
| `describe_table` | Реальные колонки, типы, PK/FK |
| `execute_readonly_sql` | Один `SELECT` / `WITH` / `EXPLAIN`; `limit` (по умолчанию 100, максимум 1000) и `offset` |
| `shop_analytics` | Рейтинги и выручка за год без ручного SQL |
Суммы, штуки и выручка считаются как `order_items.quantity * order_items.unit_price`; заказы со статусом `cancelled` исключаются. Календарный год берётся из `strftime('%Y', orders.order_date)`.
## Безопасность
- SQLite URI `file:<abs>?mode=ro`
- Комментарии вырезаются; цепочки запросов (`SELECT 1; DELETE ...`) отклоняются
- INSERT / UPDATE / DELETE / DROP / ALTER / CREATE / ATTACH / PRAGMA / … → явная ошибка «не разрешено»
- Синтаксические ошибки SQLite и неизвестные колонки возвращаются структурированно; процесс не завершается
## Замечания для оценки
- **У `customers` нет колонки `country`.** Это видно через `describe_table`; географию выдумывать нельзя. `SELECT country FROM customers` вернёт ошибку SQLite.
- **Даты заказов в текущем дампе — 2026 год.** Выручка за **2025** может быть **0**. Это корректный ответ, а не сломанный фильтр.
## Тесты
```bash
pytest
```
Тесты копируют `data/shop.db` во временный файл и проверяют, что хеш копии не меняется после отклонённых записей.
## Пример отработанного запроса
TDQS
Scored across 4 tools
The schema-discovery tools are clearly separated from the querying/analytics tools. There is some overlap between execute_readonly_sql and shop_analytics, but the descriptions explicitly steer agents toward shop_analytics for rankings and revenue, reducing confusion.
Three tools follow a clear verb-first snake_case pattern: list_tables, describe_table, execute_readonly_sql. shop_analytics breaks that pattern as a nouny resource name, though it is still understandable and not chaotic.
Four tools is a well-scoped set for a read-only shop database. Each tool fills a distinct role: schema discovery, table metadata, raw SQL execution, and prebuilt analytics, with no redundancy.
For a read-only database exploration server, the workflow is complete: list the tables, describe one, run arbitrary safe SQL, and get common analytics. There are no missing mutations or write operations because the server is explicitly read-only.