pgbot は PostgreSQL の統計ビューを読み取り専用で読み、「何が悪いか」を所見として返す Go 製の CLI だ。README は自らを「データベースの中からの可観測性(in-database observability)」と呼び、常駐のエージェントも外部サービスも、書き込み権限も要らないと書く。2026-10-18 時点の GitHub Star は1,416、fork は67、最新リリースは v0.8.1(2026-09-06)。本記事では v0.8.1 のリリース版と main(b38f5e9)のソースビルドを PostgreSQL 16.15 に向けて動かし、読み取り専用の主張、前回との差分、CI の終了コード、--json のスキーマ、MCP サーバーを確かめた。

WHAT IT REPLACES何を代替するか

統計ビューを読む作業を1回の接続と終了コードにまとめる

これまでの読み方

SQL を書くか、常駐の道具を置く

  • pg_stat_* の SQL を自分で書いて読む閾値と前回の値は自分で持つ
  • PgHero は Web ダッシュボードを置くDocker か Rails エンジンで常駐
  • pgBadger はログファイルを解析するサーバーのログ設定が前提

pgbot 0.8.1

Go 製の静的バイナリ 1本

  • pg_monitor ロールで接続して所見を出す当サイト実測:inspect 1回 約5秒
  • 終了コード 0・1・2・3・64 で CI を止める--fail-on で止める重さを選ぶ
  • --json と MCP の 12 ツールで渡すAI の説明は鍵を入れたときだけ
出典: pgrundev/pgbot README(main b38f5e9)・PgHero / pgBadger の README・当サイト実測(2026-10-18・PostgreSQL 16.15)

30秒でわかる pgbot(2026-10-18時点)

  • ・正体:統計ビューを読んで所見と健康スコアを出す Go 製の静的バイナリ。対応は PostgreSQL 14〜18(16〜18 が正式、14・15 はベストエフォート)
  • ・実測:inspect 1回は約5秒。サーバーに記録された205文に書き込みは0文で、36回すべて BEGIN READ ONLY だった
  • ・CI:終了コードは 0(問題なし)・1(警告)・2(重大)・3(接続失敗)・64(引数の誤り)。手元の5つとも一致した
  • ・AI:pgbot mcp は12ツールを出す。任意の SQL を実行するツールは無い
  • ・注意:README は main の姿を書く。--json の契約 1.5.0 は未リリースで、v0.8.1 が出すのは 1.2.0

DB の運用を道具に任せる全体像は AI自動化ツール|ノーコードからコードまで2026年版の比較と選び方 にまとめている。同じ開発元(pgrundev)の CLI では、Postgres の論点を読ませる pgbookとは|Postgresのロック・VACUUM・JSONBをターミナルで読むGo製CLIを実測 も扱った。

pgbotの役割と所見の目録

PostgreSQL は自分の状態を pg_stat_statements・pg_stat_user_tables・pg_stat_user_indexes などのビューで公開している。材料は揃っているが、毎回 SQL を書き、閾値を決め、前回の値と比べるのは人の仕事になる。pgbot はその読み方を Go のコードに固定し、結果を所見として並べる。

所見の目録(docs/findings/)は61ページある。使われていないインデックス、デッドタプルの比率、autovacuum の停止、トランザクション ID の周回、レプリケーションの遅れ、遅くなったクエリなどで、どのページにも「自分で確かめる読み取り専用の SQL」と「無視してよいとき」が書いてある。

名前の近い別物は GitHub にいくつかあるが、規模が小さい。

リポジトリ 中身 Star(2026-10-18)
pgrundev/pgbot(本記事) PostgreSQL の読み取り専用の診断 CLI(Go) 1,416
mybuddymichael/pgbot IRC のボット(Clojure) 18
mindcrime/pgbot AIML で書いた対話ボット 6

更新の頻度も見ておく。リポジトリの作成は2026-08-11で、main のコミットは8月に232件、9月に78件、10月は12日までに16件。最後のリリース v0.8.1 のあと main は60コミット進み、PostgreSQL のドライバの入れ替え(pgx から自作の pgGo へ)や所見の追加が「Unreleased」のまま積まれている。LICENSE ファイルは Apache-2.0 の利用条件の告知(738バイト)だけなので、GitHub の表示は「Other」になる。

pgbotを動かす:pg_monitorロールとpg_stat_statementsの準備からinspectまで

README は curl | sh のスクリプト、npm(npx @pgbot/cli)、Homebrew、Docker、Go を挙げる。ここではリリースの tarball をチェックサムで確かめて展開した。npm 版も npx -y @pgbot/cli --version で pgbot version 0.8.1 を返した。

curl -fsSLO https://github.com/pgrundev/pgbot/releases/download/v0.8.1/pgbot_0.8.1_linux_amd64.tar.gz
curl -fsSLO https://github.com/pgrundev/pgbot/releases/download/v0.8.1/checksums.txt
grep 'linux_amd64.tar.gz$' checksums.txt | sha256sum -c -
tar xzf pgbot_0.8.1_linux_amd64.tar.gz pgbot
./pgbot --version

次に監視用のロールを作る。pgbot init は CREATE ROLE と GRANT の SQL を表示するだけで、自分では実行しない(実行前後で pg_roles に pgbot_ro が無いことを確かめた)。パスワードの仮の値を置き換えて、管理者の接続で流す。

ADMIN_URL="postgres://[email protected]:5432/shop"   # 管理者の接続先(自分の値に置き換える)
PGBOT_PW="change-me-strong-password"                   # 監視用ロールのパスワード(自分の値に置き換える)
./pgbot init "$ADMIN_URL" | sed "s/REPLACE-WITH-A-STRONG-PASSWORD/$PGBOT_PW/" | psql "$ADMIN_URL"
export DATABASE_URL="postgres://pgbot_ro:$PGBOT_PW@127.0.0.1:5432/shop"
./pgbot init --verify
./pgbot inspect

作られるのは pg_monitor と CONNECT だけを持つロールだ。クエリの集計には pg_stat_statements を shared_preload_libraries に入れて CREATE EXTENSION しておく(init の出力にも手順が出る)。init --verify は pg_monitor・pg_stat_statements・プライマリかどうかの4行を ok で返した。

試したのは20万行の orders と10万行の events を持つ小さな DB で、events は autovacuum を止めて更新を重ね、orders には使わないインデックスを置いた。

統計が15分たまるまで出ない所見がある

  • ・起動直後の inspect は「Counter-based findings ... are suppressed until the window exceeds 15m」と出し、unused indexes・キャッシュヒット率・シーケンシャルスキャンの所見を抑える
  • ・15分を過ぎた実行で「2 unused index(es) · 2.8 MiB」が出た。pgbot indexes の内訳は events_kind_idx(1.5MiB)と orders_created_idx(1.3MiB)で、3回使った orders_note_idx と主キーは出なかった
  • ・所見には「DROP の前に、レプリカを含む全ノードと定期ジョブで使われていないか確かめる」の注意が付く。プライマリのスキャン数はそのノードの分だけだからだ

pgbotは本当に読み取り専用か:送った205文を数えた

README は「読み取り専用はフラグではなくロールで守る」と書く。ロールに書き込み権限が無いことを境界とし、セッションの固定(default_transaction_read_only・statement_timeout=15s・lock_timeout=2s)と BEGIN READ ONLY は「その上の多層防御」という位置づけだ。ソースでも internal/conn/connect.go が接続ごとにこの4つの設定を入れている。

まずロールを確かめた。pgbot_ro で psql から INSERT を打つと permission denied for table orders、CREATE TABLE は permission denied for schema public で止まる。pg_monitor は統計ビューを読めても、表の中身は SELECT すらできない。

READ-ONLY GATE実測した判定

統計は読めて、書き込みは役割と pgbot の両方で止まる

pgbot_ro の接続

  • 読むpg_stat_statements を SELECT 29行を返すpg_monitor の権限
  • 書くINSERT INTO orders permission deniedpsql から pgbot_ro で
  • 計画explain_plan に DELETE pgbot が受け付けないMCP のツール呼び出し
  • 計画explain_plan に SELECT permission denied表を読む権限が無い役割
出典: 当サイト実測(2026-10-18・PostgreSQL 16.15・pgbot 0.8.1 と main b38f5e9)。explain_plan は表の SELECT 権限を持つ接続でだけ計画を返した

次に、pgbot が実際に何を送ったかをサーバー側で数えた。pgbot_ro にだけ log_statement = 'all' を設定して main ビルドの inspect を1回流すと、記録は205文。内訳は SELECT(WITH で始まる読み取り2文を含む)93、BEGIN READ ONLY 36、COMMIT 36、SET 40で、INSERT・UPDATE・DELETE・DDL・VACUUM・ANALYZE は0文だった。最初の1本でプーラーの有無を確かめたあと、4本の接続に設定を固定して読んでいる。README の「接続プールの上限は4」と合う。

WHAT IT SENT送った文を数えた

inspect 1回が送った205文に、書き込みは0文

205文

サーバーが記録した文

log_statement=all を pgbot_ro にだけ設定

36回

BEGIN READ ONLY

同じ数だけ COMMIT

0文

INSERT・UPDATE・DDL など

残りは SELECT 93・SET 40・COMMIT 36

出典: 当サイト実測(2026-10-18・PostgreSQL 16.15・pgbot main b38f5e9 の inspect --no-store 1回)

ただし、この数え方が示すのは「今回の inspect が書き込みを送らなかった」ことまでだ。超ユーザーで接続すれば、ロールという一番外の境界は消える。README も CI 用の接続に「superuser は使わない」と書いている。pgbot が端末側に書くものはあり、ベースラインが $XDG_STATE_HOME/pgbot/baselines.db(SQLite。7日分は間引かずに残し、90日までは1時間ごとに間引く。上限100MB)にたまる。

前回との差分とCI:pgbotの終了コードと–json

inspect は実行のたびにスナップショットをベースラインに残し、2回目からは pgbot diff が接続なしで比べる。2分あけて2回流すと、diff は「1分前を頼まれたが、近いのは2分前のスナップショットなのでそれと比べる」と比べた区間を正直に出し、public.events のデッドタプル比率が 0 から 0.6 に上がったことと、上位に入ったクエリ3本を挙げた。統計のリセットをまたぐ比較は抑える(main の最新コミットもこの修正)。

直した前後も比べた。15分を過ぎた状態の inspect は健康スコア65、重大2件・警告4件で終了コード2。autovacuum を戻して events を VACUUM し、使っていない2つのインデックスを消して流し直すと、スコア91・重大0件で終了コード1になった。消えたのは autovacuum の2件、デッドタプル比率、unused indexes で、手を付けていない WAL アーカイブと HOT 更新率の所見は残った。このときの差分は既定の画面には出ず、inspect --full の「CHANGES since」と --json の deltas に入る。15分前と比べて、public.orders のシーケンシャルスキャンが10回から1.6万回に増えたことを「インデックスの経路を失ったクエリがあるかもしれない」と挙げた(インデックスの無い列で数える負荷をその間ずっと流していた)。

CI で使うのは終了コードだ。手元で5つとも README どおりに返った。

状況 終了コード
--fail-on=none 0
警告だけ(重大を設定で抑止したとき) 1
重大あり(autovacuum を止めた表がある) 2
接続できない 3
知らないフラグ 64

.pgbot.toml の [[ignore]] で重大な所見を抑止すると、終了コードは2から1に下がった。抑止した重大な所見は消えず「suppressed by config: 理由」付きで表示に残り、--json にも suppressed: true で残る。--format=sarif は SARIF 2.1.0 を出し、GitHub Action(pgrundev/pgbot@v1)はこれを Security タブに上げる。Action は未検証。

./pgbot inspect --fail-on=critical --format=sarif > pgbot.sarif; echo "exit=$?"
printf '%s\n' \
  '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"check","version":"0"}}}' \
  '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
  | ./pgbot mcp 2>/dev/null | tail -n 1 \
  | python3 -c 'import json,sys; print([t["name"] for t in json.load(sys.stdin)["result"]["tools"]])'

--json は版つきの契約で、schema/ に JSON Schema がある。v0.8.1 の出力は schema_version 1.2.0、main は 1.5.0 で、どちらも同じ版のスキーマで検証エラー0件だった(陰性対照として型を壊した文書は2件のエラーになった)。ひとつ注意がある。1.2.0 のスキーマは最上位で未知のキーを許さないので、1.5.0 の出力を 1.2.0 で厳密に検証すると collation・replica_identity が「想定外」としてエラーになる。README の「1.4.0 の利用者は 1.5.0 をそのまま読める」は、スキーマで厳密に検証しない読み手の話と読むのが安全だ。

inspect に所見が出なくても、DB が健全とは限らない。pgbot が見るのは SQL で読める統計だけで、README も CPU・ディスク IOPS・空きメモリは SQL 接続からは見えないと書いている。

MCPとAIの層:pgbotがエージェントに渡すもの

上のコードの後半は pgbot mcp に initialize と tools/list を送ってツール名を並べる。v0.8.1 と main のどちらも12ツールを返した。

種類 ツール
所見と集計 inspect・unused_indexes・top_queries・vacuum_health
計画と提案 explain_plan・suggest_indexes(hypopg が必要)・schema_of
履歴 compare_to_baseline・why
コードとの突き合わせ index_code_correlation・record_index_verdict
説明 explain_finding

プロンプト diagnose とリソース pgbot://baselines も出る。任意の SQL を流すツールが無いのが、Postgres を操作する MCP サーバーとの大きな違いだ。explain_plan に delete from orders、select 1; delete from orders、データを変える CTE を渡すと、3つとも「1文のプレーンな SELECT しか説明できない」で拒否した。データを変える CTE は超ユーザーの接続を渡して試しても同じ拒否で、orders の行数は20万のままだった。

一方で、pgbot_ro の接続では SELECT の explain_plan も permission denied for table orders で失敗した。計画を立てるにも表の SELECT 権限が要るからだ。explain_plan や advise を使うなら、どの表を読ませるかを別に決める必要がある。なお、tools/list の各ツールに MCP の readOnlyHint の注釈は付いていなかった。record_index_verdict は DB には触れないが、端末のベースラインに書く。

AI の説明は任意で、pgbot ask と pgbot explain だけが外へ送る。キーが無ければ ask は終了コード3で止まる。送る中身を確かめるため、手元に OpenAI 互換の偽サーバーを立てて ask を向けた。送られたのはシステム指示 1,732字と所見の JSON を含む 7,278字で、DB 名・表名・PostgreSQL の版は入っていたが、接続先のホスト、ロール名、パスワード、クエリに埋めた文字列や数値は入っていなかった。実在するモデルの回答の質は未検証。

pgHero・pgBadger・Postgres MCP Proとの違い

  pgbot 0.8.1 PgHero pgBadger Postgres MCP Pro
形 1回ずつ走らせる CLI Web ダッシュボード(Docker か Rails エンジン) Perl 製のログ解析 MCP サーバー
読むもの 統計ビュー 統計ビュー サーバーのログファイル 統計ビューと SQL の実行結果
DB 側に作るもの 無し(ロールは自分で作る) クエリ統計の履歴を残すなら専用の表 無し(ログ設定は要る) README の記載では無し
エージェント向け MCP 12ツール・SQL 実行なし README に記載なし README に記載なし execute_sql を含む。restricted で読み取り専用
本記事で動かしたか 動かした 未検証 未検証 未検証

README 自身も、ダッシュボード・アラート・長期保存が要るなら pganalyze・Percona PMM・pgwatch を使うよう勧め、pgbot は「置き換えない」と書く。常時見張る道具ではなく、10秒で答えが欲しいとき、自分の管理していない DB を調べるとき、エージェントに構造化した所見を渡したいときの道具だ。同じ開発元の道具では論理レプリケーションでデータを別の Postgres へ運ぶ pgsync実測|Postgres論理レプリケーションのメモリ上限を同梱ベンチで検算した もあり、pgbot が「状態を読む」側、pgsync が「データを動かす」側を受け持つ。

未検証の範囲

  • ・PostgreSQL 14・15・17・18、レプリカ、RDS・Aurora・Neon・Supabase などのマネージド環境
  • ・curl | sh のインストーラ(cosign 署名の検証)、Homebrew、Docker イメージ、GitHub Action
  • ・実在するモデルでの ask・explain、hypopg を使う advise、4回以上の履歴が要る why
  • ・大きな本番 DB での所要時間と負荷。測ったのは20万行の小さな DB

この記事の結論

  • ・pg_monitor のロール1つで、統計ビューの読み方を所見と終了コードにまとめてくれる
  • ・inspect 1回の205文に書き込みは無かった。境界はロールなので、超ユーザーで繋がない
  • ・MCP は12ツールで任意の SQL を出さない。表の権限が無いと explain_plan は使えない
  • ・README は未リリースの main を書く。入るのは v0.8.1 なので、スキーマの版を確かめてから組み込む

参照ソース

・pgrundev/pgbot(GitHub):README・CHANGELOG・docs/findings・schema・internal/conn/connect.go(main b38f5e9、2026-10-12)
・pgbot v0.8.1(GitHub Releases)
・@pgbot/cli(npm)
・PostgreSQL 16 ドキュメント:累積統計システム
・PostgreSQL 16 ドキュメント:定義済みロール(pg_monitor)
・ankane/pghero(GitHub)・PgHero Docker ガイド
・darold/pgbadger(GitHub)
・crystaldba/postgres-mcp(GitHub)