shop-mcp

shop-mcp

Enables AI agents to read-only analyze a SQLite e-commerce database, exploring schema and running analytical SQL queries over stdio.

Category
访问服务器

README

shop-mcp

A local, read-only MCP (Model Context Protocol) server that lets an AI agent analyze a SQLite e-commerce database (shop.db) — customers, products, orders and order items — over the stdio transport. No HTTP server, no separate database process: the server opens shop.db directly and exposes two small, general-purpose tools an agent can use to explore the schema and run its own analytical SQL.

Built with the official Python MCP SDK (mcp on PyPI).

Project structure

mcp-sql/
├── server.py                    # the MCP server (stdio transport)
├── shop.db                      # SQLite database (not modified by this project)
├── requirements.txt
├── .env.example
├── mcp-config.example.json
├── tests/
│   ├── conftest.py
│   ├── test_server.py           # unit tests (call tool functions directly)
│   └── test_stdio_integration.py# protocol-level test (spawns server.py over stdio)
└── README.md

Database schema (as actually found in shop.db)

customers(id PK, first_name, last_name, email UNIQUE, phone, created_at)
products(id PK, name, category, price, stock_quantity, created_at)
orders(id PK, customer_id -> customers.id, order_date, status, total_amount)
order_items(id PK, order_id -> orders.id, product_id -> products.id, quantity, unit_price)

orders.status is constrained to: new, processing, shipped, completed, cancelled. products.category currently has 5 distinct values. Foreign keys: orders.customer_id → customers.id, order_items.order_id → orders.id, order_items.product_id → products.id. The server derives all of this from the live database at query time (via sqlite_master / PRAGMA table_info / PRAGMA foreign_key_list) — nothing here is hard-coded, so if shop.db is swapped for another file with a different schema, get_database_schema will reflect that automatically.

Known data characteristics of the provided shop.db: customers has no country column, so "customers from Germany" style questions cannot be answered — the schema tool makes this discoverable, and query_database returns a clear no such column: country error instead of guessing. All 750 orders currently in the database are dated in 2026 (none in 2025), so a "revenue in 2025" query correctly returns 0/null, not an error.

Installation

cd mcp-sql
python3 -m venv .venv
source .venv/bin/activate        # on Windows: .venv\Scripts\activate
pip install -r requirements.txt

Configuration

The database path is never hard-coded in the source. It is resolved as:

  1. the SHOP_DB_PATH environment variable, if set;
  2. otherwise shop.db next to server.py.

Copy .env.example to .env and edit it if you want to point the server at a different database file (you'll need to load it into your shell/agent launcher yourself, e.g. export $(cat .env | xargs), or just set SHOP_DB_PATH directly):

cp .env.example .env
# edit .env, or simply:
export SHOP_DB_PATH=/absolute/path/to/shop.db

Run

source .venv/bin/activate
python server.py

The process speaks MCP over stdio and waits for a client — it will look "stuck" with no output, which is expected: connect an MCP client (an AI agent, or mcp-inspector, see below) rather than running it standalone in a terminal.

Quick manual check with the official MCP Inspector (no install needed):

npx @modelcontextprotocol/inspector --cli .venv/bin/python server.py --method tools/list

Connect to an AI agent

Most MCP-compatible clients (Claude Desktop, Claude Code, etc.) read a JSON config block like mcp-config.example.json:

{
  "mcpServers": {
    "shop-mcp": {
      "command": "/absolute/path/to/mcp-sql/.venv/bin/python",
      "args": ["/absolute/path/to/mcp-sql/server.py"],
      "env": {
        "SHOP_DB_PATH": "/absolute/path/to/mcp-sql/shop.db"
      }
    }
  }
}

Notes:

  • Use the absolute path to the venv's Python interpreter (as above) so the mcp package is found without activating the venv manually; using a bare python3 also works if mcp is installed in whatever environment that resolves to.
  • SHOP_DB_PATH is optional — omit it to use the bundled shop.db.
  • Absolute paths belong in this configuration file, supplied by whoever connects the server — never inside server.py itself.
  • Client-specific placement of this block varies (e.g. Claude Desktop uses claude_desktop_config.json with the same mcpServers shape; other clients may want just the inner {"command": ..., "args": ..., "env": ...} object). Check your client's docs for where the file lives.

Testing

source .venv/bin/activate
python -m pytest tests/ -v

This runs 48 tests, including:

  • schema discovery (tables, columns, PK/FK, relationships, row counts);
  • SELECT, JOIN, WHERE, GROUP BY, ORDER BY, aggregates (COUNT/SUM/AVG/MIN/MAX), subqueries, a safe WITH ... SELECT CTE, and date filtering (strftime);
  • row-limit clamping and offset-based pagination;
  • friendly error handling for invalid SQL, unknown tables/columns, an empty query, and a missing database file;
  • read-only safety: every statement type listed in the assignment (DELETE, UPDATE, DROP, CREATE, INSERT, plus ALTER, REPLACE, TRUNCATE, ATTACH, DETACH, VACUUM, REINDEX, a destructive PRAGMA, a stacked SELECT 1; DROP TABLE ..., and a WITH x AS (...) DELETE ... CTE-disguised delete) is rejected, and the database file's row counts and SHA-256 hash are asserted unchanged afterwards;
  • tests/test_stdio_integration.py launches server.py as a real subprocess and drives it through the actual MCP client SDK over stdio (initializelist_toolscall_tool), rather than calling Python functions directly — this is the same path a real agent uses.

MCP tools

get_database_schema()

No parameters. Call this first whenever you don't already know the exact table/column names — don't guess them. Returns, per table: row_count, columns (name, SQLite type, not_null, default_value, is_primary_key), primary_key, foreign_keys (column, referenced table/column, ON DELETE/ON UPDATE), and a few sample_rows so the agent can see real date formats, status values, price magnitudes, etc. A top-level relationships list gives table.column -> other_table.column strings derived from the live foreign keys.

query_database(sql, limit=100, offset=0)

Runs one read-only SQL statement (SELECT, or WITH ... SELECT) and returns {columns, rows, row_count, limit, offset, truncated, total_matching_rows}. Supports JOIN, WHERE, GROUP BY, ORDER BY, aggregate functions, subqueries, and CTEs. limit is clamped to 1..500 (default 100); use offset to page through larger results. total_matching_rows and truncated tell the caller whether the current page is the whole result or there is more to fetch. Errors (bad syntax, unknown table/column, or a rejected write attempt) are raised as a short, specific message — never a raw Python traceback.

Security: how read-only is enforced

The assignment explicitly asks not to rely on a single regex/keyword check, so this server layers four independent defenses — verified in tests/test_server.py:

  1. OS-level read-only file handle. The SQLite file is opened with the URI file:<path>?mode=ro. SQLite itself then refuses any write (OperationalError: attempt to write a readonly database) no matter what SQL is executed — this holds even if every check below has a bug.
  2. PRAGMA query_only = ON is set on every connection as a second, independent SQLite-level guard against writes.
  3. A sqlite3 authorizer callback (Connection.set_authorizer) allow-lists only the SELECT / READ / FUNCTION / RECURSIVE actions at the SQLite engine level and denies everything else — INSERT, UPDATE, DELETE, DROP, ALTER, CREATE, REPLACE, TRUNCATE, ATTACH, DETACH, VACUUM, REINDEX, PRAGMA, transactions, etc. This runs on the parsed statement, so it also catches the classic CTE bypass WITH x AS (SELECT 1) DELETE FROM ... that a naive "must start with SELECT" text check would miss.
  4. Statement-shape checks in server.py: the submitted text must start with SELECT/WITH (fast, friendly rejection before touching SQLite), and every query is executed wrapped as SELECT * FROM (<query>) LIMIT :limit OFFSET :offset — a single statement is required for this to parse at all, so a stacked SELECT 1; DROP TABLE customers becomes a plain SQL syntax error rather than two executed statements.

Because layer 1 (mode=ro) is enforced by SQLite/the OS independently of this server's own logic, shop.db cannot be modified through this server even if a bug existed in layers 2-4.

Known limitations

  • customers has no country/location column in the provided shop.db, so questions like "customers from Germany" cannot be answered from this data — the schema tool surfaces this rather than the server inventing a column.
  • All orders in the provided data are dated in 2026; a 2025 revenue query correctly returns 0 rather than an error.
  • total_matching_rows in query_database is computed with a second COUNT(*) wrapping the same query; for very expensive queries this roughly doubles the work. Given the size of this database (hundreds to a few thousand rows per table) this is not a practical concern.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选