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": { ... }

ユースケース

「サーバが読み取り専用と謳っているから安全」では足りない本番系で、バグや侵害があってもサーバが書き込めないという多層防御が必要なときの構成です。

試してみる — MCP Server Config Generator

フルツールを開く