MCP Toolbox for Databasesを試した -- LLMからSQLを安全に実行する仕組み

LLMエージェントにデータベースを触らせたい場面が増えてきた。社内データの自然言語検索、レポートの自動生成、問い合わせボットでのリアルタイム在庫参照。
問題は「LLMに生SQLを自由に実行させるのは怖い」ということだ。プロンプトインジェクションでDROP TABLEされたら洒落にならない。
LLMからDBにアクセスする方法は、ざっくり3つある。
方法1: LLMにSQLを生成させて直接実行する
# 危険なパターン
sql = llm.generate(f"ユーザーの質問: {user_query}\nSQLを生成してください")
cursor.execute(sql) # プロンプトインジェクションのリスク自由度は高いが、安全性がゼロに等しい。本番環境では使えない。
方法2: 事前定義のクエリだけを許可する
ALLOWED_QUERIES = {
'get_user': 'SELECT * FROM users WHERE id = ?',
'list_orders': 'SELECT * FROM orders WHERE user_id = ? LIMIT 100',
}安全だが、対応できる質問が固定される。新しい質問パターンのたびにコード更新が要る。
方法3: MCP Toolbox(間に入るミドルウェア)
MCP Toolbox for Databases(旧称: Gen AI Toolbox for Databases)は、Googleが2025年4月にオープンソースで公開したミドルウェアだ。この2つの中間に位置する。事前に「ツール」としてクエリテンプレートを定義し、LLMはツールを呼ぶ形でデータにアクセスする。パラメータはバインド変数として渡されるので、SQLインジェクションは構造的に防げる。
AIエージェント → (MCP) → Toolbox → (SQL) → データベースGitHub上の googleapis/genai-toolbox リポジトリで開発されている。MCP(Model Context Protocol)に準拠しているので、Claude DesktopやCursorなどのMCPクライアントからそのまま使える。
セットアップ
インストール
Go製のバイナリが提供されている。
# バイナリをダウンロード(Linux/macOS)
export OS=$(uname -s | tr '[:upper:]' '[:lower:]')
export ARCH=$(uname -m)
curl -L "https://github.com/googleapis/genai-toolbox/releases/latest/download/toolbox-${OS}-${ARCH}" -o toolbox
chmod +x toolboxDocker経由でも動く。
docker pull us-central1-docker.pkg.dev/database-toolbox/toolbox/toolbox:latest設定ファイル(tools.yaml)
Toolboxの設定は1つのYAMLファイルで完結する。
sources:
my-postgres:
kind: postgres
host: localhost
port: 5432
database: myapp
user: readonly_user
password: ${DB_PASSWORD}
tools:
get_monthly_sales:
kind: postgres-sql
source: my-postgres
description: "指定月の売上合計を返す"
statement: |
SELECT
DATE_TRUNC('month', order_date) AS month,
SUM(amount) AS total_sales
FROM orders
WHERE DATE_TRUNC('month', order_date) = DATE_TRUNC('month', $1::date)
GROUP BY month
parameters:
- name: target_month
type: string
description: "対象月(YYYY-MM-DD形式)"
search_customers:
kind: postgres-sql
source: my-postgres
description: "名前で顧客を検索する"
statement: |
SELECT id, name, email, created_at
FROM customers
WHERE name ILIKE '%' || $1 || '%'
LIMIT 20
parameters:
- name: search_name
type: string
description: "検索する顧客名(部分一致)"toolsセクションで定義した各ツールが、LLMから見える「使えるアクション」になる。descriptionフィールドがLLMのツール選択に直接影響するので、何ができるのか、どんなパラメータが必要なのかを明確に書く。
起動
./toolbox --tools-file tools.yaml --port 5000ヘルスチェック:
curl http://localhost:5000/healthz
# {"status":"ok"}エージェントからの接続(Python)
MCP対応のクライアントから接続できる。LangChainの場合:
from toolbox_langchain import ToolboxClient
# Toolboxサーバーに接続
client = ToolboxClient("http://localhost:5000")
# 利用可能なツールを取得
tools = client.load_toolset()
# LangChainのエージェントに渡す
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent
model = ChatOpenAI(model="gpt-4o")
agent = create_react_agent(model, tools)
# エージェントに質問
response = agent.invoke({
"messages": [{"role": "user", "content": "先月の売上を教えて"}]
})エージェントは get_monthly_sales と search_customers をツールとして認識し、質問に応じて適切なツールを選んでパラメータを渡す。SQL自体はToolboxが実行するので、エージェントはSQLを意識しない。
Claude DesktopからMCP経由で使う
claude_desktop_config.json に追加する。
{
"mcpServers": {
"my-database": {
"command": "/path/to/toolbox",
"args": ["--tools-file", "/path/to/tools.yaml", "--mcp"],
"env": {
"DB_PASSWORD": "your-password"
}
}
}
}--mcp フラグでMCPサーバーモードが有効になり、stdioで通信する。「先月の売上は?」と聞けば、Toolbox経由でDBを検索して回答してくれる。
対応データベース
2026年3月時点で以下に対応している。
データベース | ステータス |
|---|---|
PostgreSQL | GA |
MySQL | GA |
Cloud SQL(PostgreSQL/MySQL) | GA |
AlloyDB | GA |
Cloud Spanner | GA |
SQLite | ベータ |
BigQuery | プレビュー |
Google Cloudのマネージドデータベースとの統合が手厚い。Cloud SQLの場合、IAM認証でパスワード管理が不要になる。
sources:
cloud-sql-prod:
kind: cloud-sql-postgres
project: my-gcp-project
region: asia-northeast1
instance: my-instance
database: myapp
# IAM認証を使う場合、user/passwordは不要Cloud Runにデプロイすれば、サーバーレスで運用できる。
gcloud run deploy toolbox-server \
--source . \
--set-env-vars "TOOLS_FILE=tools.yaml" \
--region asia-northeast1セキュリティ面で評価できる点
パラメータ化クエリの強制
tools.yamlのSQL文では、パラメータがバインド変数($1, $2)として処理される。LLMの出力がSQL文の一部になることはない。SQLインジェクションに対する構造的な防御だ。
接続プール管理
各データソースへの接続プールがToolbox側で管理される。大量のリクエストが来ても、DBへの同時接続数はプール設定で制御できる。
sources:
my-postgres:
kind: postgres
host: localhost
port: 5432
database: myapp
user: readonly_user
password: ${DB_PASSWORD}
pool:
max_connections: 10
max_idle_time: 300s読み取り専用ユーザーの推奨
SELECT権限だけ持つユーザーを作成しておけば、万が一ツール定義にミスがあっても、データの変更や削除は起きない。
CREATE USER readonly_user WITH PASSWORD 'secure_password';
GRANT CONNECT ON DATABASE myapp TO readonly_user;
GRANT USAGE ON SCHEMA public TO readonly_user;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly_user;他のアプローチとの比較
方法 | 安全性 | 柔軟性 | セットアップ |
|---|---|---|---|
LLM生SQL直接実行 | 低 | 高 | 簡単 |
固定クエリのAPI化 | 高 | 低 | 中程度 |
MCP Toolbox | 高 | 中 | 中程度 |
Hasura / PostgREST | 高 | 中 | やや複雑 |
HasuraやPostgRESTもDB APIを提供するが、MCP準拠でLLMのツール呼び出しにネイティブ対応している点がToolboxの差別化要素になる。
使ってみて気づいたこと
tools.yamlの設計がすべて
ツール定義の粒度が粗すぎると、LLMが適切なツールを選べない。細かすぎると、ツール数が増えて選択精度が落ちる。1テーブルに対して2〜3個のツール(一覧取得、条件検索、詳細取得)を定義するのがちょうどいい。
descriptionの書き方が精度に直結する
LLMはツールのdescriptionを読んでどのツールを呼ぶか判断する。「商品を検索する」だけだと、「在庫を確認したい」という質問でこのツールが選ばれない。「商品を名前・価格帯で検索する。在庫確認にも使える。」と書くと選択精度が上がる。
起動が速い
Go製のシングルバイナリなので起動は一瞬。Docker不要で動く点も、開発環境のセットアップが楽でいい。
向かないケース
tools.yamlに定義したクエリしか実行できないので、「自由にデータを探索させたい」ユースケースには向かない。探索的な分析にはText-to-SQLアプローチのほうが適切だ。
始めるなら
- PostgreSQLかMySQLが手元にあるなら、バイナリをダウンロードして
tools.yamlを書く - 2〜3個のツールを定義してToolboxを起動する
- Claude DesktopのMCP設定に追加して、自然言語でクエリしてみる
- 動作を確認したら、ツール定義を増やしていく
DBとLLMをつなぐ「安全な橋」が必要な場面で、MCP Toolboxは堅実な選択肢だ。



