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 の説明は鍵を入れたときだけ
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表を読む権限が無い役割
次に、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
ただし、この数え方が示すのは「今回の 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)





