PostgreSQL MCP サーバを専用読み取り専用ロールで運用
SELECT しかできない Postgres ロールを作成し、postgres MCP サーバの接続文字列ユーザにします。サーバ自体が読み取り専用でも防御を多層化します。
Security
詳細な説明
なぜ専用ロールを作るか
postgres MCP サーバはプロトコルレベルで「書き込み禁止」を強制します — query ツールしか公開せず、それを read-only トランザクションでラップしています。しかしプロトコルレベルのフィルタにもバグはありえます。堅牢なパターンは、接続文字列に 物理的に誤動作できない DB ユーザを与えることです。
DB 側の一度きりセットアップ
-- ロール作成
CREATE ROLE mcp_readonly WITH LOGIN PASSWORD 'use-a-strong-password';
-- DB 接続を許可
GRANT CONNECT ON DATABASE myapp TO mcp_readonly;
-- スキーマ列挙を許可
GRANT USAGE ON SCHEMA public TO mcp_readonly;
-- 既存テーブルへの SELECT
GRANT SELECT ON ALL TABLES IN SCHEMA public TO mcp_readonly;
-- 将来追加されるテーブルへの SELECT を自動付与
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT ON TABLES TO mcp_readonly;
-- シーケンス読み取り(SERIAL カラムのイントロスペクション)
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO mcp_readonly;
設定
{
"mcpServers": {
"postgres": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-postgres",
"postgresql://mcp_readonly:STRONG-PASSWORD@localhost:5432/myapp"
]
}
}
}
この対策が防ぐもの
- MCP サーバのバグで誤って DELETE が通ってしまうケース。
- モデルが「ハルシネーション」でツール名を発明し、読み取りチェックを回避するケース。
- サーバの将来アップデートで、未許可の書き込みツールが追加されるケース。
接続自体の堅牢化
非ローカル DB なら接続文字列に ?sslmode=require を追加します。本番に近い読み取り用途なら、プライマリではなく読み取りレプリカを指してください。モデルのクエリは重くなることがあり、ユーザートラフィックと競合させたくありません。
マルチ DB 構成
複数 DB のプロジェクトでは、名前を変えて複数の postgres MCP エントリを並べます:
"postgres-prod-replica": { ... },
"postgres-staging": { ... }
ユースケース
「サーバが読み取り専用と謳っているから安全」では足りない本番系で、バグや侵害があってもサーバが書き込めないという多層防御が必要なときの構成です。