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

データベーススキーマ(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.statusnewprocessingshippedcompletedcancelledに制約されています。products.categoryには現在5つの異なる値があります。外部キー:orders.customer_id → customers.idorder_items.order_id → orders.idorder_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、リレーションシップ、行数);

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

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

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

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

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

MCPツール

get_database_schema()

パラメータなし。正確なテーブル/列名をまだ知らない場合は、まずこれを呼び出してください — 推測しないでください。テーブルごとにrow_countcolumns(名前、SQLite型、not_nulldefault_valueis_primary_key)、primary_keyforeign_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}を返します。JOINWHEREGROUP BYORDER BY、集計関数、サブクエリ、CTEをサポートします。limit1..500にクランプされます(デフォルト100)。offsetを使用して大きな結果をページングします。total_matching_rowstruncatedは、現在のページが結果全体なのか、さらに取得するものがあるのかを呼び出し元に伝えます。エラー(構文エラー、不明なテーブル/列、拒否された書き込み試行)は、生の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アクションのみを許可リストに登録し、それ以外のすべてを拒否します — INSERTUPDATEDELETEDROPALTERCREATEREPLACETRUNCATEATTACHDETACHVACUUMREINDEXPRAGMA、トランザクションなど。これは解析されたステートメントに対して実行されるため、素朴な「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.dbcustomersにはcountry/場所の列がないため、「ドイツの顧客」のような質問はこのデータからは答えられません — スキーマツールがこれを明らかにし、サーバーが列をでっち上げることはありません。

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

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

-
license - not tested
Not graded
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

  • Run AI customer support from your terminal: conversations, knowledge base, and chat widget.

  • Read-only bank access for your AI agent. Connects Claude, ChatGPT, Cursor, Gemini, Codex.

  • Query PostgreSQL databases in plain English — LLM-generated, safety-validated SQL.

View all MCP Connectors

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/AndrewKonst/shop-mcp'

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