Skip to main content
Glama

shop-mcp

ローカルで動作する読み取り専用のMCP(Model Context Protocol)サーバーです。AIエージェントがSQLiteのeコマースデータベース(shop.db)— 顧客、商品、注文、注文明細 — をstdioトランスポート経由で分析できるようにします。HTTPサーバーも別のデータベースプロセスも不要で、サーバーはshop.dbを直接開き、エージェントがスキーマを調査して独自の分析SQLを実行するための、小さく汎用的な2つのツールを公開します。

公式Python MCP SDK(PyPIのmcp)で構築されています。

プロジェクト構造

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

Related MCP server: shop-db MCP Server

データベーススキーマ(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はnew、processing、shipped、completed、cancelledに制約されています。products.categoryには現在5つの異なる値があります。外部キー:orders.customer_id → customers.id、order_items.order_id → orders.id、order_items.product_id → products.id。サーバーはこれらすべてをクエリ時にライブデータベースから取得します(sqlite_master / PRAGMA table_info / PRAGMA foreign_key_list経由)— ここにハードコードされたものは何もないため、shop.dbが別のスキーマを持つ別のファイルに置き換えられた場合、get_database_schemaは自動的にそれを反映します。

提供されたshop.dbの既知のデータ特性: customersにはcountry列がないため、「ドイツの顧客」のような質問には答えられません — スキーマツールでそれが判明し、query_databaseは推測する代わりに明確なno such column: countryエラーを返します。現在データベースにある750件の注文はすべて2026年の日付です(2025年はありません)。そのため「2025年の売上」クエリはエラーではなく、正しく0/nullを返します。

インストール

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

設定

データベースパスはソースにハードコードされていません。次のように解決されます:

  1. SHOP_DB_PATH環境変数が設定されている場合はそれを使用。

  2. それ以外の場合はserver.pyの隣のshop.db。

サーバーを別のデータベースファイルに向けたい場合は、.env.exampleを.envにコピーして編集します(シェル/エージェントランチャーに自分で読み込む必要があります。例:export $(cat .env | xargs)、またはSHOP_DB_PATHを直接設定します):

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

実行

source .venv/bin/activate
python server.py

プロセスはstdio上でMCPを話し、クライアントを待ちます — 出力がなく「固まっている」ように見えますが、これは想定どおりです。ターミナルで単独で実行するのではなく、MCPクライアント(AIエージェント、またはmcp-inspector、下記参照)を接続してください。

公式MCP Inspectorを使った簡単な手動確認(インストール不要):

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

AIエージェントに接続

ほとんどのMCP互換クライアント(Claude Desktop、Claude Codeなど)は、mcp-config.example.jsonのような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"
      }
    }
  }
}

注意:

  • venvのPythonインタープリタへの絶対パスを使用してください(上記のように)。これにより、venvを手動でアクティベートしなくてもmcpパッケージが見つかります。mcpが解決先の環境にインストールされていれば、素のpython3でも動作します。

  • SHOP_DB_PATHはオプションです — 省略すると同梱のshop.dbが使用されます。

  • 絶対パスはこの設定ファイルに属します。これはサーバーを接続する人が提供するもので、server.py自体には決して入れないでください。

  • このブロックの配置場所はクライアントによって異なります(例:Claude Desktopは同じmcpServers構造のclaude_desktop_config.jsonを使用します。他のクライアントは内側の{"command": ..., "args": ..., "env": ...}オブジェクトだけを必要とする場合があります)。ファイルの場所についてはクライアントのドキュメントを確認してください。

テスト

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

これにより48のテストが実行されます。含まれるもの:

  • スキーマ検出(テーブル、列、PK/FK、リレーションシップ、行数);

  • SELECT、JOIN、WHERE、GROUP BY、ORDER BY、集計(COUNT/SUM/AVG/MIN/MAX)、サブクエリ、安全なWITH ... SELECT CTE、日付フィルタリング(strftime);

  • 行数制限のクランプとオフセットベースのページネーション;

  • 無効なSQL、不明なテーブル/列、空のクエリ、データベースファイルの欠落に対する親切なエラー処理;

  • 読み取り専用の安全性:課題に記載されたすべてのステートメントタイプ(DELETE、UPDATE、DROP、CREATE、INSERT、さらにALTER、REPLACE、TRUNCATE、ATTACH、DETACH、VACUUM、REINDEX、破壊的なPRAGMA、スタックされたSELECT 1; DROP TABLE ...、WITH x AS (...) DELETE ... CTEで偽装された削除)が拒否され、データベースファイルの行数とSHA-256ハッシュがその後も変更されていないことが検証されます;

  • tests/test_stdio_integration.pyはserver.pyを実際のサブプロセスとして起動し、実際のMCPクライアントSDKをstdio経由で駆動します(initialize → list_tools → call_tool)。Python関数を直接呼び出すのではなく、実際のエージェントが使用するのと同じ経路です。

MCPツール

get_database_schema()

パラメータなし。正確なテーブル/列名をまだ知らない場合は、まずこれを呼び出してください — 推測しないでください。テーブルごとにrow_count、columns(名前、SQLite型、not_null、default_value、is_primary_key)、primary_key、foreign_keys(列、参照先テーブル/列、ON DELETE/ON UPDATE)、およびエージェントが実際の日付形式、ステータス値、価格の規模などを確認できるようにするいくつかのsample_rowsを返します。トップレベルのrelationshipsリストは、ライブの外部キーから導出されたtable.column -> other_table.column文字列を提供します。

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

1つの読み取り専用SQLステートメント(SELECT、またはWITH ... SELECT)を実行し、{columns, rows, row_count, limit, offset, truncated, total_matching_rows}を返します。JOIN、WHERE、GROUP BY、ORDER BY、集計関数、サブクエリ、CTEをサポートします。limitは1..500にクランプされます(デフォルト100)。offsetを使用して大きな結果をページングします。total_matching_rowsとtruncatedは、現在のページが結果全体なのか、さらに取得するものがあるのかを呼び出し元に伝えます。エラー(構文エラー、不明なテーブル/列、拒否された書き込み試行)は、生のPythonトレースバックではなく、短く具体的なメッセージとして発生します。

セキュリティ:読み取り専用の強制方法

課題では単一の正規表現/キーワードチェックに依存しないことが明示的に求められているため、このサーバーは4つの独立した防御策を重ねています — tests/test_server.pyで検証済み:

  1. OSレベルの読み取り専用ファイルハンドル。 SQLiteファイルはURI file:<path>?mode=roで開かれます。SQLite自体がその後、実行されるSQLに関係なく、あらゆる書き込みを拒否します(OperationalError: attempt to write a readonly database)— これは以下のすべてのチェックにバグがあったとしても成立します。

  2. PRAGMA query_only = ON がすべての接続で設定され、書き込みに対する2番目の独立したSQLiteレベルのガードとなります。

  3. sqlite3オーソライザコールバック(Connection.set_authorizer)は、SQLiteエンジンレベルでSELECT / READ / FUNCTION / RECURSIVEアクションのみを許可リストに登録し、それ以外のすべてを拒否します — INSERT、UPDATE、DELETE、DROP、ALTER、CREATE、REPLACE、TRUNCATE、ATTACH、DETACH、VACUUM、REINDEX、PRAGMA、トランザクションなど。これは解析されたステートメントに対して実行されるため、素朴な「SELECTで始まる必要がある」テキストチェックでは見逃される古典的なCTEバイパスWITH x AS (SELECT 1) DELETE FROM ...も捕捉します。

  4. server.pyでのステートメント形状チェック:送信されたテキストはSELECT/WITHで始まる必要があり(SQLiteに触れる前の高速で親切な拒否)、すべてのクエリはSELECT * FROM (<query>) LIMIT :limit OFFSET :offsetとしてラップされて実行されます — これが解析されるには単一のステートメントが必要なため、スタックされたSELECT 1; DROP TABLE customersは2つの実行ステートメントではなく、単純なSQL構文エラーになります。

レイヤー1(mode=ro)はSQLite/OSによってこのサーバー自身のロジックとは独立に強制されるため、レイヤー2〜4にバグがあったとしても、このサーバーを通じてshop.dbを変更することはできません。

既知の制限

  • 提供されたshop.dbのcustomersにはcountry/場所の列がないため、「ドイツの顧客」のような質問はこのデータからは答えられません — スキーマツールがこれを明らかにし、サーバーが列をでっち上げることはありません。

  • 提供されたデータのすべての注文は2026年の日付です。2025年の売上クエリはエラーではなく正しく0を返します。

  • query_databaseのtotal_matching_rowsは、同じクエリをラップする2番目のCOUNT(*)で計算されます。非常に高価なクエリの場合、作業がほぼ2倍になります。このデータベースのサイズ(テーブルあたり数百〜数千行)を考えると、これは実際的な問題ではありません。

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    C
    maintenance
    Provides AI agents read-only analytical access to a SQLite database over stdio, with tools for listing tables, describing schemas, and running paginated SQL queries.
    -
  • F
    license
    A
    quality
    B
    maintenance
    Gives AI agents read-only analytical access to an e-commerce SQLite database (customers, orders, order_items, products) via SQL queries, table listing, and schema inspection.
    3
    -
  • F
    license
    A
    quality
    C
    maintenance
    Enables AI agents to analyze an SQLite e-commerce database via secure read-only SQL queries, providing tools for table inspection and analytical requests.
    2
    -
  • F
    license
    Not graded
    quality
    C
    maintenance
    Enables AI agents to connect to a read-only SQLite e-commerce database via stdio, safely executing SELECT queries with schema exploration, sample data, pagination, and self-correcting error messages.
    -