Skip to main content
Glama
qualiteg

sales-db

by qualiteg
README.md
# 売上データベース MCP サーバー チュートリアル

「先月いちばん売れた GPU はどれ?」と**ふつうの日本語で聞くと、AI が自分で SQL を組み立てて
売上データベースに問い合わせ、集計結果まで答えてくれる**——それを実現する MCP サーバーのサンプルです。

Python + [FastMCP](https://github.com/jlowin/fastmcp) で書かれています。
解説記事は [Qualiteg Blog](https://blog.qualiteg.com/) に掲載しています。

## クイックスタート

```bash
git clone https://github.com/qualiteg/mcp-server-tutorial.git
cd mcp-server-tutorial
python -m pip install -r requirements.txt
python db_setup.py
```

`pip` ではなく `python -m pip` を使ってください。`pip` だと、MCP サーバーを起動する Python と
別の環境に入ってしまうことがあります。

`db_setup.py` は固定シードで架空のデータを生成します(実在の企業・人物・価格とは関係ありません)。
誰が実行しても同じデータになるので、記事の実行結果と一致します。

- customers … 顧客マスター 500 人
- products … 商品マスター 72 種類(PC パーツ 8 カテゴリ)
- sales_transactions … 売上明細 4,000 件(2024-01-01 〜 2026-12-31)

## Claude Code から使う

```bash
claude mcp add sales-db -- python /path/to/mcp-server-tutorial/mcp_server_sales.py
claude mcp list
```

`claude mcp list` に `✔ Connected` と出れば、AI からこのサーバーが見えています。
うまく繋がらないときは、Python とスクリプトを絶対パスで指定してください。仮想環境を使っている場合は、
その中の Python を指す必要があります。

```bash
claude -p "職業別の売上トップ3を教えて"
```

- 「2025年に発売されたハイエンドGPUで、売上が多い順に並べて」
- 「月別の売上推移を出して、いちばん売れた月は?」

**stdio で使う場合、`python mcp_server_sales.py` を手動で起動しておく必要はありません。**
登録したコマンドは Claude Code が子プロセスとして起動します。サーバー単体の動きを確認したいときだけ
手動で起動してください。

## 提供しているツール

| ツール | 役割 |
|---|---|
| `get_database_stats` | テーブル構造・件数・データ期間・カテゴリ別売上の要約を返す |
| `execute_sql_query` | SELECT 文を実行して結果を返す |

業務ごとに「売上集計ツール」「顧客分析ツール」と関数を並べる設計もできますが、**SQL を書けるのは
AI 側**なので、汎用の SQL 実行ツールを 1 本渡すほうが応用が利きます。そのぶん安全側の手当てが
サーバーの責任になるので、役割の違う 3 つの安全弁を設けています。

1. **読み取り専用の接続**: SQLite を `mode=ro` で開く。書き込みは接続そのものが拒む
2. **SQL の補助フィルター**: SELECT 以外の文と書き換え系キーワードを早い段階で落とす
   (構文解析ではないので、これは補助的な位置づけ)
3. **実行時間の上限**: 10 秒を超えるクエリを実時間で中断する(リソース保護のため)

あわせて、AI へ返す行数を 50 行までに制限しています。AI は平気で `SELECT * FROM sales_transactions`
を投げてくるので、返す量はサーバー側で決めます。ただし制限しているのは返却行数だけで、SQLite から
読み込む件数は制限していません。大規模データでは `fetchmany` や SQL 側の LIMIT を検討してください。

安全弁が効いているかは `verify.py` で確認できます。

```bash
python verify.py
```

## HTTP サーバーとして動かす

独立して常駐させ、ネットワーク経由で使う場合は Streamable HTTP で起動します。

```bash
python mcp_server_sales.py --http --port 9904
```

- MCP エンドポイント: `http://127.0.0.1:9904/mcp`
- ヘルスチェック: `http://127.0.0.1:9904/health`

**既定の待受は 127.0.0.1(ローカルのみ)です。** このサンプルには認証がありません。
`--host 0.0.0.0` や LAN 内の IP を指定すると、到達できる相手なら誰でも SQL を実行できてしまいます。
そのまま共有環境や外部へ公開しないでください(指定した場合は起動時に警告が出ます)。

外へ出すなら、HTTPS、認証と認可、接続元の制限、Origin 検証、監査ログを前段に置く前提になります。

## 既存の REST API を MCP 化する(OpenAPI 統合)

MCP サーバーをゼロから書くのではなく、**社内にすでにある REST API を MCP サーバーに変換する**サンプルです。
FastMCP の OpenAPI 統合(`FastMCP.from_openapi`)を使うと、ツールの実装コードを書かずに
OpenAPI スキーマから MCP ツールを自動生成できます。

| ファイル | 役割 |
|---|---|
| `sales_api.py` | 「社内にすでにある REST API」役の FastAPI アプリ(売上DBを返す・ポート 9905) |
| `mcp_sales_api.py` | その API の OpenAPI スキーマを読み込んで MCP サーバー化する |

```bash
# 1. 既存 API 役を起動しておく
python sales_api.py

# 2. 別ターミナルで MCP サーバーとして登録
claude mcp add sales-api -- python /path/to/mcp-server-tutorial/mcp_sales_api.py
claude -p "2025年でいちばん売れたGPUはどれ?"
```

`mcp_sales_api.py` は RouteMap で **GET のエンドポイントだけ**をツールにしています。
書き込み系(POST / DELETE)は AI からは存在ごと見えません。

## Web 版の ChatGPT・Claude から使いたい場合

ChatGPT や Claude は、構成によっては認証なしのリモート MCP にも接続できます。ただし、社内データベースへ
到達するサーバーを無認証で公開する構成は採用できません。解説記事の後編では、MCP Authorization 仕様に
沿った OAuth 認証を使う方法を紹介しています。

## 動作環境

- Python 3.10 以降
- FastMCP 3.4.5 以上 4 未満
- FastAPI・uvicorn・httpx(OpenAPI 統合サンプル `sales_api.py` / `mcp_sales_api.py` で使用)

このサンプルは仕組みを理解するための最小構成です。複数人が同時に使う場面では、同期的な DB 処理を
スレッドへ逃がす、同時実行数を絞る、DB 側のタイムアウトを設けるといった手当てが別に要ります。

## ライセンス

MIT License. サンプルコードなので自由に改変してお使いください。