PostgreSQL MCP Server について
PostgreSQL MCP Serverは、Claude CodeやCursorなどのクライアントに、データベース検索、スキーマ確認、性能診断のツールを提供します。書き込みは既定で無効で、PostgreSQLのロールと任意の書き込み設定で制御でき、インデックス助言機能も備えています。
主な機能
- 01パラメーター付きSQLクエリの実行
- 02スキーマ、テーブル、列、権限の確認
- 03実行計画とデータベース状態の確認
- 04クエリ、インデックス、ロック、レプリケーションの診断
- 05仮想インデックスによる候補評価
活用シーン
- 01AIクライアントからデータベースの内容を確認
- 02テーブル構造とロール権限を調査
- 03遅いクエリ、ロック、テーブル肥大化を調査
- 04クエリ負荷に対するインデックス候補を評価
MCP 対応
MCPサーバーとして、PostgreSQLのクエリ、スキーマ確認、実行計画、診断ツールをクライアントに公開します。クエリは既定で読み取り専用で、必要に応じて書き込みを有効化でき、構造化出力も返します。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
ツール
23掲載時のツール一覧 · 2 週間前に更新
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
| sql | string | はい | 実行する SQL 文。上限は 1 MB です。 |
| params | array | いいえ | SQL 内で $1、$2、... として参照する位置パラメーター。 |
詳細
@yawlabs/postgres-mcp
ワンクリックでローカルの Yaw MCP 設定に追加され、すべての Yaw Terminal セッションで利用できるようになります。または、以下の手順で手動インストールしてください。
Claude Code、Cursor、およびあらゆる MCP クライアントから PostgreSQL データベースにクエリを実行できます。 デフォルトでは読み取り専用です。書き込みを有効にするには、環境変数を 1 つ設定します。これにより、エージェントが知らないうちにテーブルを削除することを防げます。
Yaw Labs が開発・保守しています。
0.12 の新機能
インデックスアドバイザー、オプトイン方式の監査ログ、構造化ツール出力、現行リビジョンの MCP クライアントへの対応を追加しました。詳細は変更履歴をご覧ください。
pg_index_advisorはワークロードに適したインデックスを提案し、推定コストを実測可能なほど低減するものだけを残します。候補のコスト評価には HypoPG の仮想インデックスを使用し、ディスク上には作成しません。また、PostgreSQL 18 のスキップスキャンによって、有用な複合インデックスがどう変わるかも考慮します。- オプトイン方式の監査ログでは、SQL ツールが送信する内容を記録します。監査対象のステートメントごとに 1 行の JSON を、stderr またはファイルに出力します。バインドパラメーターの値は記録されません。デフォルトでは無効です。また、完全な記録ではありません。詳しくは監査ログをご覧ください。
- 構造化ツール出力。 すべてのツールが
outputSchemaを宣言し、変更のないテキストブロックに加えてstructuredContentを返します。そのため、現在テキストを読み取っているものは、引き続き動作します。 - 2 つの MCP プロトコル世代に対応。 これまではレガシー(2025 年)のプロトコルリビジョンだけをサポートしていたため、現行の 2026-07-28 リビジョンを使用するクライアントとは互換性がありませんでした。現在は両方に対応しています。
0.12.0 をお使いですか? アップグレードしてください。 0.12.1 では、pg_index_advisor における複数クエリ実行の抜け穴を塞ぎました。statements 引数に渡された SQL は、1 つの文字列で複数のコマンドを受け付けるプロトコル上で実行されていたため、SELECT 1; COMMIT; DROP SCHEMA public CASCADE; によって読み取り専用トランザクションを回避できました。このツールには読み取り専用の注釈が付いているため、ホストが自動的に許可することがよくあります。同じリリースでは、別のデータベースで保持されているロックを、OID が一致するローカルテーブルのロックとして pg_inspect_locks が誤って帰属させる問題を修正し、アドバイザーの貪欲探索を修正し、監査ログの行に欠けていた tool フィールドを追加しました。
0.10.x から移行しますか? 0.11.0 には 3 つの破壊的変更があります。pg_seq_scan_tables、pg_unused_indexes、pg_top_queries はエンベロープを返すようになりました(以前 data を読み取っていた箇所では data.rows を読み取ってください)。analyze: true を指定した pg_explain は BUFFERS を出力します(以前の出力に戻すには buffers: false を指定してください)。また、Node 22 以上が必要です。詳細は0.11.0 の変更履歴エントリーをご覧ください。
背景
Anthropic のリファレンス Postgres MCP サーバーである @modelcontextprotocol/server-postgres は、2025 年 5 月にアーカイブされ、2025 年 7 月に npm で非推奨と表示されました。Anthropic は後継をリリースしていません。非推奨になったにもかかわらず、最終公開バージョン(v0.6.2)は現在も週に約 20,000 回ダウンロードされており、多くのエージェントがメンテナンスされていないパッケージを使用しています。
このメンテナンスされていないパッケージには、既知の公開されているスタッククエリ SQL インジェクション(Datadog Security Labs)もあります。COMMIT; DROP SCHEMA public CASCADE; のような入力で BEGIN READ ONLY ラッパーを回避できます。この脆弱性は npm で一度も修正されていません。
コミュニティによるフォークもいくつか登場していますが、それぞれ対応範囲は限られています。
@zeddotdev/postgres-context-server- Zed によるフォークで、主にオリジナルの構成にセキュリティパッチを適用しています。- Postgres MCP Pro(Crystal DBA)- インデックスのチューニングと、仮想インデックス/バッファキャッシュの診断に特化しています。
- AWS Labs Postgres MCP - Aurora/RDS Data API と Secrets Manager に依存しています。
いずれも、任意の Postgres に接続して Claude Code や Cursor に使わせる、汎用的な日常使いのツールとしては位置付けられていません。最新のイントロスペクション、パフォーマンス支援機能、ロール/権限の認識、そして最初から備わっている書き込み安全性への配慮が不足しています。これが @yawlabs/postgres-mcp が埋めるギャップです。
このサーバーを選ぶ理由
- デフォルトで読み取り専用、無条件の読み取り専用ツールも用意 -
pg_queryはユーザーの SQL をBEGIN READ ONLYトランザクションで実行するため、文字列解析ではなく PostgreSQL 自体が書き込みをブロックします。ALLOW_WRITES=1を指定すると、書き込みを許可できます。pg_readonlyは別個のツールで、ALLOW_WRITESの設定にかかわらず読み取り専用です。そのため、ツールごとに許可を設定するホスト(Claude Code の権限、mcp.hosting)では自動的に許可できます。ただし、最小権限のロールと組み合わせてください。READ ONLYが制限するのはデータベースへの書き込みであり、あらゆる副作用ではありません(詳細)。 - ロールベースのアクセス制御を主な対策に - 推奨される方法は、
DATABASE_URLに最小権限の PostgreSQL ロール(たとえばGRANT pg_read_all_dataを付与したロール)を使用することです。これにより、環境変数を設定しなくても PostgreSQL 自体が境界を適用します。アクセスの設定を参照してください。 - すべてのユーザー SQL に拡張クエリプロトコルを使用 -
pg_queryはユーザー入力をqueryMode: 'extended'で送信し、各リクエストを単一のステートメントに制限します。これにより、リファレンスサーバーのBEGIN READ ONLYラッパーを無効化したスタックドクエリーインジェクションの手法(COMMIT; DROP SCHEMA x CASCADE;)を防ぎます。統合テストで拒否されることを確認します。 - パラメーター化クエリー -
pg_queryは$1、$2などに対応するparams配列を受け取ります。コード内で文字列補間を使った SQL はありません。 - 一から作成し、積極的にメンテナンス - 非推奨コードのフォークではありません。ユニットテストと統合テスト(
npm test、npm run test:integration)は実際の Postgres に対して実行されます。リリースはrelease.shで作成します。 - スキーマのイントロスペクションを内蔵 -
pg_list_schemas、pg_list_tables、pg_describe_tableは、エージェントがpg_catalogの結合方法を覚えていなくても、列、主キー、外部キー、インデックスを返します。 EXPLAINを主要なツールとして提供 - テキスト形式または JSON 形式を選択でき、ANALYZEも指定できます。SELECT 以外のステートメントでANALYZEを実行するにはALLOW_WRITES=1が必要で、実行後は必ずロールバックされるため、実際の実行計画を取得しつつ、書き込まれた行は保持されません。(Postgres でロールバックされないものは残ります。ステートメントによって進んだシーケンスは、進んだままです。)- 非推奨サーバーにはなかったパフォーマンス診断機能 -
pg_top_queries(pg_stat_statementsを使用)、pg_seq_scan_tables、pg_unused_indexes、pg_table_bloat、pg_inspect_locks、pg_replication_statusを利用できます。「なぜ遅いのか?」を 1 回のツール呼び出しで調べられます。 - ヘルススナップショット -
pg_healthは、バージョン、データベースサイズ、接続数、実行時間が最も長い実行中クエリー 10 件を 1 回の呼び出しで返します。 - ロールと権限を確認 - よくある「誰が何にアクセスできるか?」という疑問には、
pg_list_rolesとpg_table_privilegesを利用できます。 - 即時起動 - ランタイム依存関係ゼロの単一バンドルファイルとして提供されます。
npxの起動のたびに、node_modulesのインストールに何分もかかることはありません。 - 結果の切り捨て - 大きな結果セットは
POSTGRES_MAX_ROWS(デフォルトは 1000)で上限が設定され、truncated: trueフラグが付与されます。そのため、誤ってSELECT * FROM eventsを実行しても、モデルのコンテキストがあふれることはありません。
クイックスタート
1. プロジェクトのルートに .mcp.json を作成
macOS / Linux / WSL:
{
"mcpServers": {
"postgres": {
"command": "npx",
"args": ["-y", "@yawlabs/postgres-mcp@latest"],
"env": {
"DATABASE_URL": "postgres://user:password@host:5432/dbname"
}
}
}
}
Windows:
{
"mcpServers": {
"postgres": {
"command": "cmd",
"args": ["/c", "npx", "-y", "@yawlabs/postgres-mcp@latest"],
"env": {
"DATABASE_URL": "postgres://user:password@host:5432/dbname"
}
}
}
}
Windows で手順が追加されるのはなぜですか? Node 20 以降、
child_process.spawnでは.cmdファイル(Windows におけるnpx)を直接実行できません。cmd /cでラップするのが標準的な回避策です。
2. 再起動して許可
Claude Code(または MCP クライアント)を再起動し、プロンプトが表示されたら postgres MCP サーバーを許可します。
3. (任意)書き込みを有効にする
デフォルトは読み取り専用です。エージェントが INSERT、UPDATE、DELETE を実行したり、DDL を実行したりできるようにするには、env ブロックに ALLOW_WRITES=1 を追加します。
"env": {
"DATABASE_URL": "postgres://...",
"ALLOW_WRITES": "1"
}
この設定は開発用/テスト用データベースだけに適用することをおすすめします。本番環境では書き込みを無効のままにし、マイグレーションツールを別途使用してください。
アクセスの設定
DATABASE_URL のロールが、主要なアクセス制御です。Postgres には30年にわたって実績のある権限システムがあるため、ALLOW_WRITES だけに頼らず、これを活用してください。最小限の権限を持つロールを使えば、ツールや環境変数の設定にかかわらず、書き込みはサーバーによって拒否されます。
読み取り専用エージェント(推奨されるデフォルト):
CREATE ROLE mcp_reader LOGIN PASSWORD 'change-me';
GRANT CONNECT ON DATABASE your_db TO mcp_reader;
GRANT USAGE ON SCHEMA public TO mcp_reader;
GRANT pg_read_all_data TO mcp_reader;
DATABASE_URL を mcp_reader に設定します。ALLOW_WRITES の値にかかわらず、Postgres がすべての書き込み、すべての DDL、すべての権限変更を拒否します。回避可能なアプリケーションレベルのガードはなく、データベースが境界となります。
スコープを限定した書き込みエージェント(開発/テスト、または本番環境での限定的な使用):
CREATE ROLE mcp_writer LOGIN PASSWORD 'change-me';
GRANT CONNECT ON DATABASE your_db TO mcp_writer;
GRANT USAGE ON SCHEMA public TO mcp_writer;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO mcp_writer;
GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO mcp_writer;
-- DDL not granted -- the agent can change data but not schema.
ALLOW_WRITES=1 を設定して pg_query に書き込みを実行させ、ロールによってエージェントが DDL やほかのスキーマにアクセスできないようにします。
ツールは、権限レベルに応じて次の2つのクラスに明確に分かれます。
- 自動許可:
pg_readonly(サーバー側でBEGIN READ ONLYを実行し、無条件で許可)、pg_index_advisor(渡されたステートメントに対して、BEGIN READ ONLY内でANALYZEを使わずにEXPLAINのみを実行)、およびイントロスペクションと診断のツール(pg_list_*、pg_describe_table、pg_search_columns、pg_health、pg_inspect_locks、pg_table_bloat、pg_unused_indexes、pg_seq_scan_tables、pg_top_queries、pg_io_stats、pg_replication_status、pg_advisor、pg_table_privileges)。 - 常に確認を求める:
pg_query(ALLOW_WRITES=1が設定され、ロールが許可している場合は書き込みを実行)、pg_explain(analyze: trueはステートメントを実行します。ALLOW_WRITES=1の場合はロールバックされる書き込みも含み、設定されていない場合、このツールの権限範囲はpg_readonlyと同じです)、pg_kill(セッションの状態を変更するため、ALLOW_WRITES=1が必要)。
この分類は各ツールの readOnlyHint アノテーションに基づいており、Claude Code の permissions ブロックと mcp.hosting のツールごとの切り替え設定は、どちらもこの分類に従います。
READ ONLYで防げること、防げないこと。BEGIN READ ONLYトランザクションは、データベースへの書き込み(INSERT/UPDATE/DELETE、DDL、nextval/setval)をブロックします。テーブルデータの外部に影響を与える関数はブロックしません。SELECT pg_terminate_backend(...)、pg_cancel_backend、pg_read_file、lo_export、COPY ... TO PROGRAMはすべてpg_readonly内で最後まで実行されます。つまり、pg_readonlyを自動許可すると、pg_killをALLOW_WRITES=1の背後に置いた場合と同じ機能にアクセスできることになります。これらを実行するには、いずれもDATABASE_URLのロールが実際に保持している権限(pg_signal_backend、pg_read_server_files、スーパーユーザー)が必要です。そのため、このツールの権限範囲を定めるのはトランザクションモードではなくロールです。pg_readonlyを自動許可する場合は、最小限の権限を持つロールを使用してください。アクセスの設定を参照してください。
多層防御としての ALLOW_WRITES:
ALLOW_WRITES は、追加の安全策となる二次的なゲートです。次の場合に役立ちます。
- ロールを2つ目に分けて作るのが難しいマネージドデータベースを使用している場合(Supabase/Neon の一部のプランなど)。
- 書き込み可能なロールを1つだけ使いながら、通常の運用中は MCP サーバーによって書き込みを拒否したい場合。
それ以外の場合は、ロールを設定し、ALLOW_WRITES に頼るのはやめてください。
エージェントで何ができますか?
接続すると、エージェントは依頼内容に応じてツールを自動的に選択します。単一のツールを使う例をいくつか紹介します。
- 「users テーブルについて説明して」 ->
pg_describe_table-> 種類、列、PK、FK、インデックスを返します。 - 「
user_id列があるテーブルはどれ?」 -> パターンuser_idを指定してpg_search_columnsを実行します -> すべてのテーブルを順に調べる代わりに、1 回の呼び出しで結果を返します。 - 「このクエリが遅いのはなぜ?」 ->
analyze: trueを指定してpg_explainを実行します -> 実際の行数と実行時間を含むプランを返します。 - 「実行中のクエリで最も遅いものは?」 ->
pg_top_queries->pg_stat_statementsから平均/合計/最小/最大実行時間とともに上位 N 件を返します。 - 「使われていないインデックスはある?」 ->
pg_unused_indexes-> スキャン回数がゼロまたは少ない、ユニークでもプライマリでもないインデックスと、そのサイズを返します。 - 「
pgvectorはインストールされている?」 ->pg_list_extensions-> バージョンとともに、はい/いいえで返します。
より大きな効果を発揮するのは、複数のツールを組み合わせた推論です。実際のワークフローをいくつか紹介します。
- ハングしたアプリを復旧する。
pg_inspect_locksはブロックされている PID、ブロックしている PID、問題のクエリを返し、その後pg_kill(ALLOW_WRITES=1が必要)でブロッカーをキャンセルします。エージェントは両方を 1 回のターンで実行できるため、「アプリがフリーズしている」状態から「復旧した」状態への最速の手段です。 - 遅いページの原因を追う。
pg_top_queriesで遅いクエリを順位付けし、analyze: trueを指定したpg_explainで最上位のクエリのプランを確認します。pg_seq_scan_tablesとpg_unused_indexesによって、答えが「ここにインデックスを追加する」なのか「そこの使われていないインデックスを削除する」なのかが分かります。 - オンコール時のトリアージ。
pg_healthで接続性、アクティブなクエリ数、データベースサイズを確認します。pg_inspect_locksとpg_replication_statusで、オンコールの DBA にページャーで連絡する前に、競合やレプリケーションの遅延が発生しているかどうかを確認できます。
ツール
| ツール | 説明 |
|---|---|
pg_readonly | 永続的なデータ変更を行わずに SQL 文を実行します。ALLOW_WRITES の値にかかわらず、常に BEGIN READ ONLY の中で実行されます。読み取りアクセスに推奨されるツールで、アドホックな SQL に対して自動許可するツールです。最小権限のロールと組み合わせてください(理由)。 |
pg_query | SQL クエリを実行します。書き込みは、まず DATABASE_URL のロールによって、次に ALLOW_WRITES によって制御されます。params を使ったパラメーター化クエリに対応しています。結果フィールドには dataTypeID とともに dataTypeName(例:int4、jsonb)が含まれます。 |
pg_list_schemas | システムスキーマ以外のスキーマを一覧表示します。 |
pg_list_tables | スキーマ内のテーブル(および任意でビュー)を推定行数とともに一覧表示します。limit/offset でページ分割できます。 |
pg_describe_table | リレーションの種別、列、PK、外部キー(参照先)、被参照外部キー(referenced_by)、CHECK/UNIQUE/EXCLUDE 制約、インデックス、パーティションの親/子を表示します。生成列と ID 列には(generated、identity、generation_expression)のフラグが付くため、エージェントがそれらに書き込もうとするのを防げます。制約には validated が含まれ、PG18 以降では enforced/has_period も含まれます。 |
pg_list_views | スキーマ内のビューとマテリアライズドビューを、SQL 定義とともに一覧表示します。 |
pg_list_functions | スキーマ内の関数、プロシージャ、集約関数を、シグネチャと戻り値の型とともに一覧表示します。 |
pg_list_extensions | インストール済みの拡張機能(pgvector、postgis、pg_stat_statements など)をバージョンとともに一覧表示します。 |
pg_search_columns | すべてのユーザースキーマから、名前のパターンに一致する列を検索します。大文字と小文字を区別せず、SQL の LIKE ワイルドカードに対応しています。 |
pg_explain | SQL 文に対して EXPLAIN または EXPLAIN ANALYZE を実行します。テキストまたは JSON で出力します。プランナーのオプション:buffers(analyze 使用時はデフォルトで有効)、settings、verbose、wal、costs、timing、さらに generic_plan(PG16 以降。値を指定せずにパラメーター化クエリをプランニング)および memory/serialize(PG17 以降)。任意の hypothetical_indexes(HypoPG 拡張機能が必要)を使うと、インデックスをディスク上に作成せずに「これらのインデックスがあった場合、プランはどうなるか」を確認できます。 |
pg_index_advisor | ワークロードに対するインデックスを推奨し、それぞれの効果を事前に検証します。渡した statements、または pg_stat_statements から取得した上位 N 件を受け取り、プランナーがフィルター/結合キー/ソートキーとして報告した内容から候補列を抽出します(SQL パーサーは使わず、すべてのトークンを実際の pg_attribute の列リストと照合します)。次に、HypoPG の仮想インデックスを使って各候補のコストを算出し、推定コストを明確に下げるものだけを残します。max_candidates/max_explains による貪欲な上限付き検索のため、大規模なワークロードでも処理が際限なく増えることはありません。検索が打ち切られた場合は budget_exhausted で示されます。CREATE INDEX(および CONCURRENTLY 形式)、実行前後のコスト、各インデックスが有効なステートメント、推定サイズを返します。PG18 対応: PG18 では B-tree のスキップスキャンが追加されたため、先頭列が一度もフィルターされない複数列インデックスも、もはや役に立たないとは限りません。従来のこの除外処理は、無条件に適用されるのではなく、サーバーのバージョンに応じて制御されます。HypoPG が必要です。インデックスはセッション単位で作成され、終了時にはどの経路でもリセットされます。 |
pg_health | サーバーバージョン、データベースサイズ、max_connections に対する接続数、待機イベントとトランザクション経過時間を含むアクティブなクエリ、pg_stat_database の集計(デッドロック、一時ファイル、キャッシュヒット率)、テーブル数を表示します。 |
pg_top_queries | 合計/平均実行時間による上位 N 件のクエリを表示します。pg_stat_statements 拡張機能が必要です。pg_stat_statements_info から取得した stats_reset(他の統計ツールとは異なる基準時刻)と、拡張機能 1.9 以降の dealloc を返します。dealloc が 0 以外の場合、pg_stat_statements.max を超えてエントリが削除されているため、ランキングの母集団は不完全です。 |
pg_seq_scan_tables | シーケンシャルスキャンが多いテーブルを表示します。インデックス不足の候補を特定できます。カウンターの値は対象期間がわからなければ意味をなさないため、行とともに stats_reset の期間を返します。PG16 以降では last_seq_scan/last_idx_scan も返します。 |
pg_unused_indexes | スキャン回数が少ない、一意インデックスおよび主キー以外のインデックスを表示します。削除候補を特定できます。また stats_reset も返します。カウンターが最近リセットされていると、すべてのインデックスが未使用に見え、重要なインデックスが削除される原因になります。PG16 以降では last_idx_scan も返します。 |
pg_io_stats | I/O の可観測性を提供します。バックエンドの種類とコンテキストごとの pg_stat_io の読み取り/書き込み/拡張/fsync の回数、バイト数、時間(PG16 以降)に加え、進行中の非同期 I/O ハンドル(pg_aios)とアクティブな io_method(PG18 以降)を表示します。 |
pg_inspect_locks | 現在、誰が誰をブロックしているか(ブロックされている PID、ブロッカーの PID、ロックの種類、クエリ)を表示します。 |
pg_list_roles | ログイン/スーパーユーザー/データベース作成のフラグとグループメンバーシップを含む、データベースロールを一覧表示します。 |
pg_table_privileges | テーブルまたはスキーマ全体に対する SELECT/INSERT/UPDATE/DELETE などの権限を持つユーザーを表示します。 |
pg_table_bloat | デッドタプルの割合が高いテーブルを表示します。VACUUM の候補を特定できます。 |
pg_replication_status | レプリケーションスロット、接続中のレプリカ、現在の WAL 位置を表示します。 |
pg_advisor | DBA 向けの診断項目を 1 回の呼び出しでまとめて表示します。シーケンス枯渇の候補、ラップアラウンドのリスク(データベース単位およびテーブル単位で、age(relfrozenxid) を autovacuum_freeze_max_age と、mxid_age(relminmxid) を autovacuum_multixact_freeze_max_age と比較します。ロックの多いワークロードでは XID が正常に見えてもマルチトランザクションが枯渇する可能性があり、triggered_by で原因を確認できます)、主キーのないテーブル、(設定可能な)RLS が無効な public テーブルを検出します。「何を確認すべきか」を知るための出発点です。 |
pg_kill | 実行中のクエリをキャンセルするか、バックエンド接続を終了します。ALLOW_WRITES=1 が必要です。 |
設定
すべての環境変数は MCP サーバーの環境から読み込まれます。
| 変数 | デフォルト | 用途 |
|---|---|---|
DATABASE_URL | (必須) | PostgreSQL 接続文字列です。 |
ALLOW_WRITES | 未設定 | pg_query、書き込みに対する pg_explain の ANALYZE、および pg_kill 用の副次的な書き込みゲートです。1 または true に設定すると、前者 2 つを囲む BEGIN READ ONLY ラッパーが解除され、pg_kill が実行できるようになります。DATABASE_URL のロールが主な制御手段です。アクセスの設定を参照してください。無条件の pg_readonly には影響しません。 |
POSTGRES_STATEMENT_TIMEOUT_MS | 30000 | ステートメントごとのタイムアウトです。 |
POSTGRES_CONNECTION_TIMEOUT_MS | 10000 | TCP 接続タイムアウトです。これを設定しない場合、OS が接続を諦めるまで、応答しないホストへの接続がハングします(約 2 分)。 |
POSTGRES_MAX_ROWS | 1000 | pg_query が返す行数の上限です。 |
POSTGRES_POOL_MAX | 5 | 接続プールの最大接続数です。シングルスレッドのバックエンド(pglite-socket、PgBouncer のトランザクションモード)では 1 に設定してください。 |
POSTGRES_SSL_REJECT_UNAUTHORIZED | 未設定 | プライベート CA 証明書を使用するマネージド DB の場合は、false に設定して TLS 証明書の検証をスキップします。接続は引き続き暗号化されます。 |
POSTGRES_APPLICATION_NAME | postgres-mcp | pg_stat_activity.application_name で報告される値です。これにより、データベースを監視している人がエージェントのトラフィックを識別できます。DATABASE_URL に application_name が指定されている場合は、そちらが優先されます。 |
POSTGRES_AUDIT_LOG | 未設定(オフ) | 1、true、または stderr を指定すると監査ログが有効になり、監査対象のステートメントごとに 1 行の JSON が出力されます。POSTGRES_AUDIT_LOG_FILE も設定されていない限り、stderr に書き込まれます。0、false、または off は明示的なオフです。大文字と小文字は区別されません。それ以外の値を指定すると、空または空白のみの場合も含め、サーバーは起動時に停止します。監査ログを参照してください。 |
POSTGRES_AUDIT_LOG_FILE | 未設定 | 監査ログを stderr ではなく、このファイルに追記します。これだけを設定しても監査ログが有効になります。値が空の場合、ファイルを開けない場合、または POSTGRES_AUDIT_LOG が明示的にオフの場合、サーバーは起動を拒否します。 |
POSTGRES_AUDIT_REDACT | 未設定(オフ) | 1 または true を指定すると、SQL の代わりに各ステートメントの先頭キーワードと、そのテキストの SHA-256 をログに記録します。0、false、または off は明示的なオフで、SQL 全文がログに記録されます。これだけでは監査ログは有効になりません。監査ログがオフの場合でも、空または空白のみの場合を含め、それ以外の値を指定するとサーバーは起動時に停止します。 |
POSTGRES_MCP_RUNTIME | auto | サーバーを実行する JS ランタイムです。auto(最新の oam が 0.18.0 以降ならそれを使用し、そうでなければ Node)、oam(同じランタイムを使用しますが、Node にフォールバックせずエラーで終了)、node(常に Node を使用します。oam run で起動され、PATH 上の Node に処理を引き継ぎます。POSTGRES_MCP_SANDBOX が有効な場合は拒否されます)。大文字と小文字は区別されません。それ以外の値は auto と同じ動作になります。ランタイムを参照してください。 |
OAM_BIN | 未設定 | 検出より優先して使用する oam バイナリのパスです。バージョンは 0.18.0 以降である必要があります。バイナリが存在しない、バージョンが古い、または実行できない場合、ランチャーは stderr にその旨を出力し、検出を続行します。 |
POSTGRES_MCP_SANDBOX | 未設定(オフ) | 1 または true を指定すると、oam の --permission サンドボックス内でサーバーを実行します。ファイルシステムと子プロセスは拒否され、ネットワークアクセスは pg ドライバーが接続する 1 つのホストとポートに制限されます。接続先は DATABASE_URL(host= および port= パラメーターが優先)、次に PGHOST/PGPORT、それ以外はドライバーと同じ順序で localhost:5432 から決まります。0、false、または off は明示的なオフです。大文字と小文字は区別されません。それ以外の値を指定すると、空または空白のみの場合も含め、ランチャーは起動時に停止します。これは通常、MCP クライアントの実行環境で変数が未設定のまま "POSTGRES_MCP_SANDBOX": "${SANDBOX}" のような env エントリーがある状態です。未設定として読み取られると、サンドボックスなしでサーバーが実行されてしまいます。未設定とみなされるのは、変数自体が設定されていない場合のみです。サンドボックスが黙って無効になることはありません。適用できない場合、つまり POSTGRES_MCP_RUNTIME=node、使用可能な oam 0.18.0 以降がない、oam の起動に失敗する、またはデータベースのエンドポイントを 1 つの TCP ホストとポートに固定できない場合(Unix ソケット、複数ホスト、1~65535 の範囲外のポート、単純な DNS 名でも IP アドレスでもないホスト、読み取り可能な postgres:// URL ではない DATABASE_URL)、ランチャーは修正方法を示すメッセージとともに終了します。DATABASE_URL が未設定の場合、サーバーにはネットワークアクセスが一切許可されず、ツールはその変数が見つからないことを報告します。oam の変更履歴によると、--permission が fs と child_process のすべてをカバーするようになったのは 0.9.1 以降であり、ポート許可が厳密になったのは 0.15.0 以降です。oam は DNS ルックアップを制限しません。継承された NODE_OPTIONS にある --permission または --allow-* フラグは、サンドボックス内の oam の環境から削除されます。oam はコマンドラインで省略されたすべての権限フラグをそこから取得するため、フラグが残っていると許可範囲が広がってしまいます。ファイル監査ログはサンドボックス内では開けません。stderr 出力を使用してください。トラブルシューティングを参照してください。 |
サポート対象の Postgres バージョン
統合テストマトリックスでは、PostgreSQL 15、17、18 でテスト済みです。
PG13 以降で動作しますが、上流のサポート状況にはご注意ください。PG13 は 2025-11-13 にサポート終了を迎え、PG14 も 2026-11-12 に終了します。 PG13/14 はこのプロジェクトではテストされておらず、今後の互換性対象ではありません。PG12 以前はさらにサポート対象外であり、一部のツールは PG13 で追加された列に依存しています(pg_replication_status は wal_status を参照し、pg_top_queries は *_exec_time を参照します)。
新しいサーバーバージョンでは追加のフィールドが利用可能になりますが、必須ではありません。バージョンに依存する各列は server_version_num によって制御され、それより前のバージョンのサーバーでは単に省略されるため、エラーにはなりません。返される情報が少し減るだけです。重要なバージョン境界は次のとおりです。
| サーバー | 追加されるもの |
|---|---|
| PG16+ | 統計ツールの last_idx_scan/last_seq_scan(単なるカウンターではなく、インデックス/テーブルの古さを示します)、pg_explain の generic_plan |
| PG17+ | pg_explain の memory と serialize |
| PG18+ | 生成列の形式(stored と virtual)、NOT NULL 制約の有効性、pg_describe_table の conenforced/conperiod 制約メタデータ、pg_advisor の凍結対象範囲を示す relallfrozen。サーバー側では EXPLAIN ANALYZE の BUFFERS がデフォルトで有効です |
バージョンの確認に失敗した場合、サーバーは、拒否される可能性のある SQL を出力するのではなく、サポート対象のうち最も古い形式を前提とします。
ランタイム
公開されている postgres-mcp コマンドは、小さなランチャーです。見つかった中で最新の oam ランタイムを優先し、見つからない場合は Node にフォールバックします。
oam がなくても、何も変わりません――ただし、oam が必要な POSTGRES_MCP_SANDBOX が有効な場合は別です。この場合、oam がなければメッセージを表示してランチャーを停止します。フォールバック時にプロセスを再起動することはありません。npm はランチャーを実行するためにすでに Node を起動しているため、フォールバックでは同じプロセス内でサーバーを単純に import() します。existsSync の呼び出しが少し発生するだけで、サブプロセスは起動せず、Node で dist/index.js を直接実行した場合と同じように動作します。
使用する oam。 oam 0.18.0 以降(最新リリース)のみを使用します。ランチャーはインストール済みの場所($OAM_INSTALL_DIR が設定されている場合はその場所、Windows では %LOCALAPPDATA%\oam\bin と ~/.oam/bin、それ以外では ~/.oam/bin)および PATH を検索し、見つかったすべての oam にバージョンを問い合わせて、最も新しいものを実行します。同じバージョンの場合はインストール済みのものを優先します。古い oam は使用しません。Windows では oam.exe のみが対象です。oam.cmd / oam.bat のシムは実行されません。使用可能な oam が見つからない場合に限り、使用しなかったバイナリとシムが標準エラー出力に表示されます。新しい oam が実行された場合、それらについては何も表示されません。その際に表示される修正方法は、見つかった内容に応じたものです。古すぎる oam の場合は oam self-update、起動できない oam の場合はバイナリの確認、oam がまったくない場合に限り oamjs.org からのインストールが示されます。使用できない OAM_BIN は、ランチャーが oam を検索するたびに必ず表示されます。その後に新しい oam が実行される場合も同様です。ホストが oam 0.18.0 以降で oam run を使ってコマンドを起動すると、2つ目の oam を起動せず、その oam 内でサーバーが実行されます。ただし、POSTGRES_MCP_SANDBOX が有効な場合は別です。この場合、新たに起動した oam が必要で、起動できる oam がなければエラーで終了します。ホストの oam が 0.18.0 より古い場合、その oam 自身がサーバーを実行することはありません。最新の使用可能な oam に処理を引き継ぎ、それがなければ(サンドボックスが無効な場合)PATH 上の Node に引き継ぎ、それもなければエラーで終了します。
使用可能な oam がある場合、 サーバーはその上で実行されます。どちらのランタイムでも同等の動作を確認済みです。23個すべてのツールが登録され、クエリは同一の行と dataTypeName の値を返し、エラー時の動作も一致します。oam はドライバーに必要なすべての node: 組み込みモジュール(net、tls、crypto、dns など)を提供し、SCRAM 認証と拡張クエリプロトコルもどちらも動作します。
起動コストの測定値。 windows-arm64、1.4 MB のバンドル、postgres-mcp version(モジュール全体の初期化)、各バイナリを事前にウォームアップ、12回実行した平均値:
| 経路 | 起動時間 |
|---|---|
スタンドアロンバイナリ(oam compile) | 298ms |
oam run dist/index.js | 306ms |
node dist/index.js | 358ms |
| ランチャー -> Node(同一プロセス内) | 370ms |
| ランチャー -> oam(起動) | 409ms |
この環境では、oam は Node より高速に起動します。ランチャーのコストは 起動 にあります。oam に到達する前に Node がすでに起動しており、その切り替えにかかる時間(約100ms)は、oam の約52msの優位性より大きいためです。そのため、npm の bin 経由では両者の差は約40ms以内に収まり、POSTGRES_MCP_RUNTIME=node の優位性はわずかで、実質的な差ではありません。これらの数値は、マシン上に oam が1つだけある状態で測定しました。ランチャーは現在、見つかったすべての oam バイナリに対して --version を実行するため、コピーが1つ増えるごとに確認処理が追加されます。
どちらの場合も、これはツール呼び出しごとではなく、MCP セッションごとに1回限りのコストです。ホストはサーバーを1回起動し、そのまま起動状態を保ちます。起動時間が本当に重要な場合は、スタンドアロンバイナリを使えばランチャーを完全に回避でき、最も高速です。
この README の以前のリリースでは、Node の起動時間を約650~900ms、oam の起動時間を約980~1290msと報告し、それを根拠に oam を無効化することを推奨していました。これらの数値は、冷えた状態のビルド直後のバイナリを測定したもので、どちらのランタイムの性能ではなく、Windows のオンアクセス型ウイルススキャナーの影響を反映していました。数値の大きさも優劣の方向も誤っていました。0.9.1 で修正済みです。
{
"mcpServers": {
"postgres": {
"command": "npx",
"args": ["-y", "@yawlabs/postgres-mcp"],
"env": {
"DATABASE_URL": "postgres://...",
"POSTGRES_MCP_RUNTIME": "node" // opt out of oam
}
}
}
}
マネージド Postgres(Supabase、Neon、RDS など)への接続
ほとんどのマネージドデータベースでは TLS が必要ですが、Node のデフォルトの信頼ストアが認識しないプライベート CA によって署名された証明書が使われています。次のいずれかのエラーが発生します。
self signed certificate in certificate chainunable to get local issuer certificateunable to verify the first certificate
通信を暗号化したまま接続できるようにするには、env ブロックに POSTGRES_SSL_REJECT_UNAUTHORIZED=false を追加します。
"env": {
"DATABASE_URL": "postgres://user:pass@host:5432/db?sslmode=require",
"POSTGRES_SSL_REJECT_UNAUTHORIZED": "false"
}
これにより証明書チェーンの検証だけが無効になります。TCP 接続は引き続きエンドツーエンドで TLS 暗号化されます。CA をインストールできる本番環境では、検証をグローバルに無効にするのではなく、証明書を Node の信頼ストア(NODE_EXTRA_CA_CERTS)に登録することをおすすめします。
PG17 以降でラウンドトリップを減らす。 Postgres 17 ではダイレクト TLS ネゴシエーションが追加され、TLS の前に行われる平文の SSLRequest ハンドシェイクを省略できます。同梱のドライバーがこれに対応しているため、DATABASE_URL に sslnegotiation=direct を追加します。
postgres://user:pass@host:5432/db?sslmode=require&sslnegotiation=direct
デフォルトではなくオプトイン方式です。PG16 以前のサーバーは接続を拒否し、短縮できるのはプールされた接続ごとに 1 回のラウンドトリップだからです。離れた場所にあるマネージドデータベースでは効果がありますが、ローカル環境では違いが分かりません。
監査ログ
デフォルトでは無効です。env ブロックに POSTGRES_AUDIT_LOG=stderr を設定するか、POSTGRES_AUDIT_LOG_FILE のみを設定して有効にします。
"env": {
"DATABASE_URL": "postgres://...",
"POSTGRES_AUDIT_LOG_FILE": "/var/log/postgres-mcp/audit.jsonl"
}
サーバーは監査対象のステートメントごとに 1 行の JSON を書き出します。以下は PostgreSQL 17 から取得したログの 2 件で、パラメーター化クエリーに続いて、失敗したクエリーが記録されています。
{"ts":"2026-09-12T19:26:42.587Z","tool":"pg_query","source":"user","sql":"SELECT $1::int AS n","params":1,"ms":2.827,"rows":1,"ok":true}
{"ts":"2026-09-12T19:26:42.591Z","tool":"pg_readonly","source":"user","sql":"SELECT * FROM does_not_exist_xyz","params":0,"ms":2.217,"rows":null,"ok":false,"sqlstate":"42P01"}
| フィールド | 意味 |
|---|---|
ts | 行が書き込まれた時刻を示す ISO 8601 形式の時刻です。ステートメントの実行完了直後に記録されます。 |
tool | ステートメントを発行した MCP ツールです。サーバーのクエリ関数がツールハンドラーの外部から呼び出された場合に限り、記録されません。たとえば、プロセス内に組み込まれている場合です。 |
source | pg_query、pg_readonly、pg_explain に渡された SQL の場合は user です。それ以外はすべて internal です。サーバー独自のカタログクエリ(pg_describe_table を1回呼び出すと複数記録されます)、pg_kill のシグナル呼び出し(サーバーがそのステートメントを書き込み、エージェントが指定するのは pid のみです)、および pg_index_advisor が実行するすべてのステートメント(statements に渡されたワークロード SQL を含む)が該当します。user でフィルタリングしても、エージェントが渡した SQL がすべて表示されるわけではなく、pg_kill の呼び出しも表示されません。これらを絞り込むには tool でフィルタリングしてください。SQL として送信された kill(SQL を受け取るツール経由の SELECT pg_terminate_backend(...))は、そのツール名の user 行として通常どおり記録されます。 |
sql | 送信されたステートメントのテキストです。pg_explain の場合は、組み立てられたステートメントです。たとえば、EXPLAIN SELECT ...、またはオプションを指定した EXPLAIN (ANALYZE, BUFFERS) SELECT ... です。 |
sqlKeyword, sqlSha256 | POSTGRES_AUDIT_REDACT が有効な場合に sql の代わりに記録されます。先頭の空白または ( の後に続く最初の英字列を大文字にしたもの(コメントなど、それ以外のものが先頭にある場合は UNKNOWN)と、テキスト全体の 16 進数形式の SHA-256 ハッシュです。キーワードはステートメントの効果ではなく先頭の単語です。ログを削除する CTE も WITH となり、すべての pg_explain 行は EXPLAIN と記録されます。 |
params | 渡されたバインドパラメーターの数です。値ではなく、個数のみを記録します。 |
ms | 監査対象のステップにかかった実時間のミリ秒数であり、サーバーの実行時間ではありません。user 行では、接続の取得からステートメントの完了までを計測するため、プール接続の待機、BEGIN、pg_explain の仮想インデックスのセットアップ、行数上限付きカーソルの往復が含まれます。internal 行には、プール接続の待機や、同じ呼び出し内の他のカタログクエリの完了待ちが含まれる場合があります。 |
rows | 失敗時は null です。それ以外の場合は、postgres が報告した件数、または報告がない場合は返された行数です(SET は 0 と記録されます)。上限付きの SELECT では、最大で POSTGRES_MAX_ROWS + 1 件が報告されます。超過を検出するため、追加の1行が含まれます。カーソルを使用できないステートメント(EXPLAIN、SHOW、... RETURNING)では、応答が切り詰められていても全件数が報告されます。 |
ok | ステートメントが実行され、成功したかどうかです。呼び出しの失敗が先に発生したためにステートメントが送信されなかった場合も false になります。たとえば、接続が拒否された、BEGIN が失敗した、または pg_explain が仮想インデックスを作成できなかった場合です。この場合、行にはその失敗のコードが記録されます。PostgreSQL 自身のログには、そのようなステートメントの記録はありません。エージェントの SQL では、true であってもコミットされたことを意味しません。後続の COMMIT は記録されないため、コミットに失敗した書き込みでも true と記録されます。 |
sqlstate | ok が false で、エラーにコードが含まれる場合に限り記録されます。通常は 42P01 のような5文字の SQLSTATE ですが、接続に失敗した場合は代わりに Node のエラーコード(ECONNREFUSED、ENOTFOUND)が記録されます。接続タイムアウトの場合は記録されず、DATABASE_URL がない場合も同様です。 |
どのモードでも記録されない情報: バインドパラメーターの値(個数のみ)とエラーメッセージ(コードのみ)です。postgres はエラーメッセージ内で問題の値を引用するためです。ステートメントのテキストは記述されたとおりに記録されるため、機密性の高いリテラルは pg_query、pg_readonly、または pg_explain に params として渡してください。pg_index_advisor は params を受け取りません。代わりに、statements 内で $1 形式のプレースホルダーを使用してください(PostgreSQL 16 以降では GENERIC_PLAN を使用して計画されます)。
値は厳密に解析されます。 Configuration の表で許容される値(yes、on、hash)以外を指定すると、off として読み込まれるのではなく、サーバーは起動時に停止します。これは、監査がオフの POSTGRES_AUDIT_REDACT でも同様です。監査制御がひそかに無効になるのは、制御がないよりも悪いことです。大文字と小文字の違い、および前後の空白は無視されます。変数が設定されていても値が空、または空白のみの場合も、サーバーは停止します。通常これは、MCP クライアントの実行環境で変数が設定されていないのに、"POSTGRES_AUDIT_REDACT": "${AUDIT_REDACT}" のような env エントリがあることを意味します(Windows では、bash または WSL のプロファイルにある変数は、cmd 経由で起動したサーバーには引き継がれません)。値を未設定として読み込むと、SQL 全文がログに記録されてしまいます。未設定と見なされるのは、変数がまったく設定されていない場合だけです。どちらかのフラグをオフのままにするには、env ブロックから削除するか、0 を設定してください。
ファイル出力先。 POSTGRES_AUDIT_LOG_FILE だけで監査が有効になり、両方が設定されている場合は、警告なしで POSTGRES_AUDIT_LOG=stderr より優先されます。他の 2 つの変数と同様に、空または空白のみの値は未設定ではなくエラーです。パスは既存のディレクトリ内のファイルを指定する必要があります。ファイルが存在しない場合は作成され、起動時に追記モードで開かれ、プロセスの終了まで開いたままになります。絶対パスを使用してください。相対パスは、MCP クライアントがサーバーを起動したディレクトリを基準に解決されます。後から書き込みに失敗しても(ディスク容量不足など)、クエリが失敗することはありません。警告が 1 回 stderr に出力され、その後の行も書き込みが試行されます。
stderr では、 監査行は起動バナーや警告と混在するため、JSON ではない行は読み飛ばしてください。ファイル出力先には監査行だけが記録されます。stdout は使用されません。これは MCP プロトコルの通信チャネルです。
墨消しで隠せるのはテキストであり、推測ではありません。 同一のステートメントは同じハッシュになるため、引き続き件数を数えたり関連付けたりできます。そのため、ログを持つ人なら誰でも、推測したステートメントを照合できます。したがって、予測可能なクエリ内の短いリテラルは復元可能です。機密情報を含む値には、バインドパラメーターを使用してください。
POSTGRES_MCP_SANDBOX の下では、 サンドボックスがファイルシステムを拒否するため、ファイル出力先を開けず、サーバーは起動時に終了します(could not be opened for append: Access to this API has been restricted)。この環境では POSTGRES_AUDIT_LOG=stderr を使用してください。3 つの監査変数はいずれもサンドボックスを通過します。
監査ログに記録されない情報。
pg_killがどのバックエンドにシグナルを送ったか。 シグナルを送るたびに、1 行が記録されます。tool: "pg_kill"、source: "internal"、params: 1、およびsqlはSELECT pg_cancel_backend($1) AS signaledまたはSELECT pg_terminate_backend($1) AS signaledのいずれかです。ただし、pid はバインドパラメーターであるため、ログに記録されるのはその数だけです(値は PostgreSQL 自体のログに記録されることがあります。このセクションの最後を参照)。対象がこのサーバー自身のステートメントの 1 つで、シグナルが有効になった場合、そのステートメントは独自のok: false行を書き込みます(通常、キャンセルではsqlstateが57014、終了では57P01です)。2 行を対応付ける手掛かりはtsだけです。ループバック TCP 経由でローカルサーバーを測定したところ、対象の行は通常、pg_killの行と同じミリ秒か、その次のミリ秒を示し、CPU 負荷の高い対象を終了するには最大で数百ミリ秒かかりました。割り込みチェックに到達するまで時間のかかる対象では、さらに長くかかることがあります。対象の行は通常 2 行のうち後の方ですが、まれに先になることもあります。時間が近いことは証拠ではありますが、確証ではありません。ステートメントタイムアウトも57014を記録するためです。POSTGRES_AUDIT_REDACTの場合、どちらのモードもsqlKeywordはSELECTになるため、上記 2 つのテキストのハッシュを使ってキャンセルと終了を判別してください。末尾に改行を付けず、シェルでシングルクォートで囲んで$1がそのまま残るようにします。pg_killのシグナルが有効になったかどうか。okが示すのはpg_kill自身のステートメントの結果であり、返されたsignaledの値は記録されません。ok: trueは、シグナルが届いた場合、pid がバックエンドではない場合(postgres はfalseを返します)、および何も変化しなかったキャンセルの場合を含みます。postgres がtrueを返すのはシグナルを送信した時点であり、実際に有効になった時点ではありません。たとえば、トランザクション中にアイドル状態のセッションはシグナルを無視します。42501を伴うok: falseは拒否を示します。そのロールには、当該バックエンドにシグナルを送る権限がない可能性があります。pg_kill自身の行に57014または57P01を伴うok: falseがある場合は、pg_killの実行中の接続を pid に指定したときなどに、pg_kill自身のステートメントがキャンセルまたは終了されたことを意味します。終了後は、その行がok: falseと示していても、当該バックエンドは消失しています。ALLOW_WRITES=1が設定されていないために拒否された呼び出しは、何も送信せず、何も記録しません。- エージェントの SQL を取り巻く処理(
BEGIN READ ONLY、行数制限用のセーブポイントとカーソル、終了時のCOMMIT/ROLLBACK)、dataTypeNameのためのpg_type検索、pg_explainのhypothetical_indexesのためのhypopg_create_index/hypopg_reset呼び出しは、いずれも独自の行を記録しません。失敗したBEGIN、セーブポイント、hypopg_create_indexは、そのステートメント自体のok: false行に記録されます(上記のokを参照)。これは、そのステートメントが送信されないためです。一方、失敗したpg_type検索は記録されません。ステートメントはすでに実行されており、検索は結果に情報を付加するだけだからです。例外はpg_index_advisorで、トランザクション制御や各ワークロードステートメントのEXPLAINなど、送信するものはすべて記録されます。一部のツールが最初に実行するサーバーバージョンのプローブSELECT current_setting('server_version_num') AS vは通常のinternal行です。読み取りが成功するとプロセスの存続期間中キャッシュされるため、成功した読み取りごとに 1 回だけ記録されます(最初の読み取りと競合する呼び出しは同じ結果を共有します)。サーバーに到達できない間は、失敗がキャッシュされないため、プローブを行うたびに記録されます。 - 呼び出しが組み立てる段階まで到達しなかったステートメント。すべてのツールは、接続が拒否された場合、送信しようとしていた最初のステートメントの
ok: false行として記録します。行には各ツールのtoolが設定され、sqlstateには Node のエラーコードが入るか、接続タイムアウトの場合は値が入りません。pg_queryとpg_readonlyではエージェントの SQL、pg_explainではEXPLAIN自体が記録されます。ただし、バージョン下限のあるオプション(settings、wal、generic_plan、memory、serialize、analyzeなしのbuffers)またはhypothetical_indexesが渡された場合は、バージョンプローブまたは HypoPG チェックの行が記録されます。これらの後に組み立てられるEXPLAINはその行に含まれません。pg_killではシグナルを送るステートメント、その他のツールではバージョンプローブまたは最初のカタログクエリが記録されます。後者には、複数のクエリで 1 つの接続を共有するツール(pg_describe_table、pg_health、pg_advisor、pg_replication_status、pg_seq_scan_tables、pg_unused_indexes、pg_io_stats)も含まれます。接続をチェックアウトするのは最初のクエリです。 PostgreSQL 自身のログは、サーバー側の完全な記録です。log_statement = 'all'では、実行に到達したすべてのステートメントが記録され、デフォルト値がerrorのlog_min_error_statementでは、それより前の段階で失敗したステートメントも追加で記録されます。また、この記録から漏れるバインドパラメーターの値と、完全なエラーメッセージも正確に記録されます。
トラブルシューティング
DATABASE_URL is not set - MCP クライアントが、環境変数を指定せずにサーバーを起動しています。特に Windows では、bash/PowerShell のプロファイルで設定した環境変数は、cmd 経由で起動された MCP サーバーには継承されません。DATABASE_URL は .mcp.json の env ブロックに直接指定してください。
password authentication failed - ユーザー名とパスワード、およびそのユーザーにデータベースへの CONNECT 権限があることを確認してください。パスワードに特殊文字が含まれる場合は、URL エンコードしてください(@ → %40、# → %23、/ → %2F)。
SASL: SCRAM-SERVER-FIRST-MESSAGE: client password must be a string - 接続文字列のパスワードが空か、URL デコード後に null になっています。接続文字列を確認してください。
canceling statement due to statement timeout - 単一のクエリが POSTGRES_STATEMENT_TIMEOUT_MS(デフォルトは 30s)を超えました。この値を大きくするか、WHERE でクエリの対象を絞るか、インデックスを追加してください。これは想定された動作です。このタイムアウトは、処理が止まらなくなったクエリによってエージェントがハングしないように設けられています。
Write blocked: this server is in read-only mode - pg_query 経由でエージェントに書き込みを依頼しましたが、ALLOW_WRITES が設定されていません。.mcp.json の env ブロックに ALLOW_WRITES=1 を追加して MCP クライアントを再起動する(開発/テスト用データベースの場合)か、本番環境では、DATABASE_URL に INSERT/UPDATE/DELETE 権限を付与したロールを使用し、ALLOW_WRITES は設定しない方法がより適切です。アクセスの設定を参照してください。なお、pg_readonly は常に書き込みを拒否します。書き込みを行う場合は、呼び出しを pg_query 経由にする必要があります。
起動時の Error: [postgres-mcp] POSTGRES_AUDIT_... または Error: [postgres-mcp] audit log file ... could not be opened for append - 監査ログ用の変数の設定に誤りがあるため、サーバーは推測で処理を続けず、スタックトレースを出力して終了します。Error: の行に該当するケースが記載されています。認識されない値(受け付け可能な値が一覧表示されます)、設定されているものの空の変数(通常は MCP 設定内の未展開の変数です。env ブロックから削除するか、フラグには 0 または 1、ファイルにはパスを指定してください)、POSTGRES_AUDIT_LOG がオフなのにファイルが設定されているケース、またはファイルを開く際の OS エラーです。ENOENT は通常、ディレクトリが存在しないことを意味し、Access to this API has been restricted は POSTGRES_MCP_SANDBOX=1 を意味します。監査ログを参照してください。
postgres-mcp: POSTGRES_MCP_SANDBOX=... refusing to start without the sandbox - サンドボックスが要求されましたが適用できないため、ランチャーはサンドボックスなしでサーバーを実行せずに停止します。最初の行に該当するケースが、最後の行に対処方法が記載されています。POSTGRES_MCP_RUNTIME=node(Node には --permission がありません)、使用可能な oam 0.18.0 以降がない(インデントされた行に、検出されたものと、それぞれが選ばれなかった理由が記載されます)、oam の起動に失敗した、またはデータベースのエンドポイントを固定できないケースです。これには Unix ソケット(oam は接続できますが、サンドボックスで許可できるのは TCP のホストとポートのみです。TCP 経由で接続するか、POSTGRES_MCP_SANDBOX の設定を解除してください)、複数のホスト、1-65535 の範囲外のポート、空白文字または ASCII 以外の文字を含むホスト(xn-- 形式を使用してください)、読み取り可能な postgres:// URL ではない DATABASE_URL が該当します。DATABASE_URL 自体が出力されることはありません。postgres-mcp: POSTGRES_MCP_SANDBOX="..." is not a recognized value は、値が正しくないことを意味します。1 または true でサンドボックスが有効になり、0、false、または off で無効になります。
PgBouncer のトランザクションモードまたは pglite-socket で接続プールが枯渇する - これらのバックエンドは、1 つの接続での並列クエリに対応していません。env ブロックに POSTGRES_POOL_MAX=1 を設定してください。
最初のクエリは遅く、後続のクエリは速い - 想定された動作です。pg ドライバーは最初の接続を遅延して確立し、後続のクエリではプールを再利用します。
開発
実際の Postgres に対して、ユニットテストと統合テストの全スイートを実行します。
DATABASE_URL='postgres://user:pass@host:5432/db' POSTGRES_MCP_INTEGRATION=1 npm run test:integration
統合テストスイートでは、破棄可能なデータベースを使用することを前提としています。test_fixture スキーマを作成して削除するため、大切なデータがあるデータベースは指定しないでください。
実際の oam に対して POSTGRES_MCP_SANDBOX のパスも実行するには、--permission のもとでランチャー経由のハンドシェイクと select 1 を行い、続いて固定されたポートの隣のポートに接続して ERR_ACCESS_DENIED で拒否されることを確認します。DATABASE_URL と同じ場所にある OAM_BIN を設定してください。どちらも設定されていない場合、このファイルはスキップされ、書き込みも行いません。
OAM_BIN=/path/to/oam DATABASE_URL='postgres://user:pass@host:5432/db' npm run test:integration
破壊的なテスト(REVOKE/制限付きロールのパス)も実行するには、POSTGRES_MCP_DESTRUCTIVE_TESTS=1 を追加します。破棄可能なクラスターでのみ安全です。
DATABASE_URL='postgres://user:pass@host:5432/db' POSTGRES_MCP_INTEGRATION=1 POSTGRES_MCP_DESTRUCTIVE_TESTS=1 npm run test:integration
Windows:WSL2 経由での統合テスト
Windows ARM64 でのネイティブ Postgres は不安定です(UCRT ランタイムの不足や ARM64 ビルドの欠如)。信頼できる方法は、WSL2 上の破棄可能な Ubuntu 内で統合テストスイートを実行することです(WSL2 の NAT により、Windows ホストから :5432 に接続できないため、PowerShell からテストを実行しないでください)。
wsl --install -d Ubuntu --no-launch
# reboot, then:
wsl -d Ubuntu -u root bash -c "apt-get update && apt-get install -y nodejs npm rsync"
wsl -d Ubuntu -u root bash /mnt/c/path/to/postgres-mcp/scripts/wsl-pg-setup.sh
wsl -d Ubuntu -u root bash /mnt/c/path/to/postgres-mcp/scripts/wsl-test-matrix.sh
wsl-pg-setup.sh は PGDG apt リポジトリから PG15、PG17、PG18 をインストールします(ポートは pg_createcluster によって自動割り当てされます。通常は 17 が 5432、18 が 5433、15 が 5434 です)。postgres のパスワードを postgres に設定し、それぞれに postgres_mcp_test を作成します。wsl-test-matrix.sh は作業ツリーを /root/postgres-mcp に rsync し、npm ci を一度実行してから、pg_lsclusters で見つかったすべてのクラスターに対して統合テストスイートを実行します。
PowerShell の代わりに Git Bash から実行しますか? 両方のスクリプト呼び出しの前に MSYS_NO_PATHCONV=1 を付けてください。Git Bash は wsl.exe に渡される前に /mnt/c/... 引数を書き換えるため、スクリプトには C:/Users/<you>/scoop/apps/git/<ver>/mnt/c/... として渡され、何も実行されないまま bash が「No such file or directory」で終了します。また、どちらのスクリプトも tail/head にパイプしないでください。パイプラインの終了ステータスは最後のコマンドのものになるため、マトリックスの失敗が成功として報告されます。
完了したら、wsl --unregister Ubuntu を実行して環境を削除してください。
ライセンス
MIT © 2026 YawLabs
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。