PostgreSQL MCPサーバーの使い方 — 索引提案とヘルスチェックまで
PostgreSQL専用のMCPサーバーPostgres MCP Proで、ヘルスチェックと索引チューニングを行う手順をまとめます。DBHubとの使い分けも扱います。
MCPはAIエージェントと外部ツールをつなぐ標準プロトコルで、詳しい仕組みはMCPとはにまとめています。MCPからPostgreSQLに接続する記事といえば、汎用DB接続サーバーDBHubを軸にしたMCPからデータベースに接続する方法がすでにあります。DBHubは複数DBに読み取り専用でつなぐ用途に強い一方、索引の提案やヘルスチェックのようなPostgreSQL固有のパフォーマンス分析は主目的ではありません。この記事で扱うPostgres MCP Pro(crystaldba/postgres-mcp)は、その隙間を埋めるPostgreSQL専用のMCPサーバーです。仮想インデックスを使った索引提案、遅いクエリの特定、DBの健全性チェックを1つのサーバーで行えます。NoSQL側には専用のサーバーがあり、MongoDB MCPサーバーガイドにまとめています。
DBHubとどちらを選ぶか
同じ「MCPでPostgreSQLに触る」でも、狙っている作業が違います。
| 項目 | DBHub | Postgres MCP Pro |
|---|---|---|
| 対応DB | DBHubPostgreSQL / MySQL / MariaDB / SQL Server / Oracle / SQLite | Postgres MCP ProPostgreSQLのみ |
| 主な用途 | DBHub複数DBへのクエリ(読み取り専用モードあり) | Postgres MCP Pro単一DBのパフォーマンス分析・チューニング |
| 代表ツール | DBHubexecute_sql / search_objects | Postgres MCP Proanalyze_db_health / analyze_workload_indexes / explain_query |
| 安全側の仕組み | DBHub読み取り専用モード・行数制限・タイムアウト | Postgres MCP Pro--access-modeで選ぶrestricted / unrestricted |
DBHubは「安全にクエリを投げる」ことに寄せた設計です。既定のツールは2本で、explain_sqlとhealth_checkはオプトインで追加できます。Postgres MCP Proは「どこが遅いか、どの索引を足せば効くか」を分析する側に寄せています。
選び方は単純です。PostgreSQL以外も触るならDBHub、PostgreSQLに絞って深く診断したいならPostgres MCP Proです。両方を同じプロジェクトに登録して、日常のクエリはDBHub、診断のときだけPostgres MCP Proという運用もできます。対象がBigQueryのようなクラウドDWHなら、Google自身がマネージドで提供するMCPサーバーという別の選択肢があります。詳しくはClaude BigQuery連携にまとめています。
先にDB側の準備を済ませる
Postgres MCP Proの索引提案とヘルスチェックの一部は、2つのPostgreSQL拡張に頼っています。サーバーを登録する前に有効化しておくと、後で原因を探す手間が省けます。
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
CREATE EXTENSION IF NOT EXISTS hypopg;pg_stat_statementsはクエリ実行統計を集計する標準拡張で、遅いクエリの特定に使われます。hypopgは実際にはインデックスを作らずに「作ったらどうなるか」をシミュレートする拡張です。
実行できる環境は、DBの運用形態で分かれます。
- RDS・Azure・Cloud SQLなどのマネージドPostgreSQL: 配布元はどちらの拡張も最初から使える状態にあるとしています。十分な権限のロールで上の
CREATE EXTENSIONを流せば足ります - 自前運用のPostgreSQL:
pg_stat_statementsはshared_preload_librariesに載せる必要があり、設定変更は再起動を伴います。hypopgはPostgreSQL本体に同梱されない場合があり、パッケージマネージャー経由での導入が別途必要です
本番で初めて有効化するときは、再起動の時間枠を先に確保しておきます。
もう1つの前提が実行環境です。配布元の前提条件はDockerまたはPython 3.12以上で、環境差の問題が出にくいとしてDockerを勧めています。
インストールと登録
インストール方法は3通りあります。
pipx install postgres-mcp
# または
uv pip install postgres-mcp
# またはDockerイメージを使う
docker pull crystaldba/postgres-mcpどれを選んでも、Claude Code側から見えるツールは同じです。
接続文字列はDATABASE_URIという環境変数で渡します。Claude Codeへの登録はclaude mcp addの--envを使います。
claude mcp add --env DATABASE_URI="postgresql://readonly:pass@prod.db.com:5432/analytics" \
--transport stdio postgres -- postgres-mcp --access-mode=restrictedDockerイメージを使う場合は、docker runに環境変数を渡す形になります。
claude mcp add --transport stdio postgres -- docker run -i --rm \
-e DATABASE_URI="postgresql://readonly:pass@prod.db.com:5432/analytics" \
crystaldba/postgres-mcp --access-mode=restrictedホスト名がlocalhostのDBに向ける場合、Dockerイメージが自動で読み替えます。macOSとWindowsではhost.docker.internalが使われ、コンテナの中から手元のDBに届きます。
登録コマンドの落とし穴 — 接続文字列の保存先
登録先のスコープで、接続文字列の置き場所が変わります。claude mcp addの--scopeはlocal(既定)・user・projectの3つです。localとuserは~/.claude.jsonに、projectはプロジェクト直下の.mcp.jsonに書かれます。
projectは.mcp.jsonをGitで共有する前提のスコープです。そのまま登録すると、DBのパスワードまで平文でファイルに書き込まれます。v2.1.287で、作業ディレクトリを空にして確かめました。
claude mcp add --scope project \
--env DATABASE_URI="postgresql://readonly:pass@localhost:5432/analytics" \
--transport stdio postgres -- postgres-mcp --access-mode=restricted
cat .mcp.json{
"mcpServers": {
"postgres": {
"type": "stdio",
"command": "postgres-mcp",
"args": ["--access-mode=restricted"],
"env": {
"DATABASE_URI": "postgresql://readonly:pass@localhost:5432/analytics"
}
}
}
}(実際の出力はargsが複数行で整形されます。ここでは1行にまとめています。)
接続文字列がそのまま入っています。チームで共有するなら、.mcp.jsonは環境変数の参照にとどめます。.mcp.jsonのenvは${VAR}の展開に対応しているので、登録時に参照を渡します。
claude mcp add --scope project --env 'DATABASE_URI=${PG_READONLY_URI}' \
--transport stdio postgres -- postgres-mcp --access-mode=restrictedシングルクォートで囲むのがポイントです。ダブルクォートだと、シェルが先に展開してしまいます。出力された.mcp.jsonのDATABASE_URIは${PG_READONLY_URI}のままで、実際の値は各自の環境変数に持たせます。
登録直後のステータスも確かめておきます。
claude mcp get postgrespostgres:
Scope: Project config (shared via .mcp.json)
Status: ⏸ Pending approval (run `claude` to approve)
Type: stdio
Command: postgres-mcp
Args: --access-mode=restricted
Environment:
DATABASE_URI=${PG_READONLY_URI}⏸ Pending approvalは接続に失敗したのではなく、.mcp.jsonのサーバーを未承認のまま接続していない状態です。claude mcp listやclaude mcp getは承認前のサーバーに接続しないため、この表示になります。claudeを対話で起動し、承認ダイアログで許可すると✔ Connectedに変わります。
なお--envは複数のKEY=valueを取るので、サーバー名の直前に置くと名前まで環境変数として読まれて拒否されます。上のコマンドのように、--transport stdioを間に挟みます。
アクセスモードの使い分け
Postgres MCP Proは起動時に--access-modeでモードを選びます。
アクセスモードは何が違うか
restricted
読み取り専用トランザクションに限定します。リソース制約も入りますが、現時点で制約されるのは実行時間だけです。COMMITやROLLBACKを含むSQLは実行前に弾かれます。
unrestricted
データとスキーマの変更を含む読み書きの両方ができます。CREATE INDEXもこのモードのexecute_sqlで通ります。
restrictedの仕組みを知っておくと、限界も見えます。PostgreSQLには接続を読み取り専用にする設定がないため、読み取り専用トランザクションの上でSQLを実行する方式です。
素朴に実装すると、ROLLBACK; DROP TABLE users;のように、トランザクションを抜けてから書き込む手が通ってしまいます。配布元はこれを防ぐため、実行前にSQLを構文解析してCOMMITとROLLBACKを拒否しています。
配布元自身が、この防御が破れる条件も書いています。COMMITやROLLBACKを許さない安全でない手続き言語をDB側で有効にしていると、読み取り専用の保護を回避できる可能性があるとのことです。
もう1つ、--access-modeは起動時に1つ選ぶサーバー全体の設定で、ツール単位の切り替えはREADMEに用意されていません。そこで接続に使うDBユーザーの権限を絞ります。配布元も、読み取り専用の権限を持つDBユーザーを作る方法を最も単純な手段として挙げています。restrictedと読み取り専用ユーザーは、別の層の防御として重ねられます。DBHubの記事で扱った多重防御と同じ発想です。
ヘルスチェックと索引提案を実際に使う
ここまで済めば、あとは自然文で聞くだけです。
このデータベースの健全性をチェックして、問題があれば教えてanalyze_db_healthが呼ばれます。確認範囲は、バッファキャッシュのヒット率、接続の使用状況、制約の検証、索引の健全性(重複・未使用・無効)、シーケンスの上限、バキュームの状態です。
索引の提案は、聞き方で呼ばれるツールが変わります。
聞き方と呼ばれるツール
ワークロード全体を見たい
「過去1週間で遅いクエリを見つけて、効きそうな索引があれば提案して」と聞きます。
get_top_queriesで遅いクエリを特定し、analyze_workload_indexesが全体から索引の組み合わせを検討します。1本のクエリだけを見たい
「このSELECT文にインデックスを1つ足すなら、どこに何を足す?」と、SQLを添えて聞きます。
analyze_query_indexesが指定クエリ(最大10個)に絞って候補を出します。索引を作る前に効果を見たい
explain_queryを仮想インデックス付きで呼ぶと、hypopgでコストの変化を見積もります。実際の索引作成は不要です。
get_top_queriesは総実行時間の長い順に報告し、平均実行時間を基準にする指定もできます。ワークロード分析のほうは、実行回数と平均実行時間にしきい値を置いて、遅いクエリを選ぶ方式です。
索引を実際に作るCREATE INDEXは、unrestrictedモードのexecute_sqlでだけ通ります。見積もりと作成を分けられるので、本番ではrestrictedのまま提案だけ受け取り、作成は自分のマイグレーション手順で行う流れになります。
見積もりの精度はhypopgの予測に左右されます。配布元は、この予測の精度に索引提案が依存していると書いています。提案された索引は、作成前に自分の環境でEXPLAINを確かめる使い方が向いています。
提供ツールは次の9本です。
| ツール名 | 機能 |
|---|---|
list_schemas | 機能すべてのスキーマを一覧表示 |
list_objects | 機能スキーマ内のテーブル・ビュー・シーケンス・拡張を表示 |
get_object_details | 機能テーブルのカラム・制約・インデックス情報を取得 |
execute_sql | 機能SQLを実行(restrictedでは読み取り専用) |
explain_query | 機能実行計画を取得。仮想インデックスのシミュレーションも可能 |
get_top_queries | 機能pg_stat_statementsに基づき遅いクエリを報告 |
analyze_workload_indexes | 機能ワークロードを分析し、最適な索引を提案 |
analyze_query_indexes | 機能指定クエリ(最大10個)に対する索引を提案 |
analyze_db_health | 機能バッファキャッシュ・接続・制約・索引・シーケンス・バキュームを確認 |
スキーマの把握だけが目的なら、list_schemas・list_objects・get_object_detailsの3本で足ります。
ツール単位で許可を分ける
--access-modeはサーバー起動時に固定するモードです。Claude Code側のpermissionsを組み合わせると、ツール単位でさらに細かく制御できます。ルールはmcp__<サーバー名>__<ツール名>の形式で書きます。
{
"permissions": {
"allow": ["mcp__postgres__analyze_db_health", "mcp__postgres__get_top_queries"],
"ask": ["mcp__postgres__execute_sql"]
}
}この設定なら、ヘルスチェックと遅いクエリの確認はプロンプトなしで通ります。execute_sqlは呼ぶたびに確認が挟まります。サーバー名の部分は登録時の名前なので、claude mcp add ... postgresと付けたならpostgresです。設定の書き方全般はClaude Code MCP設定ガイドで扱っています。
複数のクライアントで共有したいとき
標準の登録はstdioで、Claude Codeを起動するたびにサーバーが立ち上がります。複数のMCPクライアントで1台のサーバーを共有したいなら、SSEトランスポートの選択肢があります。--transport=sseを付けて起動し、クライアント側にURLを向けます。
docker run -p 8000:8000 -e DATABASE_URI="postgresql://..." \
crystaldba/postgres-mcp --access-mode=restricted --transport=sse配布元の設定例ではURLがhttp://localhost:8000/sseです。共有した時点で、接続文字列はサーバー側の1か所に集まります。READMEはSSEの認証や暗号化について触れていないため、リモートで公開するなら通信経路とアクセス制御は別途用意します。
対応バージョンと制約
対応するPostgreSQLのバージョンは、配布元がFAQで答えています。テストの中心はPostgreSQL 15・16・17で、13から17までの対応を予定しているとのことです。
つまり、13と14は予定の範囲で、検証の厚みは15以降が上です。古い環境に入れる場合は、まず検証用DBでanalyze_db_healthが通るかを確かめます。
実装の注意点がもう1つあります。接続情報はサーバー起動時に渡す方式です。配布元はこの方式の弱みとして、MCPクライアントの設定ファイルを安全に保管するものは少ないと書いています。前の節の${VAR}参照は、この弱みへの現実的な対処です。
よくあるつまずき
- 索引提案ツールが何も返さない:
pg_stat_statementsが有効化されていないとget_top_queriesが使えません。SELECT * FROM pg_extension;で有効化状況を確認します。拡張を作ったのに空のままなら、shared_preload_librariesへの登録と再起動が済んでいるかを見ます - 仮想インデックスの見積もりが出ない:
hypopgが入っていない環境で、explain_queryの仮想インデックス機能は使えないと考えられます。拡張の有無を先に確認します unrestrictedのまま本番接続文字列を渡してしまう: 開発用の設定を本番のDATABASE_URIに使い回すと、書き込みも通る状態で本番につながります。環境ごとに接続設定を分けます- DBHubと同時登録して重複する: 同じDBに両方つなぐと、
execute_sqlのような近い名前のツールが2系統でき、どちらが呼ばれるか分かりにくくなります。用途で片方を/mcpから無効化します .mcp.jsonがPending approvalのまま: 前の節のとおり、承認待ちの状態です。接続エラーと混同しないようにします
まとめ
Postgres MCP Proは、PostgreSQLのパフォーマンス診断に絞ったMCPサーバーです。入れる前に決めておくのは、接続文字列の置き場所とアクセスモードの2つです。共有する.mcp.jsonには${VAR}参照だけを書き、本番にはrestrictedと読み取り専用のDBユーザーを重ねます。