Skip to main content
Glama
stalexsm

shop-mcp

by stalexsm

shop-mcp — Read-Only SQLite MCP Server

MCP-сервер на Python, предоставляющий AI-агенту (например, Pi) безопасный read-only доступ к базе данных SQLite shop.db через stdio.

Агент самостоятельно исследует схему БД, пишет SQL-запросы и решает аналитические задачи. Сервер не содержит готовых ответов — только инструменты для исследования и выполнения read-only запросов.

AI Agent (Pi)
        │  stdio
        ▼
┌────────────────────┐
│     MCP Server     │   list_tables / describe_table / read_query
└─────────┬──────────┘
          ▼
   SQL validation            ← только один SELECT / WITH ... SELECT
          ▼
   read-only guard           ← connection authorizer
          ▼
   SQLite (mode=ro)          ← файл физически невозможно изменить

1. Requirements

  • Python 3.13+

  • uv

  • Файл базы данных shop.db (уже находится в корне проекта)

2. Installation

uv sync

uv создаст виртуальное окружение и установит зависимости. Вручную создавать venv не нужно.

3. Database configuration

Путь к базе не захардкожен и настраивается через переменную окружения.

Вариант A — переменная окружения (абсолютный путь):

export SHOP_DB_PATH=/absolute/path/to/shop.db
export MAX_RESULT_ROWS=1000   # опционально, default 1000

Вариант B — без настройки (fallback): если SHOP_DB_PATH не задана, сервер использует shop.db из корня проекта.

Допустимо также скопировать .env.example в .env и указать значения там (сервер читает .env из корня проекта; переменные окружения имеют приоритет):

cp .env.example .env

4. Run MCP locally

uv run python -m shop_mcp.server

Сервер работает через stdio и ожидает MCP-протокол на stdin/stdout — отдельно его запускать не нужно, его запускает сам клиент (Pi). Ручной запуск выше полезен только для отладки.

Некорректная конфигурация (например, отсутствует файл БД) завершает процесс с понятным сообщением в stderr.

5. Connect MCP to Pi

Pi подключает MCP-серверы через пакет pi-mcp-adapter и читает конфигурацию из .mcp.json в корне проекта. Такой файл уже входит в репозиторий:

{
  "mcpServers": {
    "shop": {
      "command": "uv",
      "args": ["run", "python", "-m", "shop_mcp.server"],
      "cwd": "/Users/stalexsm/projects/shop-mcp"
    }
  }
}

Для другой машины поправьте cwd на абсолютный путь к каталогу проекта (или замените на env с переменной SHOP_DB_PATH):

{
  "mcpServers": {
    "shop": {
      "command": "uv",
      "args": ["run", "python", "-m", "shop_mcp.server"],
      "cwd": "/absolute/path/to/shop-mcp",
      "env": {
        "SHOP_DB_PATH": "/absolute/path/to/shop.db",
        "MAX_RESULT_ROWS": "1000"
      }
    }
  }
}

Запускать отдельный HTTP-сервер или вручную держать python server.py в терминале не требуется: Pi сам стартует процесс по stdio (лениво, при первом обращении к инструментам).

Если адаптер ещё не установлен:

pi install npm:pi-mcp-adapter

Затем перезапустите Pi в каталоге проекта. Инструменты сервера появятся в панели /mcp.

6. Available tools

list_tables

Список таблиц БД с кратким описанием и количеством строк. Отправная точка исследования схемы. SQL не требуется.

describe_table

Структура одной таблицы: колонки (name, type, nullable, primary_key, default) и foreign keys в виде orders.customer_id -> customers.id. Несуществующая таблица даёт понятную ошибку со списком доступных таблиц.

read_query

Выполнение одного read-only SQL-запроса (SELECT или WITH ... SELECT).

Параметры:

  • sql (обязательный) — текст запроса;

  • max_rows (опциональный) — запрошенный лимит строк; серверный hard limit MAX_RESULT_ROWS (по умолчанию 1000) не может быть превышен.

Поддерживается обычная аналитика SQLite: JOIN, LEFT JOIN, GROUP BY, HAVING, ORDER BY, LIMIT/OFFSET, COUNT/SUM/AVG/MIN/MAX, DISTINCT, CASE, CTE.

Результат — структурированный JSON:

{
  "columns": ["name", "revenue"],
  "rows": [["Ноутбук UltraBook 15", 6569270.0]],
  "row_count": 1,
  "truncated": false,
  "execution_time_ms": 0.716
}

truncated: true означает, что из-за лимита возвращена только часть строк — уточните запрос (LIMIT, WHERE, агрегация), не считая данные полными.

7. Security model

Три независимых уровня защиты:

  1. SQL validation — разрешён ровно один statement, начинающийся с SELECT/WITH. Запрещены INSERT, UPDATE, DELETE, REPLACE INTO, DROP, ALTER, CREATE, ATTACH, DETACH, VACUUM, REINDEX, PRAGMA и другие изменяющие операции. Multi-statement запросы (SELECT ...; DELETE ...) отклоняются целиком. Валидатор понимает строковые литералы, комментарии и закавыченные идентификаторы, поэтому 'DELETE' внутри строки не считается нарушением.

  2. Connection authorizer — всё, что не является чтением (SELECT / чтение таблицы / вызов функции), отклоняется на этапе подготовки запроса.

  3. mode=ro — файл SQLite открывается в read-only режиме; даже при обходе первых двух уровней физическая запись невозможна.

Ошибки возвращаются агенту в понятном виде (Database query failed: no such column: foo) — без traceback, путей файловой системы и деталей реализации.

shop.db — read-only source of truth: сервер не изменяет ни содержимое, ни структуру файла. Это зафиксировано тестом целостности (checksum + счётчики строк до/после всех попыток разрушающих операций).

8. Example questions

Задайте эти вопросы агенту Pi — он сам вызовет list_tables, describe_table и read_query:

  • Show me all available tables and explain what information each table contains.

  • 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?

Справка по бизнес-логике (агент выводит это из описаний инструментов, сервер ответов не кодирует):

  • revenue по товарам/категориям считается как SUM(order_items.quantity * order_items.unit_price);

  • заказы со статусом cancelled не учитываются;

  • revenue по годам считается по orders.order_date; если заказов нет — корректный ответ 0.

Вопрос про страны

How many customers are from Germany? — на этот вопрос нельзя достоверно ответить: в таблице customers нет поля country (только first_name, last_name, email, phone, created_at). Сервер отдаёт агенту достоверную информацию о схеме, а агент обязан сообщить, что требуемых данных в БД нет, вместо того чтобы выводить страну из email/телефона или гадать.

9. Testing

uv run pytest

Набор тестов (66):

  • tests/test_database.py — read-only подключение, discovery схемы, foreign keys, закрытие соединений;

  • tests/test_security.py — все запрещённые операции (раздел 24 спецификации), multi-statement, integrity-тест БД;

  • tests/test_tools.py — интеграционные тесты MCP-инструментов через реальную клиентскую сессию (in-memory transport), включая обработку ошибок;

  • tests/test_analytics.py — аналитические сценарии (раздел 27) со сверкой против независимого SQLite-источника, лимиты размера результата.

Тесты не изменяют shop.db (integrity-тест сверяет checksum файла).

10. Troubleshooting

Симптом

Причина и решение

Configuration error: Database file not found

SHOP_DB_PATH указывает на несуществующий файл. Укажите абсолютный путь или положите shop.db в корень проекта.

Инструменты не видны в Pi

Убедитесь, что .mcp.json лежит в корне проекта, cwd указывает на каталог проекта, установлен pi-mcp-adapter (pi install npm:pi-mcp-adapter), и перезапустите Pi.

Multiple SQL statements are not allowed

В одном вызове read_query допускается ровно один statement; разделите запрос на несколько вызовов.

Only read-only queries are allowed

Запрос начинается не с SELECT/WITH либо содержит DML/DDL. Перепишите запрос как SELECT.

Результат неполный (truncated: true)

Сработал лимит строк. Добавьте LIMIT/WHERE/агрегацию или увеличивать его не пытайтесь — hard limit задаёт сервер.

Хочу другой лимит строк

Задайте MAX_RESULT_ROWS в окружении (сервер перезапустится Pi автоматически при следующем старте).

Project layout

shop-mcp/
├── README.md
├── pyproject.toml
├── uv.lock
├── .env.example
├── .gitignore
├── .mcp.json              # конфигурация MCP для Pi
├── shop.db                # read-only source of truth
├── scripts/
│   └── smoke_stdio.py     # ручной smoke-тест через реальный stdio
├── src/shop_mcp/
│   ├── __init__.py
│   ├── server.py          # MCP-инструменты (stdio)
│   ├── database.py        # read-only слой доступа к SQLite
│   ├── security.py        # SQL validation + single-statement guard
│   ├── models.py          # структуры результатов
│   └── config.py          # SHOP_DB_PATH / MAX_RESULT_ROWS
└── tests/
    ├── test_database.py
    ├── test_security.py
    ├── test_tools.py
    └── test_analytics.py

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/stalexsm/shop-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server