Claude Code × PostgreSQL MCP 導入完全ガイド——スキーマ理解からクエリ最適化まで自然言語でやる
Claude CodeのPostgreSQL MCP設定手順と実践的な使い方。自然言語でSQLを生成・実行・デバッグする方法、スキーマ設計の相談、インデックス最適化の依頼、N+1クエリの発見まで。接続設定とセキュリティ設計も丸ごと解説。
エンジニアのゆとです。
「SQLはかけるけど、最適化とかスキーマ設計になると自信がない」というエンジニアには特に刺さる設定の話をする。
PostgreSQL MCP を Claude Code に繋ぐと、DBに対して自然言語で問い合わせができる。「このスキーマで〇〇するクエリを書いて」だけじゃなくて「このクエリ遅い原因を調べて」「このテーブル設計の問題点を指摘して」みたいなことが全部できる。
PostgreSQL MCP とは
MCP(Model Context Protocol)はAnthropicが策定したプロトコルで、Claude Codeがローカルの各種ツールやサービスと接続する仕組み。
PostgreSQL MCPはその中で、PostgreSQLデータベースへの読み書きを可能にするサーバー。Claude Codeがこのサーバー経由でDBに接続して、スキーマの確認・クエリの実行・データの取得などができる。
公式リポジトリ:@modelcontextprotocol/server-postgres
セットアップ手順
1. PostgreSQL MCP サーバーのインストール
# npm で globally インストール
npm install -g @modelcontextprotocol/server-postgres
# または npx で都度実行(インストール不要)
# 後述の設定で npx 経由で動かすのが楽
2. Claude Code の設定ファイルに追加
~/.claude/settings.json(全プロジェクト共通)または .claude/settings.json(プロジェクト固有)に追加する。
{
"mcpServers": {
"postgres": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-postgres",
"postgresql://localhost:5432/mydb"
]
}
}
}
接続文字列の形式:
postgresql://[user]:[password]@[host]:[port]/[dbname]
3. 環境変数で接続文字列を管理する(推奨)
接続文字列をそのまま settings.json に書くと、Gitに入れた時に認証情報が漏れる。環境変数で管理する。
{
"mcpServers": {
"postgres": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-postgres",
"${PG_CONNECTION_URL}"
],
"env": {
"PG_CONNECTION_URL": "${PG_CONNECTION_URL}"
}
}
}
}
# ~/.zshrc or ~/.bashrc
export PG_CONNECTION_URL="postgresql://myuser:mypassword@localhost:5432/mydb"
1Password MCPと組み合わせると、接続文字列をVaultから取得する構成にできる(後述)。
4. 動作確認
Claude Codeを起動して確認:
claude > DBに接続できているか確認して
claude > テーブル一覧を見せて
正常に繋がっていれば、テーブル一覧とスキーマを返してくる。
実際に使える操作パターン
スキーマの把握と設計相談
claude > このDBのER図を説明して(Mermaid形式で出力して)
claude > usersテーブルとordersテーブルのリレーションを確認して、正規化上の問題点があれば指摘して
実際の出力例:
erDiagram
users {
int id PK
varchar email
timestamp created_at
}
orders {
int id PK
int user_id FK
decimal total_amount
varchar status
timestamp created_at
}
orders }o--|| users : "belongs to"
「このスキーマで注文履歴付きのユーザー一覧を取るクエリを書いて」と続けて指示すると、スキーマを理解した上で最適なJOINを書いてくれる。
クエリ生成と最適化
claude > 過去30日間に5回以上注文したユーザーの一覧を取るクエリを書いて
生成されるクエリ:
SELECT
u.id,
u.email,
COUNT(o.id) as order_count,
SUM(o.total_amount) as total_spent
FROM users u
INNER JOIN orders o ON o.user_id = u.id
WHERE o.created_at >= NOW() - INTERVAL '30 days'
GROUP BY u.id, u.email
HAVING COUNT(o.id) >= 5
ORDER BY order_count DESC;
続けて「このクエリのEXPLAIN ANALYZEを実行して結果を分析して」と頼むと実際にDBに対して実行して分析まで返してくれる。
遅いクエリの診断
claude > このクエリが遅い。原因を調べてインデックスの提案をして
SELECT * FROM orders
WHERE status = 'pending'
AND created_at > '2026-01-01'
ORDER BY created_at DESC;
Claudeがやること:
EXPLAIN ANALYZEを実行してQueryPlanを確認- Sequential Scanになっているかどうかをチェック
- インデックスの提案をSQL文で返す
返ってくる提案:
-- status と created_at の複合インデックス
CREATE INDEX CONCURRENTLY idx_orders_status_created
ON orders (status, created_at DESC);
-- 作成後に再度 EXPLAIN ANALYZE で確認
EXPLAIN ANALYZE
SELECT * FROM orders
WHERE status = 'pending'
AND created_at > '2026-01-01'
ORDER BY created_at DESC;
CONCURRENTLY を自動で付けてくれるのは助かる。本番DBでCONCURRENTLYなしのインデックス作成をやるとテーブルロックになる。
マイグレーション設計
claude > ordersテーブルに shipping_address_id カラムを追加したい。
外部キー制約付きで、既存データへの影響を最小にするマイグレーションを書いて
返ってくる例(Prisma形式):
model orders {
id Int @id @default(autoincrement())
user_id Int
shipping_address_id Int? // NULLableで追加(既存データへの影響なし)
// ...
shipping_address addresses? @relation(fields: [shipping_address_id], references: [id])
}
対応するSQLマイグレーション:
-- Step 1: カラムを NULL 許容で追加(既存データを壊さない)
ALTER TABLE orders
ADD COLUMN shipping_address_id INT REFERENCES addresses(id);
-- Step 2: 既存データを移行(アプリ側の処理)
-- Step 3: NOT NULL 制約を追加(データ移行後)
-- ALTER TABLE orders ALTER COLUMN shipping_address_id SET NOT NULL;
接続先を複数管理する
本番・ステージング・ローカルで接続先を切り替えられるように設定する。
{
"mcpServers": {
"postgres-local": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres", "${PG_LOCAL_URL}"]
},
"postgres-staging": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres", "${PG_STAGING_URL}"]
}
}
}
Claude Codeに指示する時に接続先を明示する:
claude > staging のDBに接続して、users テーブルのレコード数を確認して
本番DBへの接続は最大限慎重に。
デフォルトでは読み取りと書き込みの両方ができる設定になるので、本番に繋ぐ場合はread-only接続を用意することを強く推奨する。
読み取り専用ユーザーの作成
-- 本番DB用に読み取り専用ロールを作成
CREATE ROLE claude_readonly;
GRANT CONNECT ON DATABASE mydb TO claude_readonly;
GRANT USAGE ON SCHEMA public TO claude_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO claude_readonly;
-- ユーザー作成
CREATE USER claude_agent PASSWORD 'secure-password' IN ROLE claude_readonly;
# 本番はread-onlyユーザーの接続文字列を使う
export PG_PROD_URL="postgresql://claude_agent:secure-password@prod-host:5432/mydb"
1Password MCP との組み合わせ
接続文字列を .env や環境変数に平文で書くのが嫌な場合、1Password MCP と組み合わせるとVaultから取得できる。
{
"mcpServers": {
"1password": {
"command": "op",
"args": ["mcp", "serve"]
},
"postgres": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres", "${PG_CONNECTION_URL}"]
}
}
}
Claude Codeが起動時に1Password MCPから接続文字列を取得して、PostgreSQL MCPの接続に使う構成が作れる。
N+1クエリを発見する
ORM(Prisma・Sequelize・TypeORM等)を使っているプロジェクトで頻出する N+1 問題を発見する使い方。
claude > このコードを読んで、N+1クエリが発生している箇所を見つけて、
解決策のコードを書いて
src/services/order-service.ts を確認して
Claudeがやること:
order-service.tsを読む- ループの中でDB呼び出しが発生している箇所を特定
findMany + includeやJOINを使った解決策を提案
実際に発見されるパターン:
// 問題: ループ内でN回クエリが走る
const users = await prisma.user.findMany();
for (const user of users) {
user.orders = await prisma.order.findMany({ // N+1
where: { userId: user.id }
});
}
// 解決: include で一括取得
const users = await prisma.user.findMany({
include: {
orders: true
}
});
CLAUDE.md に PostgreSQL の設計原則を書く
頻繁にDBを触るプロジェクトなら、CLAUDE.md にDB操作のルールを書いておく。
## データベース操作ルール
### 接続先
- 開発: postgres-local(MCP設定済み)
- 本番: 直接接続禁止(read-onlyのみ)
### クエリ設計原則
- SELECT * は禁止。必要なカラムを明示する
- インデックスのないカラムへの WHERE 句は原則禁止
- LIMIT のない SELECT は禁止(開発DBでも)
- マイグレーション実行前に必ず確認を求める
### マイグレーション
- 本番マイグレーションは必ずDATA LOSS がないかを確認する
- カラム追加は NULLable から始める
- カラム削除は2段階(アプリからの参照削除 → DBカラム削除)
まとめ
PostgreSQL MCPをClaude Codeに繋ぐと、SQLの生成・最適化・デバッグが全部自然言語でできるようになる。
特に有効なユースケース:
- スキーマ設計の相談 — ER図の生成と正規化チェック
- 遅いクエリの診断 — EXPLAIN ANALYZEを自動実行してインデックス提案
- N+1の発見 — ORM使用コードのレビュー
- マイグレーションの安全設計 — 既存データへの影響を考慮した手順
セキュリティ面では本番はread-only接続が鉄則。接続文字列は環境変数か1Password MCPで管理して、平文で settings.json に書かない。



