Skip to main content
Glama
YawLabs

@yawlabs/postgres-mcp

by YawLabs

@yawlabs/postgres-mcp

npm version License: MIT

Query a PostgreSQL database from Claude Code, Cursor, and any MCP client. Read-only by default - writes opt in via a single env var - so an agent can't silently drop your tables.

Built and maintained by Yaw Labs.

Add to Yaw MCP

One click adds this to your local Yaw MCP config so it's available in every Yaw Terminal session. Or install manually below.

What's new in 0.11.0

PostgreSQL 18 support, a new I/O observability tool, and version-gated catalog queries. Full detail in the CHANGELOG.

Three breaking changes if you are upgrading from 0.10.x:

  1. pg_seq_scan_tables, pg_unused_indexes and pg_top_queries return an envelope, not a bare row array. Read data.rows where you used to read data. The envelope carries stats_reset, because a cumulative scan count means nothing without knowing when the counters were last reset -- if that happened an hour ago, every index looks unused, which is how a load-bearing index gets dropped.

  2. pg_explain with analyze: true now emits BUFFERS, matching what PostgreSQL 18 does server-side. Plans get longer; pass buffers: false for the old output.

  3. Node 22 is the floor. Node 20 reached end of life.

Worth knowing even if you are not upgrading yet:

  • pg_describe_table now flags generated and identity columns. Previously a generated column's expression surfaced as default_value with nothing marking it, so an agent read the column as optional-with-a-default and wrote an INSERT that PostgreSQL rejects.

  • New pg_io_stats exposes pg_stat_io (PG16+) plus in-flight async I/O from pg_aios and the active io_method (PG18+).

  • pg_advisor checks multixact wraparound alongside transaction-ID wraparound. A lock-heavy workload can exhaust multixacts while relfrozenxid still looks healthy.

  • Every version-dependent column is gated on server_version_num, so older servers get a thinner answer rather than an error.

Related MCP server: pg-mcp

Backstory

Anthropic's reference Postgres MCP server, @modelcontextprotocol/server-postgres, was archived in May 2025 and marked deprecated on npm in July 2025. Anthropic has not shipped a replacement. Despite the deprecation, the last published version (v0.6.2) is still pulled ~20,000 times per week - a lot of agents are pointed at an unmaintained package.

That unmaintained package also has a known, publicly documented stacked-query SQL injection (Datadog Security Labs) that bypasses its BEGIN READ ONLY wrapper with input like COMMIT; DROP SCHEMA public CASCADE;. It has never been patched at npm.

A handful of community forks have appeared, but each fills a narrow slice:

  • @zeddotdev/postgres-context-server - Zed's fork, primarily a security patch on the original shape.

  • Postgres MCP Pro (Crystal DBA) - focused on index tuning and hypothetical-index / buffer-cache diagnostics.

  • AWS Labs Postgres MCP - tied to Aurora / RDS Data API + Secrets Manager.

None of them position themselves as a general-purpose daily driver you'd hand to Claude Code or Cursor against an arbitrary Postgres: modern introspection, perf helpers, role/privilege awareness, and a write-safety posture out of the box. That's the gap @yawlabs/postgres-mcp fills.

Why this one?

  • Read-only by default, with an unconditional read-only tool too - pg_query runs user SQL in a BEGIN READ ONLY transaction, so postgres itself (not string parsing) blocks writes; opt in to writes with ALLOW_WRITES=1. pg_readonly is a separate tool that stays read-only regardless of ALLOW_WRITES, so hosts that gate tools individually (Claude Code permissions, mcp.hosting) can auto-allow it -- paired with a least-privileged role, since READ ONLY bounds writes to the database rather than every side effect (details).

  • Role-based access as the primary control - the recommended posture is to use a least-privileged postgres role in DATABASE_URL (e.g. one with GRANT pg_read_all_data); postgres itself then enforces the boundary, no env var needed. See Configuring access.

  • Extended query protocol for all user SQL - pg_query sends user input with queryMode: 'extended', which restricts each request to a single statement. This closes the stacked-query injection class (COMMIT; DROP SCHEMA x CASCADE;) that defeated the reference server's BEGIN READ ONLY wrapper. Integration test asserts the rejection.

  • Parameterized queries - pg_query takes a params array for $1, $2, etc. No string-interpolated SQL in our code path.

  • Written from scratch, actively maintained - not a fork of the deprecated code. Unit + integration tests (npm test, npm run test:integration) run against a real Postgres; releases cut via release.sh.

  • Schema introspection built in - pg_list_schemas, pg_list_tables, pg_describe_table return columns, primary keys, foreign keys, and indexes without the agent having to remember pg_catalog joins.

  • EXPLAIN as a first-class tool - text or JSON format, with optional ANALYZE. ANALYZE for non-SELECT statements requires ALLOW_WRITES=1 and always rolls back, so the plan is real but the write doesn't persist.

  • Perf diagnostics the deprecated server never had - pg_top_queries (from pg_stat_statements), pg_seq_scan_tables, pg_unused_indexes, pg_table_bloat, pg_inspect_locks, pg_replication_status. Answer "why is this slow?" in one tool call.

  • Health snapshot - pg_health returns version, db size, connection counts, and the 10 longest-running active queries in one call.

  • Role and privilege awareness - pg_list_roles and pg_table_privileges for the common "who can touch what?" questions.

  • Instant startup - ships as a single bundled file with zero runtime dependencies. No multi-minute node_modules install on every npx cold start.

  • Result truncation - large result sets are capped at POSTGRES_MAX_ROWS (default 1000) with a truncated: true flag, so a stray SELECT * FROM events doesn't blow out the model context.

Quick start

1. Create .mcp.json in your project root

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"
      }
    }
  }
}

Why the extra step on Windows? Since Node 20, child_process.spawn cannot directly execute .cmd files (that's what npx is on Windows). Wrapping with cmd /c is the standard workaround.

2. Restart and approve

Restart Claude Code (or your MCP client) and approve the postgres MCP server when prompted.

3. (Optional) Enable writes

Read-only is the default. If you want the agent to be able to INSERT, UPDATE, DELETE, or run DDL, add ALLOW_WRITES=1 to the env block:

"env": {
  "DATABASE_URL": "postgres://...",
  "ALLOW_WRITES": "1"
}

Prefer scoping this to dev/test databases - for production, leave writes off and use migration tools out-of-band.

Configuring access

The role in DATABASE_URL is the primary access control. Postgres has had a battle-tested permission system for 30 years; lean on it instead of relying on ALLOW_WRITES alone. A least-privileged role makes writes server-rejected no matter what tools or env vars are configured.

Read-only agent (recommended default):

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;

Point DATABASE_URL at mcp_reader. Postgres rejects every write, every DDL, every privilege change - regardless of ALLOW_WRITES. No app-level guard to bypass; the database is the boundary.

Scoped write agent (dev/test or narrow production use):

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.

Set ALLOW_WRITES=1 so pg_query will issue writes, and rely on the role to keep the agent away from DDL and other schemas.

Per-tool gating in the host:

Tools split cleanly across two authority classes:

  • Auto-allow: pg_readonly (server-side BEGIN READ ONLY, unconditional), plus the introspection tools (pg_list_*, pg_describe_table, pg_search_columns, pg_explain without ANALYZE-of-write, pg_health, pg_inspect_locks, pg_table_bloat, pg_unused_indexes, pg_top_queries, pg_replication_status, pg_advisor, pg_table_privileges, pg_list_roles).

  • Always prompt: pg_query (can write when the role allows it), pg_kill (changes session state).

Claude Code's permissions block and mcp.hosting's per-tool toggle both honor this split.

What READ ONLY does and does not cover. A BEGIN READ ONLY transaction blocks writes to the database -- INSERT/UPDATE/DELETE, DDL, nextval/setval. It does not block functions whose effect lands outside the table data. SELECT pg_terminate_backend(...), pg_cancel_backend, pg_read_file, lo_export, and COPY ... TO PROGRAM all run to completion inside pg_readonly, which means auto-allowing pg_readonly reaches the same capability that pg_kill puts behind ALLOW_WRITES=1. Every one of them still requires a privilege the DATABASE_URL role must actually hold (pg_signal_backend, pg_read_server_files, superuser), so the role is the control that bounds this tool, not the transaction mode. If you auto-allow pg_readonly, use a least-privileged role -- see Configuring access.

ALLOW_WRITES as defense-in-depth:

ALLOW_WRITES is a secondary belt-and-braces gate. Useful when:

  • You're on a managed database where creating a second role is awkward (some Supabase/Neon plans).

  • You want a single role that can write, but want the MCP server to refuse writes anyway during normal operation.

Otherwise, configure the role and stop relying on ALLOW_WRITES.

What can an agent do with this?

Once connected, the agent picks tools automatically based on what you ask. A few single-tool examples:

  • "Describe the users table" -> pg_describe_table -> returns kind, columns, PK, FKs, indexes.

  • "Which tables have a user_id column?" -> pg_search_columns with pattern user_id -> one call instead of iterating every table.

  • "This query is slow, why?" -> pg_explain with analyze: true -> returns the plan with actual row counts and timing.

  • "What's the slowest query we run?" -> pg_top_queries -> returns the top N from pg_stat_statements with mean/total/min/max times.

  • "Do we have any unused indexes?" -> pg_unused_indexes -> returns non-unique, non-primary indexes with zero or low scan counts + their size.

  • "Is pgvector installed?" -> pg_list_extensions -> yes/no with version.

The bigger leverage is multi-tool reasoning. A few real workflows:

  • Unstick a hung app. pg_inspect_locks returns blocked PID + blocking PID + the offending query, then pg_kill (ALLOW_WRITES=1 required) cancels the blocker. The agent can run both in one turn - it's the fastest path from "the app is frozen" to "back up."

  • Chase a slow page. pg_top_queries ranks the worst queries, pg_explain with analyze: true shows the plan for the top hit, pg_seq_scan_tables and pg_unused_indexes say whether the answer is "add an index here" or "drop a dead one there."

  • Oncall triage. pg_health checks connectivity + active-query count + database size; pg_inspect_locks and pg_replication_status confirm whether contention or replication lag is in play before paging the on-call DBA.

Tools

Tool

Description

pg_readonly

Run a SQL statement with no persistent data changes - always inside BEGIN READ ONLY, regardless of ALLOW_WRITES. The recommended tool for read access, and the one to auto-allow; pair it with a least-privileged role (why).

pg_query

Run a SQL query. Writes gated by the role in DATABASE_URL first, ALLOW_WRITES second. Supports parameterized queries via params. Result fields include dataTypeName (e.g. int4, jsonb) alongside dataTypeID.

pg_list_schemas

List non-system schemas.

pg_list_tables

List tables (and optionally views) in a schema with estimated row counts. Paginated via limit/offset.

pg_describe_table

Kind, columns, PK, outgoing FKs, incoming FKs (referenced_by), CHECK / UNIQUE / EXCLUDE constraints, indexes, and partition parent/children for a relation. Generated and identity columns are flagged (generated, identity, generation_expression) so an agent doesn't try to write to them. Constraints carry validated, plus enforced / has_period on PG18+.

pg_list_views

List views and materialized views in a schema, including their SQL definitions.

pg_list_functions

List functions, procedures, and aggregates in a schema with signatures and return types.

pg_list_extensions

List installed extensions (pgvector, postgis, pg_stat_statements, etc.) with versions.

pg_search_columns

Find columns by name pattern across all user schemas. Case-insensitive, supports SQL LIKE wildcards.

pg_explain

EXPLAIN or EXPLAIN ANALYZE for a SQL statement. Text or JSON output. Planner options: buffers (on by default with analyze), settings, verbose, wal, costs, timing, plus generic_plan (PG16+, plan a parameterized query with no values) and memory / serialize (PG17+). Optional hypothetical_indexes (requires the HypoPG extension) lets you ask "what would the plan be with these indexes?" without creating them on disk.

pg_index_advisor

Recommend indexes for a workload and prove each one pays for itself first. Takes statements you pass or the top N from pg_stat_statements, harvests candidate columns from what the planner reports as filters / join keys / sort keys (no SQL parser - every token is intersected with the real pg_attribute column list), then costs each candidate with HypoPG hypothetical indexes and keeps only what measurably lowers estimated cost. Greedy and bounded via max_candidates / max_explains, so a big workload cannot run away; budget_exhausted flags a truncated search. Returns the CREATE INDEX (plus a CONCURRENTLY form), cost before/after, which statements each index helps, and the estimated size. PG18-aware: PG18 added B-tree skip scan, so a multi-column index whose leading column is never filtered is no longer useless - that classic prune is gated on the server version rather than applied blindly. Requires HypoPG; indexes are session-scoped and reset on every exit path.

pg_health

Server version, database size, connections against max_connections, active queries with wait events and transaction age, pg_stat_database rollup (deadlocks, temp files, cache hit ratio), table count.

pg_top_queries

Top N queries by total/mean execution time. Requires the pg_stat_statements extension. Returns stats_reset (from pg_stat_statements_info, a different clock from the other stats tools) and dealloc on extension 1.9+ - a non-zero dealloc means entries were evicted past pg_stat_statements.max, so the ranking is drawn from an incomplete population.

pg_seq_scan_tables

Tables with heavy sequential scans - missing-index candidates. Returns the stats_reset window alongside the rows, since the counters mean nothing without it. last_seq_scan / last_idx_scan on PG16+.

pg_unused_indexes

Non-unique, non-primary indexes with low scan counts - drop candidates. Also returns stats_reset: a recently reset counter makes every index look unused, which is how a load-bearing index gets dropped. last_idx_scan on PG16+.

pg_io_stats

I/O observability: pg_stat_io read/write/extend/fsync counts, bytes and times per backend type and context (PG16+), plus in-flight async I/O handles from pg_aios and the active io_method (PG18+).

pg_inspect_locks

Who is blocking whom right now (blocked PID, blocker PID, lock type, queries).

pg_list_roles

Database roles with login/superuser/createdb flags and group memberships.

pg_table_privileges

Who has SELECT/INSERT/UPDATE/DELETE/etc. on a table or whole schema.

pg_table_bloat

Tables with high dead-tuple ratios - VACUUM candidates.

pg_replication_status

Replication slots, connected replicas, and current WAL position.

pg_advisor

Rolled-up DBA lints in one call: sequence-exhaustion candidates, wraparound risk for both counters (per-database and per-table age(relfrozenxid) against autovacuum_freeze_max_age, and mxid_age(relminmxid) against autovacuum_multixact_freeze_max_age -- a lock-heavy workload can exhaust multixacts while xids look healthy; triggered_by says which), tables without a primary key, and (configurable) public tables with RLS disabled. The "what should I be looking at?" starting point.

pg_kill

Cancel a running query or terminate a backend connection. Requires ALLOW_WRITES=1.

Configuration

All env vars are read from the MCP server's environment:

Variable

Default

Purpose

DATABASE_URL

(required)

PostgreSQL connection string.

ALLOW_WRITES

unset

Secondary write gate for pg_query and pg_explain ANALYZE-of-writes. Set to 1 or true to lift the BEGIN READ ONLY wrapper. The role in DATABASE_URL is the primary control - see Configuring access. Does not affect pg_readonly, which is unconditional.

POSTGRES_STATEMENT_TIMEOUT_MS

30000

Per-statement timeout.

POSTGRES_CONNECTION_TIMEOUT_MS

10000

TCP connect timeout. Without this, a dead host hangs until the OS gives up (~2 minutes).

POSTGRES_MAX_ROWS

1000

Cap on rows returned by pg_query.

POSTGRES_POOL_MAX

5

Max pool connections. Set to 1 for single-threaded backends (pglite-socket, PgBouncer transaction mode).

POSTGRES_SSL_REJECT_UNAUTHORIZED

unset

Set to false to skip TLS cert verification (for managed DBs using private-CA certs). Connection is still encrypted.

POSTGRES_APPLICATION_NAME

postgres-mcp

Value reported in pg_stat_activity.application_name, so agent traffic is identifiable to whoever is watching the database. An application_name in DATABASE_URL takes precedence over this.

POSTGRES_MCP_RUNTIME

auto

Which JS runtime executes the server: auto (prefer oam, fall back to Node), oam (require oam, fail if absent), node (never use oam). See Runtime.

OAM_BIN

unset

Explicit path to an oam binary, checked before PATH and the default install locations.

Supported Postgres versions

Tested on PostgreSQL 15, 17 and 18 in the integration matrix.

Works on PG13+, but note where upstream support actually sits: PG13 reached end of life on 2025-11-13 and PG14 does so on 2026-11-12. PG13/14 are not exercised here and are not a compatibility target going forward. PG12 and below are further out of support and some tools rely on columns that landed in PG13 (pg_replication_status reading wal_status, pg_top_queries reading *_exec_time).

Newer server versions unlock extra fields rather than being required. Every version-dependent column is gated on server_version_num and simply omitted on servers that predate it, so nothing errors -- you get a slightly thinner answer. The cut points that matter:

Server

What it adds

PG16+

last_idx_scan / last_seq_scan in the stats tools (index/table staleness rather than a bare counter), pg_explain generic_plan

PG17+

pg_explain memory and serialize

PG18+

Generated-column form (stored vs virtual), NOT NULL constraint validity, conenforced / conperiod constraint metadata in pg_describe_table, relallfrozen freeze coverage in pg_advisor. BUFFERS is on by default with EXPLAIN ANALYZE server-side

If the version probe fails, the server assumes the oldest supported shape rather than emitting SQL a server might reject.

Runtime

The published postgres-mcp command is a small launcher that prefers the oam runtime and falls back to Node.

If you do not have oam, nothing changes. The fallback is not a re-exec: npm already started Node to run the launcher, so falling back is a plain import() of the server into that same process. It costs a few existsSync calls and no subprocess, and behaves identically to running dist/index.js under Node directly.

If you do have oam, the server runs under it. Verified equivalent on both runtimes: all 22 tools register, queries return identical rows and dataTypeName values, and the error paths match. oam supplies every node: builtin the driver needs, including net, tls, crypto, and dns (SCRAM auth and the extended query protocol both work).

Startup cost, measured. windows-arm64, 1.4 MB bundle, postgres-mcp version (full module init), every binary warmed first, mean of 12 runs:

path

startup

standalone binary (oam compile)

298ms

oam run dist/index.js

306ms

node dist/index.js

358ms

launcher -> Node (in-process)

370ms

launcher -> oam (spawn)

409ms

oam starts faster than Node here. What the launcher costs is the spawn: reaching oam means Node has already booted, and that hop (~100ms) is larger than oam's ~52ms advantage. So through the npm bin, the two land within ~40ms of each other, and POSTGRES_MCP_RUNTIME=node is a marginal win rather than a meaningful one.

Either way it is a one-time cost per MCP session, not per tool call -- hosts spawn the server once and hold it open. If startup genuinely matters, the standalone binary avoids the launcher entirely and is the fastest option.

Earlier releases of this README reported ~650-900ms for Node and ~980-1290ms for oam, and advised opting out of oam on that basis. Those figures were measured against cold, freshly-built binaries and reflected the Windows on-access virus scanner rather than either runtime. They were wrong in both magnitude and direction. Corrected in 0.9.1.

{
  "mcpServers": {
    "postgres": {
      "command": "npx",
      "args": ["-y", "@yawlabs/postgres-mcp"],
      "env": {
        "DATABASE_URL": "postgres://...",
        "POSTGRES_MCP_RUNTIME": "node"  // opt out of oam
      }
    }
  }
}

Connecting to managed Postgres (Supabase, Neon, RDS, etc.)

Most managed databases require TLS but serve certs signed by a private CA that Node's default trust store doesn't recognize. The symptom is one of:

  • self signed certificate in certificate chain

  • unable to get local issuer certificate

  • unable to verify the first certificate

To allow the connection while keeping traffic encrypted, add POSTGRES_SSL_REJECT_UNAUTHORIZED=false to the env block:

"env": {
  "DATABASE_URL": "postgres://user:pass@host:5432/db?sslmode=require",
  "POSTGRES_SSL_REJECT_UNAUTHORIZED": "false"
}

This disables certificate chain verification only -- the TCP connection is still TLS-encrypted end-to-end. For production setups where you can install the CA, prefer putting the cert in the Node trust store (NODE_EXTRA_CA_CERTS) over disabling verification globally.

Shaving a round trip on PG17+. Postgres 17 added direct TLS negotiation, which skips the plaintext SSLRequest handshake before the TLS one. The bundled driver supports it, so append sslnegotiation=direct to your DATABASE_URL:

postgres://user:pass@host:5432/db?sslmode=require&sslnegotiation=direct

It is opt-in rather than a default because a PG16-or-older server will reject the connection outright, and the saving is one round trip per pooled connection -- worth it on a distant managed database, invisible on a local one.

Troubleshooting

DATABASE_URL is not set - Your MCP client is launching the server without the env var. On Windows especially, env vars set in bash / PowerShell profiles are not inherited by MCP servers launched via cmd. Put DATABASE_URL directly in the env block of .mcp.json.

password authentication failed - Check the username, password, and that the user has CONNECT privilege on the database. URL-encode special characters in the password (@ → %40, # → %23, / → %2F).

SASL: SCRAM-SERVER-FIRST-MESSAGE: client password must be a string - The password in your connection string is empty or became null after URL decoding. Re-check your connection string.

canceling statement due to statement timeout - A single query exceeded POSTGRES_STATEMENT_TIMEOUT_MS (default 30s). Increase it, narrow the query with WHERE, or add an index. This is working as designed -- the timeout exists so a runaway query cannot hang the agent.

Write blocked: this server is in read-only mode - You asked the agent to write via pg_query but ALLOW_WRITES is not set. Either add ALLOW_WRITES=1 to the env block of .mcp.json and restart your MCP client (dev/test DBs), or - cleaner for production - use a role with INSERT/UPDATE/DELETE grants in DATABASE_URL and keep ALLOW_WRITES unset. See Configuring access. Note that pg_readonly always rejects writes; if you want writes, the call has to go through pg_query.

Connection pool exhaustion with PgBouncer transaction mode or pglite-socket - These backends don't support concurrent queries on a single connection. Set POSTGRES_POOL_MAX=1 in the env block.

First query is slow, subsequent queries are fast - Expected. The pg driver lazily establishes the first connection; subsequent queries reuse the pool.

Development

Run the full suite (unit + integration) against a real Postgres:

DATABASE_URL='postgres://user:pass@host:5432/db' POSTGRES_MCP_INTEGRATION=1 npm run test:integration

The integration suite assumes a disposable database -- it creates and drops a test_fixture schema. Don't point it at anything you care about.

To also run the destructive tests (REVOKE / restricted-role path), add POSTGRES_MCP_DESTRUCTIVE_TESTS=1. Only safe on a disposable cluster:

DATABASE_URL='postgres://user:pass@host:5432/db' POSTGRES_MCP_INTEGRATION=1 POSTGRES_MCP_DESTRUCTIVE_TESTS=1 npm run test:integration

Windows: integration tests via WSL2

Native Postgres on Windows ARM64 is fragile (UCRT runtime gaps, missing ARM64 builds). The reliable path is a disposable Ubuntu under WSL2 with the integration suite running inside WSL (WSL2's NAT blocks the Windows host from reaching :5432, so don't try to run the tests from 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 installs PG15, PG17 and PG18 from the PGDG apt repo (ports are auto-assigned by pg_createcluster -- typically 17 on 5432, 18 on 5433, 15 on 5434), sets the postgres password to postgres, and creates postgres_mcp_test in each. wsl-test-matrix.sh rsyncs the working tree into /root/postgres-mcp, runs npm ci once, and runs the integration suite against every cluster found via pg_lsclusters.

Running these from Git Bash instead of PowerShell? Prefix both script invocations with MSYS_NO_PATHCONV=1. Git Bash rewrites the /mnt/c/... argument before wsl.exe sees it, so the script arrives as C:/Users/<you>/scoop/apps/git/<ver>/mnt/c/... and bash exits with "No such file or directory" having run nothing. Also avoid piping either script into tail/head -- the pipeline's exit status is the last command's, so a failing matrix reports success.

Tear down when finished: wsl --unregister Ubuntu.

License

MIT © 2026 YawLabs

Available Tools

23 tools
pg_advisorDatabase advisor (DBA lints)A
Read-onlyIdempotent

Rolled-up DBA lint pass. One call returns four categories of findings:

  • sequence_exhaustion: SERIAL / BIGSERIAL / IDENTITY sequences whose last_value is above seqExhaustionThreshold of max_value. The classic incident class.

  • wraparound_risk: transaction-ID AND multixact wraparound pressure, the classic pageable incident. {autovacuum_freeze_max_age, autovacuum_multixact_freeze_max_age, databases[], tables[]}. Those two cluster GUCs are the divisors both lists are measured against (null if unreadable). Multixact IDs are a SEPARATE 32-bit counter, consumed by row-level locking (SELECT ... FOR SHARE/UPDATE, FK checks), so a lock-heavy workload can exhaust them while relfrozenxid stays perfectly healthy -- both counters are checked here. databases rows: {database, xid_age (age(datfrozenxid)), mxid_age (mxid_age(datminmxid)), pct_of_freeze_max_age, pct_of_multixact_freeze_max_age, triggered_by} -- template databases included, since template0 ages like any other and the cluster horizon is the minimum across all of them. tables rows: {schema, table, relkind, xid_age (age(relfrozenxid)), freeze_max_age, pct_of_freeze_max_age, mxid_age (mxid_age(relminmxid)), multixact_freeze_max_age, pct_of_multixact_freeze_max_age, triggered_by}, where freeze_max_age / multixact_freeze_max_age are the EFFECTIVE limits -- a per-table autovacuum_freeze_max_age / autovacuum_multixact_freeze_max_age storage parameter wins over the GUC. A row is returned when EITHER ratio is at or above wraparoundThreshold, and triggered_by ('xid' | 'multixact' | 'both') says which one did it: 'xid' means chase freezing/autovacuum, 'multixact' means chase the lock-heavy workload burning members. mxid_age and pct_of_multixact_freeze_max_age are null on rows whose minmxid is InvalidMultiXactId (no multixact ever recorded); such rows can only be xid-triggered. At pct_of_freeze_max_age 1.0 autovacuum forces an anti-wraparound VACUUM, and near 2.1 billion xids (or 4.2 billion multixacts) the server stops accepting writes. tables deliberately includes pg_catalog and pg_toast relations -- the culprit is more often a TOAST table or a system catalog than a user table. On PG18+ table rows also carry pages / all_frozen_pages / frozen_page_fraction from pg_class.relallfrozen (visibility-map freeze coverage); those three keys are ABSENT on older servers rather than null.

  • tables_without_primary_key: user tables (plain and partitioned) with no PK defined. Bloat candidates and a sign of design drift; some replication setups also need PKs. Foreign tables are excluded -- PostgreSQL forbids declaring PKs on foreign tables.

  • public_tables_without_rls: tables in public (or any schema in rlsSchemas) with row-level security disabled. Useful as a security baseline check. Any category whose query fails (permission-gated catalogs on managed providers) appends to _warnings and returns empty; the other categories still return. Use this as the 'what should I be looking at?' starting point, then drill into pg_unused_indexes, pg_table_bloat, pg_seq_scan_tables for the perf side.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax rows per category (default 50).
rlsSchemasNoSchemas where RLS-missing should be flagged. Defaults to ['public'].
wraparoundThresholdNoMinimum used-fraction to flag a database or table for wraparound risk (default 0.5 = 50%). Applied to BOTH ratios -- age(frozenxid) / autovacuum_freeze_max_age and mxid_age(minmxid) / autovacuum_multixact_freeze_max_age -- and a row is flagged if either one clears it. 1.0 is where autovacuum starts forcing anti-wraparound VACUUMs.
seqExhaustionThresholdNoMinimum used-fraction (last_value / max_value) to flag a sequence (default 0.5 = 50%).

Output Schema

ParametersJSON Schema
NameRequiredDescription
_warningsNo
wraparound_riskYes
sequence_exhaustionYes
public_tables_without_rlsYes
tables_without_primary_keyYesPlain and partitioned tables only; foreign tables cannot have a PK and are excluded.

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Even though annotations already declare read-only/idempotent/non-destructive, the description adds substantial behavioral context: per-category query failures append to _warnings and return empty, system catalogs and TOAST tables are intentionally included, threshold semantics are explained, and PG18+ key-absence behavior is disclosed. This is far beyond what annotations alone provide.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long, but it earns its length for a roll-up diagnostic tool with four complex finding categories. It is well-structured with bolded category names, nested field descriptions, and a clear closing usage note. A few asides could be trimmed, but the density is justified by the tool's scope.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers all finding categories, their exact row structures, threshold behaviors, edge cases, failure modes, and follow-up tools. Despite the output schema being available, this description makes the tool's behavior fully understandable without needing additional context. Nothing essential for correct invocation or interpretation is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema already documents all four parameters at 100% coverage, so the baseline is 3. The description goes further by explaining how wraparoundThreshold applies to both xid and multixact ratios independently, and how seqExhaustionThreshold relates to last_value/max_value. This adds meaningful semantic detail beyond the schema field descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with 'Rolled-up DBA lint pass' and immediately enumerates the four distinct categories of findings with detailed definitions. It clearly identifies this as the broad advisor tool, distinct from more focused siblings like pg_unused_indexes or pg_table_bloat.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly positions the tool as the 'what should I be looking at?' starting point and names the perf-side drill-down alternatives: 'pg_unused_indexes, pg_table_bloat, pg_seq_scan_tables'. It lacks an explicit 'don't use when...' statement, but the guidance is clear enough for an agent to choose it as the first-pass diagnostic.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pg_describe_tableDescribe tableA
Read-onlyIdempotent

Describe a relation: kind (table / view / materialized_view / partitioned_table / foreign_table), columns (name, type, nullable, default, generated, identity), primary key, foreign keys (outgoing), referenced_by (other tables whose FKs point at this one), constraints (CHECK / UNIQUE non-PK / EXCLUDE), indexes, and partition info (partition_of parent, partitions children). Works on views and materialized views too -- PK/FK/constraint/index lists will simply be empty for a plain view. Use kind to disambiguate before assuming you can write to the relation. Generated columns (generated: 'stored' / 'virtual') and identity: 'always' columns are NOT writable -- omit them from INSERT/UPDATE column lists; a generated column's expression is reported as generation_expression, never as default_value. On PostgreSQL 18+ constraints also report validated / enforced / has_period, and columns report not_null_validated -- a NOT VALID not-null constraint means nullable: false can still hide NULLs.

ParametersJSON Schema
NameRequiredDescriptionDefault
tableYesTable name.
schemaNoSchema name (defaults to 'public').public

Output Schema

ParametersJSON Schema
NameRequiredDescription
kindYestable | partitioned_table | view | materialized_view | foreign_table, or the raw relkind. Defaults to 'table' with a `_warnings` entry when the kind fetch failed.
tableYes
schemaYes
columnsYes
indexesYes
_warningsNo
partitionsNoPresent only when this relation is a partitioned parent WITH children.
constraintsYesCHECK / non-PK UNIQUE / EXCLUDE only; PK and FK have their own lists.
primary_keyYesKey columns in declared order; INCLUDE columns are excluded.
foreign_keysYes
partition_ofNoPresent only when this relation is itself a partition.
referenced_byYesOther tables whose foreign keys point AT this one.

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description goes far beyond the readOnlyHint/idempotentHint annotations by explaining exactly what is included in the result, how plain views behave, which column types are not writable, and how PostgreSQL 18+ adds extra fields. It also clarifies that 'generated' columns report 'generation_expression' rather than 'default_value', which prevents misinterpretation of output.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

While the description is long, every sentence carries operational value: relation kinds, output fields, view behavior, writability caveats, generated column handling, and PG18 additions. It is front-loaded with the core result shape and then layers important caveats in a logical order.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given an output schema exists and annotations already establish the read-only and idempotent nature, the description fully covers what an agent needs to correctly invoke and interpret this tool. It explains edge cases (views, generated/identity columns, NOT VALID constraints) that would otherwise be surprising.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The description does not add meaningful parameter-level semantics beyond the schema — it describes output structure and behavioral details rather than clarifying what 'table' or 'schema' mean or how they should be formatted.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description opens with the precise verb-resource pair 'Describe a relation' and immediately enumerates the full set of returned dimensions (kind, columns, primary key, foreign keys, constraints, indexes, partition info). It distinguishes itself from generic listing siblings by explaining it returns structural metadata, not just table names or rows.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives clear context on when this tool is appropriate, notably that it works on views and materialized views and that PK/FK/constraint lists will be empty for plain views. It advises using 'kind' to disambiguate before assuming writability, which is practical guidance, though it does not explicitly name sibling tools as alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pg_explainExplain query planA
Destructive

Get the query plan for a SQL statement. By default, this uses plain EXPLAIN (no execution). Set analyze: true to run the query with EXPLAIN ANALYZE - for non-SELECT statements, ALLOW_WRITES=1 is required (since ANALYZE actually executes the statement). Writes executed during EXPLAIN ANALYZE are always rolled back, so you can inspect a plan for an INSERT/UPDATE/DELETE without persisting the mutation. Format is text (default) or json. Pass the raw SQL (not an EXPLAIN-prefixed statement). Planner options (all optional): buffers reports shared/local/temp block hits and is the fastest way to tell a bad plan from a cold cache - it defaults to TRUE whenever analyze is true (matching PostgreSQL 18, which turns it on for you), pass buffers: false to suppress it; requesting it WITHOUT analyze needs PostgreSQL 13+. verbose adds output columns and schema-qualified names. settings (PostgreSQL 12+) lists planner GUCs set away from their defaults - the usual explanation for a plan that looks impossible. wal (PostgreSQL 13+) reports WAL generated and serialize (none|text|binary, PostgreSQL 17+) charges the cost of building the result rows; both require analyze. memory (PostgreSQL 17+) reports memory used by the PLANNER, so it works with or without analyze - use it alone to ask why planning a statement is expensive. generic_plan (PostgreSQL 16+) plans a parameterized statement WITHOUT values for its $1/$2 placeholders and cannot be combined with analyze or params. costs and timing default to true (as in postgres); set either to false to drop those columns, and note timing only applies with analyze. Options that need a newer server than the one connected are rejected with an explicit error naming the required version instead of a confusing parse failure. Set hypothetical_indexes to a list of {table, columns, using?} to ask the planner 'what would the plan be if these indexes existed?' -- requires the HypoPG extension (CREATE EXTENSION hypopg). The hypothetical indexes are torn down at the end of the call, never touching real disk.

ParametersJSON Schema
NameRequiredDescriptionDefault
sqlYesThe SQL statement to explain. Do NOT prefix with EXPLAIN.
walNoReport WAL generated by the statement. Requires `analyze` (PostgreSQL 13+).
costsNoInclude estimated cost/rows/width. Set false for a terser plan.
formatNoOutput format.text
memoryNoReport memory used by the planner (PostgreSQL 17+). Works with or without `analyze`, since planning happens either way.
paramsNoPositional parameters referenced as $1, $2, ... in the SQL.
timingNoInclude per-node actual timing. Setting it to false REQUIRES `analyze: true` (it is rejected otherwise, not silently ignored); false lowers measurement overhead.
analyzeNoRun EXPLAIN ANALYZE (actually executes the query).
buffersNoReport buffer hits/reads/dirtied. Defaults to TRUE when `analyze` is true (PostgreSQL 18 does the same); pass false to suppress. Requesting it without `analyze` requires PostgreSQL 13+.
verboseNoInclude output columns, schema-qualified names, and triggers.
settingsNoReport planner GUCs set away from their defaults - explains a weird plan (PostgreSQL 12+).
serializeNoCharge the cost of serializing result rows (network-bound queries hide it otherwise). Requires `analyze` (PostgreSQL 17+).
generic_planNoPlan the statement with UNKNOWN values for its $1/$2 placeholders - the plan a prepared statement would get. Cannot be combined with `analyze` or `params` (PostgreSQL 16+).
hypothetical_indexesNoList of indexes the planner should pretend exist for this EXPLAIN. Requires the HypoPG extension. Indexes are session-scoped and reset at the end of the call.

Output Schema

ParametersJSON Schema
NameRequiredDescription
planYesNewline-joined plan text for `format: "text"` (with a trailing truncation marker when POSTGRES_MAX_ROWS chopped it), or the parsed plan array for `format: "json"`.

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description discloses far more than the annotations alone: plain EXPLAIN does not execute, ANALYZE does execute, non-SELECT requires ALLOW_WRITES=1, and writes during EXPLAIN ANALYZE are rolled back. It also explains version-gated behavior, explicit version errors instead of parse failures, HypoPG dependency, and teardown of hypothetical indexes. None of this contradicts the annotations, and the destructiveHint is consistent with the fact that ANALYZE actually runs the statement.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long, but proportionately so: it must explain 14 parameters plus cross-cutting version requirements and side effects. It front-loads the core behavior first, then works through options in a logical order. It loses a point because it is a dense wall of prose in places, and some default information is repeated from the schema rather than relying on the structured field.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with 14 parameters, execution side effects, version constraints, and an output schema, the description is remarkably complete. It covers behavior, security/authorization implications, rollback semantics, extension requirements, and parameter combinations. The presence of an output schema means the description does not need to document return-value structure, and nothing critical is missing for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema already covers 100% of parameters, but the description adds significant meaning on top: default-on behavior for buffers when analyze is true, PostgreSQL version requirements per option, incompatibilities such as generic_plan vs analyze/params, and the semantics of serialize levels. This goes well beyond the schema's standalone property descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a precise verb and resource: 'Get the query plan for a SQL statement.' It immediately clarifies the key distinction between plain EXPLAIN and EXPLAIN ANALYZE, which separates this tool from siblings like pg_query or pg_advisor. The raw-SQL-not-prefixed instruction further disambiguates the input contract.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives strong option-level usage guidance: when to use buffers ('fastest way to tell a bad plan from a cold cache'), settings ('usual explanation for a plan that looks impossible'), memory ('ask why planning a statement is expensive'), and generic_plan. It also records important constraints such as 'cannot be combined with analyze or params.' However, it never explicitly routes the agent to an alternative sibling for cases where EXPLAIN is not the right tool, so it misses the 'when not to use this tool' part.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pg_healthDatabase health snapshotA
Read-onlyIdempotent

Quick health snapshot: server version, database size, connection counts measured against max_connections, active queries with their wait events, a pg_stat_database rollup, and table count. Useful as a connection sanity check and to spot runaway queries, connection-cap pressure, and lock/IO waits.

  • connections: total for the CURRENT database, broken down into active / idle / idle_in_transaction / idle_in_transaction_aborted / other (starting, fastpath function call, disabled) / state_unavailable -- those six sum to total. idle_in_transaction_aborted is called out separately because it holds locks and blocks vacuum while doing no work and will never commit. state_unavailable counts sessions whose state reads NULL because the role lacks pg_read_all_stats / pg_monitor membership; a non-zero value means every other bucket is under-counted by at least that much, so do NOT read active: 0 next to it as an idle database. Plus cluster_client_backends (client backends across ALL databases -- those are what actually consume connection slots), max_connections, superuser_reserved_connections, and used_fraction (cluster_client_backends / max_connections). A raw connection count means nothing without the cap; read used_fraction first.

  • active_queries: pid, state, query, application_name, backend_type, wait_event_type / wait_event (both NULL when the backend is running rather than waiting -- the single most diagnostic pair in pg_stat_activity). Both are reported verbatim as the server spells them, and that spelling changes between majors: a backend waiting on a buffer pin reports wait_event_type 'BufferPin' through PostgreSQL 18 and 'Buffer' from 19 on, with the wait_event names beneath it changing to match. Read them against the reported version rather than hard-coding a literal. duration_seconds (since query_start) and transaction_age_seconds (since xact_start). A large transaction_age_seconds next to a small duration_seconds is a long-open transaction, the usual root cause behind lock waits, bloat, and stalled autovacuum.

  • database_stats: pg_stat_database for the current database -- deadlocks, temp_files / temp_bytes (work_mem spills), conflicts (recovery conflicts, only ever non-zero on a replica), blks_hit / blks_read / cache_hit_ratio, and stats_reset. Every counter is CUMULATIVE since stats_reset, not a rate -- interpret them against that timestamp. Sub-queries that fail (several of these are permission-gated on managed providers) append to _warnings and leave their field null; the rest of the snapshot still returns.

ParametersJSON Schema
NameRequiredDescriptionDefault
activeQueryLimitNoMax active queries to return (default 10, max 100).

Output Schema

ParametersJSON Schema
NameRequiredDescription
versionNoFull `version()` banner. Absent (with a `_warnings` entry) if the row came back without it.
databaseNo
_warningsNo
connectedYesAlways true on a success response -- the version probe answered.
connectionsNo
table_countNoUser tables and partitioned tables, as a decimal string.
active_queriesYesEmpty array both when nothing is running and when the fetch failed -- check `_warnings`.
database_statsYesNull when pg_stat_database is unreadable OR has no row for this database.

TDQS

A4.3/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the readOnly/idempotent annotations, the description discloses partial failure behavior (_warnings and null fields), cumulative counter semantics, state_unavailable under-counting implications, NULL wait events for running backends, and version-dependent wait_event spelling. This goes far beyond what the annotations or schema convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long but every section earns its place: a front-loaded summary, then structured bullets that clarify connections, active_queries, and database_stats with actionable diagnostic guidance. There is no filler or redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool this complex, the description covers output semantics, edge cases, permission-gated failures, and interpretation pitfalls such as under-counted active connections and cumulative stats. The existing output schema can handle the return structure, and nothing essential for correct invocation or interpretation is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema already documents activeQueryLimit with default, min, max, and a description (100% coverage). The tool description does not add parameter-specific context beyond mentioning active queries generally, so the schema carries the load and the baseline of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific resource ('Database health snapshot') and lists concrete components: server version, database size, connection counts, active queries with wait events, pg_stat_database rollup, and table count. It clearly identifies what the tool produces, though it does not explicitly distinguish itself from specialized sibling tools like pg_io_stats or pg_top_queries.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description states clear use cases: 'connection sanity check' and detecting runaway queries, connection-cap pressure, and lock/IO waits. It gives strong context for when to invoke the tool but does not mention alternatives or exclusion criteria relative to sibling tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pg_index_advisorRecommend indexes for a workloadA
Read-onlyIdempotent

Recommend indexes for a workload, and prove each one pays for itself before recommending it. Give it statements (the SQL you care about) or let it take the top N from pg_stat_statements; it plans each statement, generates candidate indexes, costs them with HypoPG hypothetical indexes, and returns only the ones that measurably cut estimated cost. How candidates are generated, and the honest limit: this tool has NO SQL parser and does not read your SQL text. It EXPLAINs each statement and harvests the columns the PLANNER reports as filters, join keys, and sort keys, then intersects those tokens with the real column list from pg_attribute -- so a candidate can never name a column that does not exist. The extraction is deliberately loose (a token matching a real column name on a different table can slip through); HypoPG is the arbiter, and anything that does not lower cost is discarded. Column ORDER within each candidate is equality columns first (most selective first, from pg_stats), then at most one range column, then sort columns. The search is greedy and BOUNDED. Each accepted index stays in place while the rest are re-costed on top of it, so later picks account for what earlier ones already fixed. max_candidates caps how many candidates are considered and max_explains caps total EXPLAIN round trips; when a cap stops the search early, budget_exhausted is true and the result is a truncated search, not a converged one. PostgreSQL 18 note, and it reverses a rule you have probably internalized: PG18 added B-tree SKIP SCAN, so a multi-column index whose LEADING column the query never constrains CAN now be used. The classic 'leading column never filtered means the index is useless' heuristic is wrong on PG18+. This tool gates that prune on the server version -- on PG18+ such candidates are kept and costed (skip_scan_available: true, and an accepted one carries requires_skip_scan), below PG18 they are pruned as unusable and counted in candidates_pruned_leading_column. Requires the HypoPG extension (CREATE EXTENSION hypopg;). Hypothetical indexes are session-scoped and are reset before the call returns, on the success and the failure path alike, so they never touch disk and never leak into a later query plan. Statements are only ever EXPLAINed, never executed, inside a BEGIN READ ONLY transaction. Costs are PLANNER ESTIMATES, not measurements: they are the right way to compare two plans for the same statement and the wrong way to predict wall-clock time. They are weighted by calls when the workload came from pg_stat_statements, so a query run a million times outranks an identical one run twice. Validate a recommendation with pg_explain before creating it, and create it with CONCURRENTLY in production (create_statement_concurrently).

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoHow many statements to pull from pg_stat_statements. Ignored when `statements` is given.
schemaNoOnly recommend indexes on tables in this schema. Candidates elsewhere are dropped.
statementsNoThe workload to optimize. When omitted, the top `limit` statements from pg_stat_statements are used instead (and weighted by their call counts).
max_explainsNoCap on EXPLAIN round trips spent searching (baseline plans are not counted). The search stops when the next candidate would exceed it and reports `budget_exhausted: true`.
max_candidatesNoCap on candidate indexes considered. Candidates are ranked by table sequential-scan count first.
min_improvementNoFraction of total weighted workload cost an index must remove to be accepted (0.1 = 10%). Relative rather than absolute so it means the same thing on a small and a large database.
max_index_columnsNoWidest candidate index to consider. Every narrower prefix is considered too.
max_recommendationsNoStop after this many accepted indexes, even if more would still help.

Output Schema

ParametersJSON Schema
NameRequiredDescription
_warningsNo
statementsYesThe workload as analyzed, in the order `helps_statements.statement` indexes into.
explains_usedYesEXPLAIN round trips spent searching, excluding baseline plans.
explain_budgetYes
recommendationsYes
budget_exhaustedYesTrue when `max_explains` stopped the search before it converged.
final_workload_costYesWeighted total after applying every recommendation.
skip_scan_availableYesTrue on PostgreSQL 18+, where B-tree skip scan exists.
candidates_consideredYes
baseline_workload_costYesWeighted total estimated cost before any recommendation.
candidates_pruned_leading_columnYesMulti-column candidates dropped by the pre-PG18 leading-column rule. Always 0 on PG18+.

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Even though readOnlyHint/openWorldHint/idempotentHint/destructiveHint already cover safety, the description adds substantial beyond-annotation behavior: no SQL parser, EXPLAIN-only execution inside BEGIN READ ONLY, session-scoped HypoPG reset on success and failure, greedy bounded search with budget_exhausted, PG18 skip-scan handling, and planner-estimate caveats. Nothing contradicts the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long but every block earns its place, covering generation, pruning, caps, version-specific behavior, side effects, and cost interpretation. The core purpose and proof requirement are front-loaded, followed by the most decision-critical caveats.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity and the existence of a rich output schema, the description covers all operational essentials: the honest no-parser limit, HypoPG requirement and cleanup, read-only safety, bounded search semantics, PG18 version reversal, and the correct interpretation of costs. No major decision-relevant context is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema already describes all 8 parameters at 100% coverage, setting a baseline of 3. The description adds meaningful interplay context: statements vs limit choice, max_candidates/max_explains as stopping caps that produce budget_exhausted, and call-count weighting, so it moves above baseline.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening line names a specific verb and resource — 'Recommend indexes for a workload' — and adds a concrete behavioral promise: each index must prove it pays for itself. This clearly distinguishes pg_index_advisor from siblings like pg_advisor, pg_explain, and pg_unused_indexes.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explains the two input modes (statements vs pg_stat_statements), states the HypoPG dependency, and tells the agent to validate with pg_explain and create with CONCURRENTLY. It does not explicitly say 'use this when...' or name when not to use it, but the context is clear enough to route a caller correctly.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pg_inspect_locksInspect blocking locksA
Read-onlyIdempotent

Show current lock contention: which sessions are blocked and who is blocking them. Returns blocked PID, blocking PID, lock types, relation being contested, and the queries involved. Use this first when a tool call hangs or the app feels stuck - it's the fastest way to identify a long-held transaction holding a lock. Row shape: one row per (blocked_pid, blocking_pid) pair. A session waiting on multiple blockers appears on multiple rows -- group/deduplicate by blocked_pid if you want a per-blocked-session count. Caveat on relation: for non-relation waits (transactionid/virtualxid, where the wait is on the blocker's xid rather than a table) relation is a best-effort hint -- an alphabetical guess among the blocker's held write-intent locks -- not authoritative. Use the blocked/blocking query text to disambiguate which table is actually contested.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax blocked/blocker pairs (default 50).

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the read-only/idempotent annotations, the description discloses non-obvious behavior: one row per blocked/blocking pair, duplicate rows for multi-blocker sessions, and a caveat that `relation` is only a best-effort guess for transactionid/virtualxid waits. This is exactly the kind of behavioral nuance annotations do not convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is dense but every sentence earns its place: purpose, use case, row-shape semantics, and a critical caveat are all covered without redundancy. The most important information is front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present and read-only/idempotent annotations, the description covers the remaining essentials: what columns are returned, how to interpret duplicate rows, and how to disambiguate the unreliable `relation` field. An agent has enough to invoke and interpret the tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The only parameter, `limit`, is fully described in the input schema (max blocked/blocker pairs, default 50, range). Since schema description coverage is 100%, the description's lack of parameter-specific detail is acceptable and matches the baseline.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'Show current lock contention.' It clearly enumerates outputs (blocked PID, blocking PID, lock types, relation, queries) and uniquely positions the tool among the sibling diagnostics, none of which target lock contention.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives an explicit usage trigger: 'Use this first when a tool call hangs or the app feels stuck.' It does not name alternatives or when-not-to-use conditions, but the context is clear enough for an agent to know when to choose this tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pg_io_statsI/O statistics and in-flight async I/OA
Read-onlyIdempotent

I/O observability: cumulative per-backend-type I/O from pg_stat_io (PostgreSQL 16+), plus in-flight asynchronous I/O handles from pg_aios (PostgreSQL 18+). This is the layer underneath pg_top_queries and pg_health -- it says WHICH subsystem is doing the I/O (client backends vs autovacuum vs checkpointer vs walwriter) and through which path, which a per-query or per-table view cannot.

  • io: one row per (backend_type, io_object, io_context) combination. Counters reads / writes / extends / writebacks / hits / evictions / reuses / fsyncs are bigints returned as decimal strings; read_time_ms / write_time_ms / writeback_time_ms / extend_time_ms / fsync_time_ms are float8 milliseconds. A timing of 0 next to a non-zero op count means track_io_timing is off, NOT that the I/O was free -- turn it on to get real numbers. A NULL counter means the operation is not possible for that combination, which is different from 0.

  • io[].read_bytes / write_bytes / extend_bytes: a normalized byte figure that means the same thing on every supported server. On PG16-17 it is computed as op_bytes * <op count>; on PG18 op_bytes was removed and the server reports bytes directly. The top-level byte_accounting field says which source produced the numbers.

  • io[].stats_reset: these are CUMULATIVE counters, so a row is only interpretable next to its reset point. Reported per row because that is how the view reports it; pg_stat_reset_shared('io') resets them together in practice, but this tool does not assert that.

  • Rows whose counters are all zero are omitted by default (pg_stat_io is mostly zeros on a quiet system, and the noise buries the handful of rows that matter). Pass includeZeroRows: true for the full matrix.

  • in_flight + io_method: PostgreSQL 18+ ONLY, and both keys are ABSENT on older servers rather than empty/null -- an empty in_flight array would read as 'nothing is stalled' when the truth is 'this server cannot tell you'. in_flight is live, currently-outstanding async I/O (pid, io_id, op, state, off, length, target_desc), which is what you want while a stall is happening rather than after it. io_method (worker / io_uring / sync) explains what in_flight can contain: with io_method = sync there is no asynchronous submission, so the array is legitimately empty no matter how much I/O is running. Requires PostgreSQL 16+. Sub-queries that fail (pg_stat_io and pg_aios are permission-gated on some managed providers) append to _warnings and set their field to null; the rest of the response still returns.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax rows per section (default 200). pg_stat_io has well under 200 combinations, so this effectively bounds the in-flight list on a busy PG18 server.
includeZeroRowsNoIf true, return every (backend_type, io_object, io_context) row including the ones with no recorded activity. Default false -- the view is mostly zeros on a quiet system.

Output Schema

ParametersJSON Schema
NameRequiredDescription
ioYesNull (not []) when the fetch was refused -- [] is a real answer on a freshly reset cluster.
_warningsNo
in_flightNoPostgreSQL 18+ only. Currently-outstanding async I/O. Null (not []) when the fetch was refused -- reading a denial as 'nothing outstanding' would point the investigation the wrong way.
io_methodNoPostgreSQL 18+ only. worker | io_uring | sync. With `sync` there is no async submission, so `in_flight` is legitimately empty however much I/O is running.
byte_accountingYesWhich source produced read_bytes / write_bytes / extend_bytes: native columns, or op_bytes * ops.
include_zero_rowsYesEchoed because it changes what an empty `io` means: no recorded I/O, vs the view returned nothing.
server_version_numYesEchoed so a caller can tell WHY the PG18-only keys are absent without a second round-trip.

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description goes far beyond the readOnly/idempotent hints: it explains NULL vs 0 semantics, the meaning of 0 timing values with non-zero counts, cumulative counters and stats_reset, absent keys vs empty arrays on PG18, omitted zero rows, per-query sub-failures appending to _warnings, and permission gating. No contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long, but nearly every clause carries a non-obvious behavioral detail that is essential for correct interpretation (e.g., absent vs empty keys, timing=0 caveat, zero-row omission). It is structured with bullets between focused sections and front-loads the overall purpose before the details.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a high-complexity PostgreSQL observability tool with an output schema available, the description covers version requirements, permission-gated failures, counter semantics, reset interpretation, zero-row filtering, and in-flight I/O caveats. An agent has enough information to call it correctly and interpret the result.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and both parameters are already documented with defaults and meaning. The description reinforces includeZeroRows and limit, but it adds little that is not already in the input schema; the baseline of 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with 'I/O observability' and precisely defines the resource: cumulative per-backend-type I/O from pg_stat_io plus in-flight async I/O from pg_aios. It also differentiates itself from siblings by calling itself 'the layer underneath pg_top_queries and pg_health' and noting that per-query or per-table views cannot show the subsystem/path involved.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description tells the agent when this tool is appropriate: when the question is which subsystem (client backends vs autovacuum vs checkpointer) and which I/O path is responsible, as opposed to per-query or per-table views. It also states required PostgreSQL versions and the includeZeroRows behavior for quiet systems, though it does not give explicit 'when not to use' alternatives beyond the sibling references.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pg_killCancel or terminate a backendA
Destructive

Cancel a running query (SIGINT-equivalent) or terminate a backend connection (SIGTERM-equivalent) by PID. Find the PID via pg_health active_queries or pg_inspect_locks. Requires ALLOW_WRITES=1 since this changes database session state. The role in DATABASE_URL must have permission - cancelling another user's query needs the pg_signal_backend role or superuser. Note: pg_signal_backend does NOT cover superuser-owned backends - only a superuser can signal another superuser's session. Cancel is graceful; terminate is forceful. When signaled=false, the note field surfaces postgres's NOTICE explaining why (e.g. 'not a PostgreSQL backend process' for a non-pg PID, 'must be a member of...' for permission denial) so an agent can act on the specific cause rather than guess from a three-way list.

ParametersJSON Schema
NameRequiredDescriptionDefault
pidYesBackend PID to signal.
modeNo`cancel` aborts the current query; `terminate` closes the connection entirely.cancel

Output Schema

ParametersJSON Schema
NameRequiredDescription
pidYesEchoed back from the request.
modeYesEchoed back, after the safer 'cancel' default is applied.
noteYesOn `signaled: false`, postgres's own NOTICE explaining why -- act on this, not on the boolean.
signaledYesWhat pg_cancel_backend / pg_terminate_backend returned.

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well beyond the annotations: explains that cancel is graceful and terminate is forceful, that it changes database session state, and describes the signaled=false note behavior including concrete examples of cause-specific messages. Annotations already mark it destructive and non-readonly, and the description enriches rather than contradicts them.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is information-dense without padding. Core operation is front-loaded, followed by prerequisites, permission nuances, and failure interpretation. Each sentence contributes necessary operational knowledge.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Covers prerequisites, permissions, mode differences, failure causes, and how to find inputs. An output schema exists, so return value details are not required. Nothing material is missing for an agent to select and invoke this tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3. The description adds value by explaining the practical meaning of mode ('Cancel is graceful; terminate is forceful') and how to obtain the PID, plus the behavior of signaled=false for interpreting failures. This exceeds baseline but PID semantics remain largely defined by the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description states a specific verb/resource pair: cancel a running query or terminate a backend by PID. It names the signal equivalents (SIGINT/SIGTERM), making the behavior unambiguous. It is clearly distinguished from sibling read-only tools like pg_health and pg_inspect_locks.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit guidance on when to use the tool and prerequisites: find PID via pg_health or pg_inspect_locks, require ALLOW_WRITES=1, and need pg_signal_backend role or superuser. It also explains the permission limitation around superuser-owned backends, so an agent knows when this tool will fail.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pg_list_extensionsList installed extensionsA
Read-onlyIdempotent

List installed PostgreSQL extensions. Returns name, version, schema, and description. Useful to check for pgvector, postgis, pg_stat_statements, uuid-ossp, etc. before writing queries that rely on them.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes

TDQS

A4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false, so the safety profile is well covered. The description adds the return column names (name, version, schema, description), which is useful, but it does not add meaningful behavioral context beyond what the annotations and output schema already imply.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and front-loaded with the core action, followed by relevant return fields and a concrete use case. Each sentence earns its place, and the concrete extension examples add practical value without bloat.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple, parameterless, read-only introspection tool with rich annotations and an output schema, the description is complete. It explains what the tool lists, what it returns, and why an agent would use it, leaving no obvious gap for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters and the schema has no properties, so there is no parameter semantics risk for the agent. Per the rubric, a zero-parameter tool receives a baseline of 4; no additional parameter documentation is needed.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a clear verb and resource: 'List installed PostgreSQL extensions.' It also mentions the returned fields, making the tool's purpose unmistakable. However, it does not explicitly contrast itself against the many sibling list_* tools, so sibling differentiation relies on the object type rather than direct comparison.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives a concrete use case: checking for extensions like pgvector, postgis, pg_stat_statements, and uuid-ossp before writing dependent queries. This provides clear context for when to invoke the tool, though it does not state when not to use it or mention any alternative tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pg_list_functionsList functions and proceduresA
Read-onlyIdempotent

List functions, procedures, and aggregates in a schema. Returns name, arguments, return type, kind (function/procedure/aggregate/window), and implementation language.

ParametersJSON Schema
NameRequiredDescriptionDefault
schemaNoSchema name (defaults to 'public').public

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already establish that the tool is read-only, idempotent, open-world, and non-destructive. The description adds useful behavioral detail by specifying exactly what is returned: name, arguments, return type, kind, and implementation language. It does not mention ordering or error behavior, but that is not essential for a simple listing tool with an output schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single sentence that front-loads the action and resource before enumerating return fields. There is no filler, redundant restatement, or unnecessary detail. Every part of the sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple listing tool, the combination of a complete parameter schema, a full output schema, and safety-relevant annotations means nothing critical is missing. The description adds the remaining contextual detail by naming the output fields. An agent can invoke this tool correctly without further information.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema fully documents the single optional 'schema' parameter, including its default and meaning. The description adds little beyond reaffirming the schema scope, so it does not substantially improve on what the schema already provides. This matches the baseline for high schema description coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb ('List'), identifies a concrete resource ('functions, procedures, and aggregates'), and scopes that resource to a schema. This clearly distinguishes it from sibling list tools such as pg_list_tables and pg_list_views. An agent can immediately understand what this tool operates on.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There are no explicit 'when to use' or 'when not to use' instructions, and no alternative tools are named. However, the description makes the usage context clear: it is for listing function-like objects within a schema, which is distinct from the other list tools. The context is clear enough even without explicit exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pg_list_rolesList rolesA
Read-onlyIdempotent

List database roles (users and groups) with their login/superuser/createdb/createrole attributes and inherited role memberships. Use this to answer 'who has access to this database?' without needing to read pg_authid directly.

ParametersJSON Schema
NameRequiredDescriptionDefault
includeSystemNoIf true, include built-in `pg_*` roles (pg_read_all_data, pg_monitor, etc.).

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes

TDQS

A4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, openWorldHint, idempotentHint, and destructiveHint=false, so the safety profile is covered. The description adds useful content detail (attributes and inherited memberships) and notes it reads pg_authid indirectly, but does not disclose auth requirements, error behavior, or other operational traits.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two focused sentences: the first states exactly what is listed, the second gives the motivating use case. No filler or restatement of the title.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-optional-parameter listing tool with annotations and an output schema, the description is nearly complete. It could marginally strengthen the 'access' framing by noting that this returns role-level principals rather than object-level privileges, but the core information is sufficient.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%: the includeSystem parameter already has a clear description in the schema. The main description does not discuss parameters, so it adds no meaning beyond the schema, matching the baseline.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb and direct object: 'List database roles (users and groups)' and enumerates the exact attributes returned. The mention of answering 'who has access to this database?' and avoiding pg_authid clearly frames what this tool is for and separates it from table/view/function inspection siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives a concrete use case ('Use this to answer "who has access to this database?"') but does not explicitly state when not to use it or name an alternative sibling. It is clear context without exclusionary guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pg_list_schemasList schemasA
Read-onlyIdempotent

List non-system schemas in the database. Excludes pg_catalog, information_schema, and other pg_* internals.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare the operation is read-only, idempotent, open-world, and non-destructive. The description adds useful behavioral detail about which schemas are omitted, including `pg_catalog`, `information_schema`, and other `pg_*` internals, which is not conveyed by the annotations alone.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two short sentences with no redundancy. The primary action and scope are stated first, followed by a precise exclusion list, making the description efficient and easy to parse.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter, read-only listing tool with an output schema and clear annotations, the description is fully adequate. Nothing an agent needs to invoke this tool correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so the baseline is 4. There are no parameter semantics to clarify, and the description accurately reflects that the operation is unconditional.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb ('List') with a clear resource ('non-system schemas') and explicitly differentiates from siblings by stating what is excluded. An agent can immediately understand this tool is for enumerating user-defined schemas, not tables, views, or functions.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description clearly establishes the tool's scope by excluding system schemas, providing sufficient context for when to use it. It does not explicitly name an alternative tool, but the sibling list and the self-contained nature of the operation make the intended use obvious.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pg_list_tablesList tables in a schemaA
Read-onlyIdempotent

List tables (and optionally views) in a schema. Returns name, type (table/view/materialized view/foreign), and estimated row count (from reltuples; null = no ANALYZE yet on PG 14+; 0 may mean empty or unanalyzed on PG <= 13). Paginate via limit/offset on very large schemas.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax rows to return (default 500, max 10000).
offsetNoRows to skip for pagination (default 0).
schemaNoSchema name (defaults to 'public').public
includeViewsNoIf true, include views and materialized views.

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes

TDQS

A3.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description adds valuable behavioral detail beyond the annotations: it specifies the returned fields, explains the meaning of null and 0 row counts across PostgreSQL versions, and notes the reltuples source. This is rich context that helps an agent interpret results correctly.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and front-loaded: purpose first, return semantics second, pagination last. Every sentence adds distinct value, with no fluff or repetition of schema fields.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only listing tool with rich annotations and an output schema, this description is complete. It covers the tool's scope, return fields, important version-dependent caveats, and pagination. No critical information needed to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so parameters are already well documented. The description adds marginal semantic value by tying 'optionally views' to includeViews and mentioning pagination, but it does not substantially expand on the parameter meanings provided by the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool 'List tables (and optionally views) in a schema' with a specific verb and resource. It does not explicitly differentiate this from sibling tools like pg_list_views or pg_list_schemas, but the name and description make the core purpose unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no guidance on when to choose this tool over siblings such as pg_list_views or pg_describe_table. The only usage-related hint is pagination for large schemas, which addresses how to page results, not when to select this tool versus alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pg_list_viewsList views with definitionsA
Read-onlyIdempotent

List views and materialized views in a schema with their SQL definitions. Use this over pg_list_tables with includeViews: true when you want the view body, not just names.

ParametersJSON Schema
NameRequiredDescriptionDefault
schemaNoSchema name (defaults to 'public').public
includeMaterializedNoIf true, include materialized views.

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes

TDQS

A4.3/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, openWorldHint, idempotentHint, and destructiveHint=false, covering the safety profile. The description adds useful context about returning SQL definitions and covering materialized views, but does not deeply describe output behavior; the output schema exists to fill that gap.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences with no filler. The core functionality is stated first, followed by a precise differentiation from a sibling tool.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool is simple, has only two optional parameters fully described in the schema, carries read-only annotations, and has an output schema. The description supplies the missing usage context, so nothing an agent needs to call it correctly is absent.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents both parameters. The description adds no new meaning beyond associating views with a schema and mentioning materialized views, which aligns with the existing parameter descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('List') and resource ('views and materialized views in a schema'), and clarifies that SQL definitions are included. It also distinguishes itself from the sibling `pg_list_tables` with `includeViews: true`, making its purpose unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly tells the agent when to prefer this tool over `pg_list_tables` with `includeViews: true`: when the view body is needed, not just names. This provides clear selection logic among sibling tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pg_queryRun SQL queryA
Destructive

Run a SQL query against the configured PostgreSQL database. Postgres itself is the primary safety gate: the role in DATABASE_URL enforces what queries can succeed. The recommended posture is a least-privileged role (e.g. one granted pg_read_all_data), which makes writes server-rejected regardless of any env var. ALLOW_WRITES=1 is a secondary belt-and-braces gate - it lifts the in-server BEGIN READ ONLY wrapper, but it cannot grant privileges the role lacks. Useful for managed databases where creating a second role is awkward. For read-only access where you want the guarantee in the tool name, prefer pg_readonly. Use params for parameterized queries to avoid SQL injection. Params can be strings, numbers, booleans, null, arrays (for postgres arrays / ANY), or objects (for json/jsonb columns). Dates and UUIDs can be passed as ISO strings. Large result sets are truncated to POSTGRES_MAX_ROWS (default 1000) with a truncated: true flag.

ParametersJSON Schema
NameRequiredDescriptionDefault
sqlYesThe SQL statement to execute. Hard cap of 1 MB.
paramsNoPositional parameters referenced as $1, $2, ... in the SQL.

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYesResult rows, capped at POSTGRES_MAX_ROWS. Values are whatever JSON type pg parsed the column into.
fieldsYesResult column descriptors, in select-list order.
commandNoPostgres command tag (`INSERT`, `CREATE TABLE`, ...). Absent on the cursor path -- read absence as 'row-returning statement, command unknown'.
rowCountYesRows AFFECTED for DML -- not necessarily rows.length -- and rows returned on the cursor path. Null when pg reported no count.
truncatedNoPresent and true only when the result hit POSTGRES_MAX_ROWS and rows were dropped.

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already signal destructive capability, and the description adds meaningful behavioral context: Postgres role privileges are the primary safety gate, ALLOW_WRITES only lifts the read-only wrapper, and writes are server-rejected without the role's permission. It also discloses result truncation behavior and the truncated flag, going beyond the structured metadata.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core purpose, then efficiently adds safety, alternatives, parameter usage, and truncation behavior. Each sentence provides actionable details without redundancy or filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the output schema exists and the tool is a general SQL executor, the description covers what an agent needs: what the tool does, safety constraints, parameterized usage, supported types, and result limits. No critical invocation detail is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema already covers both parameters fully, and the description adds substantial extra meaning: supported parameter types (strings, numbers, booleans, null, arrays, objects), ISO-format handling for dates/UUIDs, and positional $1/$2 binding. It also advises using params to avoid SQL injection.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'Run a SQL query against the configured PostgreSQL database.' It also distinguishes this tool from the read-only sibling 'pg_readonly', so an agent can tell them apart without inspecting schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly says to prefer pg_readonly for read-only access, explains the role-based safety posture, and describes when this tool is useful (managed databases where creating a second role is awkward). It gives clear guidance on using parameters for safety.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pg_readonlyRun read-only SQLA
Read-onlyIdempotent

Run a SQL statement with no persistent data changes. Always executes inside a BEGIN READ ONLY transaction regardless of ALLOW_WRITES, so postgres itself rejects any INSERT/UPDATE/DELETE/DDL and the transaction is always rolled back. Use this whenever the goal is to read - SELECT, EXPLAIN, SHOW, VALUES, WITH ... SELECT, etc. Scope caveat for hosts that auto-allow this tool: READ ONLY constrains writes to the DATABASE, not every side effect. Functions whose effect is outside the table data - pg_cancel_backend / pg_terminate_backend, pg_read_file, lo_export, COPY ... TO PROGRAM - are NOT blocked here and are NOT behind the ALLOW_WRITES gate that pg_kill sits behind. They still require the privileges the DATABASE_URL role holds, so a least-privileged role (e.g. pg_read_all_data) is what actually bounds this tool. Use params for parameterized queries to avoid SQL injection. Params can be strings, numbers, booleans, null, arrays (for postgres arrays / ANY), or objects (for json/jsonb columns). Large result sets are truncated to POSTGRES_MAX_ROWS (default 1000) with a truncated: true flag.

ParametersJSON Schema
NameRequiredDescriptionDefault
sqlYesThe SQL statement to execute. Hard cap of 1 MB.
paramsNoPositional parameters referenced as $1, $2, ... in the SQL.

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYesResult rows, capped at POSTGRES_MAX_ROWS. Values are whatever JSON type pg parsed the column into.
fieldsYesResult column descriptors, in select-list order.
commandNoPostgres command tag (`INSERT`, `CREATE TABLE`, ...). Absent on the cursor path -- read absence as 'row-returning statement, command unknown'.
rowCountYesRows AFFECTED for DML -- not necessarily rows.length -- and rows returned on the cursor path. Null when pg reported no count.
truncatedNoPresent and true only when the result hit POSTGRES_MAX_ROWS and rows were dropped.

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well beyond the readOnlyHint annotation by explaining the BEGIN READ ONLY transaction, unconditional rollback, and the caveat that READ ONLY does not block every side effect. It also discloses privilege constraints, parameterized-query advice, and result truncation behavior, giving the agent a thorough behavioral model.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long but every sentence carries essential operational information: purpose, transaction semantics, side-effect caveat, privilege model, injection safety, and truncation. It is front-loaded with the primary purpose and then layers in the caveats a caller must know.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a flexible SQL execution tool with an output schema and rich annotations, the description covers all major calling concerns: what can run, transaction guarantees, exceptions to read-only enforcement, parameter encoding, and row limits. Nothing essential for selecting or invoking this tool is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds real value beyond the schema by explaining how params map to Postgres types: strings, numbers, booleans, null, arrays for ANY, and objects for json/jsonb. This is genuinely useful for constructing valid calls.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a clear, specific verb and resource: 'Run a SQL statement with no persistent data changes.' It names the exact statement types accepted (SELECT, EXPLAIN, SHOW, VALUES, WITH ... SELECT), which distinguishes it from sibling read tools like pg_explain or pg_list_tables.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says 'Use this whenever the goal is to read' and lists the applicable SQL forms. It also warns about side-effect functions like pg_cancel_backend that are not blocked, and contrasts with pg_kill, giving the agent concrete when-to-use and when-not-to-use guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pg_replication_statusReplication statusA
Read-onlyIdempotent

Replication overview: configured replication slots, connected replicas (from pg_stat_replication), and current WAL position. Use on primary to spot lagging or disconnected replicas, on replicas to see upstream status. Returns empty arrays on a standalone (non-replicated) database rather than erroring.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
slotsYesEmpty both on a standalone database and when the fetch failed -- check `_warnings`.
replicasYes
_warningsNo
is_replicaYespg_is_in_recovery(). Null means the probe failed, NOT 'primary'.
wal_positionYesLast received LSN on a replica, current LSN on a primary. Null when the probe failed.

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, and destructiveHint=false. The description adds valuable behavior beyond these hints: it sources data from pg_stat_replication, distinguishes primary vs replica behavior, and promises empty arrays on standalone databases instead of errors. This is useful context beyond the structured annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, each earning its place: first defines scope, second gives usage guidance, third covers the standalone edge case. Information is front-loaded and no filler is present.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a parameterless read-only status tool with annotations covering safety and an output schema available, the description is complete. It covers what data is returned, where to run it, and the edge-case behavior on standalone databases.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters and schema coverage is already complete at 100%. With no params to document, the baseline of 4 applies; the description need not add parameter-level detail.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly identifies the tool as a replication overview covering replication slots, connected replicas, and WAL position. It distinguishes itself from sibling tools by being specifically about replication status rather than general database health or performance.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly tells the agent when to use it: on a primary to spot lagging/disconnected replicas, and on replicas to see upstream status. It does not name specific alternatives or exclusions, but the guidance is clear enough for this specialized status tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pg_search_columnsSearch columns by nameA
Read-onlyIdempotent

Search for columns by name across all user schemas. Supports SQL LIKE patterns (% matches any substring, _ matches one character). Case-insensitive. Use this instead of iterating pg_describe_table when the user asks 'which tables have X'.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax rows to return (default 100).
schemaNoLimit to this schema. If omitted, searches all user schemas.
patternYesLIKE pattern. Use '%' for wildcard: 'user_id', '%email%', 'created_%'.

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly, idempotent, and non-destructive behavior. The description adds useful behavioral context beyond annotations: LIKE semantics, case-insensitivity, and cross-schema search behavior. No contradictions with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four short sentences, each earning its place: scope, LIKE syntax, case-insensitivity, and when-to-use guidance. Front-loaded with the primary action and scoping, no filler or repetition.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the rich annotations, a complete input schema with 100% parameter coverage, and an output schema, the description covers all essential behavioral and routing information. There is no material gap for an agent to select and invoke this tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3. The description still adds value by clarifying that `%` is a substring wildcard, `_` matches exactly one character, and matching is case-insensitive, which goes beyond the schema's brief 'LIKE pattern' descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb ('Search'), a precise resource ('columns by name'), and a clear scope ('across all user schemas'). It distinguishes itself from pg_describe_table by naming what it is not and why it would be preferred.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly tells the agent when to use this tool: 'Use this instead of iterating pg_describe_table when the user asks which tables have X.' This gives a direct usage rule and names the alternative, so no inference is needed.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pg_seq_scan_tablesFind tables with heavy sequential scansA
Read-onlyIdempotent

Tables with high sequential-scan counts relative to index scans - the first place to look for missing-index candidates. Returns {rows, stats_reset, stats_reset_age_seconds}: each row has seq_scans, idx_scans, live tuples, and the ratio. A high ratio on a large table usually means a query is reading the whole table where an index would suffice. Pair with pg_top_queries to find which query is doing it. These counters are cumulative since the last statistics reset, so every ratio here is only meaningful relative to the top-level stats_reset (and stats_reset_age_seconds). A ratio measured over a window that was reset minutes ago describes that window, not the workload; stats_reset: null means the start of the window is unknown. On PostgreSQL 16+ each row also carries last_seq_scan and last_idx_scan timestamps (null = no such scan since the reset), which separate 'scanned hard months ago' from 'being scanned right now' in a way the raw counts cannot.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax rows to return (default 20).
schemaNoLimit to one schema. If omitted, all user schemas are included.
minSizeNoMinimum live tuple count to include (default 1000, filters out tiny/empty tables).

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes
_warningsNo
stats_resetYesEvery counter in `rows` is cumulative SINCE this point. Null = start of the window unknown.
stats_reset_age_secondsYesSeconds since `stats_reset`; null whenever that is null.

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations establish read-only/idempotent behavior, and the description goes well beyond them by explaining cumulative counters, the meaning of stats_reset and null, and the PostgreSQL 16+ last-scan timestamps. These caveats prevent a common misreading of the ratio.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Longer than average, but every sentence carries a distinct piece of information: purpose, return shape, interpretation, reset caveat, and version-specific fields. The purpose is front-loaded and the structure progresses naturally from decision to caveats.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers what the tool does, how to interpret its output, the critical stats-reset caveat, and version differences, while annotations cover safety. An agent has enough context to invoke it correctly and use the results.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

All three parameters already have descriptions in the schema, including defaults and bounds, so the description need not repeat them. It adds useful context that the ratio matters on large tables and uses live tuples, but no parameter-specific syntax beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence states a specific diagnostic goal — identifying tables with high sequential-scan counts relative to index scans — and names the tool as the first place to look for missing-index candidates. This is clearly differentiated from sibling diagnostics like pg_unused_indexes and pg_top_queries.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit when-to-use guidance ('the first place to look') and explains the follow-up action: pair with pg_top_queries to find the responsible query. It does not explicitly say when to prefer pg_unused_indexes or pg_index_advisor, so it stops short of full alternative routing.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pg_table_bloatEstimate table bloatA
Read-onlyIdempotent

Estimate table bloat (dead tuples + free space) for tables in a schema. Returns live tuples, dead tuples, dead-tuple ratio, last_vacuum / last_autovacuum timestamps, and total relation size. A high dead_ratio with a stale last_autovacuum is a sign a table needs VACUUM. On PostgreSQL 19+ every row also carries stats_reset: the last time THAT relation's counters were reset via pg_stat_reset_single_table_counters(). Read it before trusting anything else in the row -- a reset zeroes live_tuples and dead_tuples AND clears last_vacuum / last_autovacuum / last_analyze together, so a table reset a minute ago is indistinguishable from a pristine one without it. The key is ABSENT on older servers rather than null; null on PG19+ means this relation's counters have never been reset.

Three methods are available via the method parameter:

  • estimate (default): reads pg_stat_user_tables -- fast, no extensions, ANALYZE-driven approximations. Use this first.

  • approx: uses pgstattuple_approx() -- fast sampling pass, more accurate than estimates, requires the pgstattuple extension.

  • exact: uses pgstattuple() -- full table scan, exact counts, slow on large tables, requires the pgstattuple extension. Always pass schema with method='exact' -- scanning all user tables in one statement will hit statement_timeout on non-trivial databases. Install pgstattuple with CREATE EXTENSION pgstattuple (requires superuser).

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax rows to return (default 50).
methodNoBloat measurement method. 'estimate' (default) uses pg_stat_user_tables (fast, no extensions). 'approx' uses pgstattuple_approx() (fast sampling, more accurate). 'exact' uses pgstattuple() (full scan, exact but slow). Both 'approx' and 'exact' require the pgstattuple extension.estimate
schemaNoLimit to one schema. If omitted, all user schemas are included.
minDeadRatioNoMinimum dead-tuple fraction to include - dead / (live + dead). Default 0.1 = 10%.

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the readOnlyHint and idempotentHint annotations, the description discloses non-obvious behavior: stats_reset semantics on PostgreSQL 19+, the distinction between absent and null keys, the fact that reset clears several counters together, and the statement_timeout risk when calling method='exact' without a schema. This is rich, actionable behavioral context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is longer than average, but each paragraph earns its place: purpose/outputs, a critical stats_reset caveat, and method selection with a concrete timeout warning. The structure is logical and front-loaded with the core action, though a slightly tighter version could avoid some repetition with schema method descriptions.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's moderate complexity, output schema availability, and 100% parameter schema coverage, the description covers everything needed to invoke it correctly: returned columns, interpretation cues, method tradeoffs, extension installation, superuser requirement, and a specific call-safety warning for exact scans. No obvious operational gap remains.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3. The description adds substantial meaning beyond the schema for the method parameter, explaining tradeoffs, extension prerequisites, and a timeout warning tied to schema usage. It does not add comparable detail for limit or minDeadRatio, but those are already well described by the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('Estimate'), a specific resource ('table bloat ... for tables in a schema'), and enumerates the returned diagnostic fields. This clearly distinguishes it from sibling tools like pg_unused_indexes or pg_health while matching the tool name and title.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit when-to-use guidance: 'estimate' is marked as the default and 'Use this first', while 'approx' and 'exact' are contrasted by accuracy, speed, extension requirements, and timeout risk. It also provides a practical interpretation rule for high dead_ratio alongside stale last_autovacuum.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pg_table_privilegesShow table privilegesA
Read-onlyIdempotent

Show which roles have which privileges (SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER) on a table or on every table in a schema. If table is omitted, the result spans every table in schema, ordered by table then grantee. Use this to answer 'who can write to this table?' or to audit schema-wide access before a migration. Visibility caveat: backed by information_schema.table_privileges, which postgres filters by what the calling role can see. A least-privileged role may not see grants involving unrelated third-party roles. For a complete picture, run as a superuser or a member of pg_read_all_data.

ParametersJSON Schema
NameRequiredDescriptionDefault
tableNoTable name. Omit to list privileges for all tables in the schema.
schemaNoSchema name (defaults to 'public').public

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already provide readOnlyHint=true, idempotentHint=true, and destructiveHint=false, so the agent knows it is a safe read. The description adds crucial behavioral context beyond annotations: the tool is backed by information_schema.table_privileges, which postgres filters by the calling role's visibility, and a least-privileged role may miss grants. It also specifies ordering (by table then grantee) and the behavior when `table` is omitted. This is exactly the kind of behavioral disclosure that annotations alone cannot convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is roughly four sentences, each carrying meaningful content: purpose, omission behavior, usage scenarios, and the visibility caveat with a recommendation. It is front-loaded with the core purpose and stays information-dense without wordiness. It could be slightly tighter (e.g., merging the usage and caveat sentences), but it is still well-structured and earns its length.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only tool with two optional parameters and an output schema present, the description covers all essential invocation context: what happens with and without `table`, the schema default, the visibility limitation, and how to get complete results. Since an output schema exists, the lack of a return-format explanation is not a gap. Nothing an agent needs in order to call this correctly or interpret the scope is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, with both `table` and `schema` having descriptions. The description rephrases the omission behavior already present in the schema ('Omit to list privileges for all tables in the schema'), so it adds no new parameter-specific meaning. The only extra value is the ordering detail (by table then grantee), which is output behavior rather than parameter semantics. Thus baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb/resource (shows roles and their privileges) and enumerates the specific privilege types (SELECT, INSERT, etc.). It distinguishes itself from sibling tools (e.g., pg_list_tables, pg_describe_table) by focusing exclusively on privilege grants. It also provides a concrete use question ('who can write to this table?'), which removes any ambiguity about the tool's intent.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly identifies when to use this tool: to answer 'who can write to this table?' or to audit schema-wide access before a migration. It also advises running as a superuser for a complete picture, which is a practical usage hint. However, it does not name alternatives or state when *not* to use it, so it lacks explicit exclusions. This fits the 'clear context, no exclusions' benchmark.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pg_top_queriesTop queries by execution timeA
Read-onlyIdempotent

Top N queries by total or mean execution time. Requires the pg_stat_statements extension to be installed and enabled (most managed Postgres providers have it on by default). Returns {rows, stats_reset, stats_reset_age_seconds, dealloc}: each row has normalized query text (constants replaced with ?), call count, total/mean/min/max time in ms, rows returned, and cache hit ratio. Use this to find slow queries worth optimizing. calls and total_time_ms are cumulative since the last pg_stat_statements_reset(), so this ranking only describes the window that started at the top-level stats_reset (with stats_reset_age_seconds beside it). This is pg_stat_statements' OWN reset clock, read from pg_stat_statements_info -- it is independent of the stats_reset reported by pg_seq_scan_tables / pg_unused_indexes, which comes from pg_stat_database, so do not compare the two timestamps or assume one implies the other. stats_reset: null means the start of the window is unknown, not that it covers all time. READ dealloc BEFORE TRUSTING THE RANKING: it counts how many times entries for the LEAST-EXECUTED statements were evicted because more distinct statements were seen than pg_stat_statements.max allows. A non-zero dealloc means this ranking is drawn from an INCOMPLETE population -- queries may be missing from these results entirely, and an evicted query's counters restart from zero if it runs again, understating it. The larger dealloc is, the more churn, so 'not in the top N' stops being evidence that a query is cheap. Raise pg_stat_statements.max to get a complete picture. On pg_stat_statements < 1.9 (before Postgres 14) pg_stat_statements_info does not exist, so stats_reset, stats_reset_age_seconds and dealloc are omitted entirely rather than returned as nulls, and a _warnings entry says so. On pg_stat_statements >= 1.10 (Postgres 15+), also returns io_read_time_ms and io_write_time_ms to separate IO-bound from CPU-bound queries (null when track_io_timing = off or the query did no measurable IO -- enable track_io_timing in postgresql.conf to get non-null values). Scoped to the database in DATABASE_URL: pg_stat_statements is cluster-wide, so results are filtered by dbid to match every other tool here rather than leaking query text from unrelated databases sharing the cluster.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoNumber of rows to return (default 20).
orderByNoRanking: total_time (cumulative impact), mean_time (worst per-call), or calls (hottest).total_time

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes
deallocNoTimes least-executed entries were evicted for exceeding pg_stat_statements.max. Non-zero means this ranking is drawn from an INCOMPLETE population. Absent below extension 1.9.
_warningsNo
stats_resetNopg_stat_statements' OWN reset clock, independent of the pg_stat_database one the table/index tools report -- never compare the two. Absent below extension 1.9.
stats_reset_age_secondsNo

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description goes well beyond the readOnly/idempotent annotations by disclosing cumulative counter semantics, the meaning of stats_reset: null, dealloc-based eviction risks, version-dependent behavior, and database scoping. It explains the exact conditions under which the ranking can be misleading and what remediation looks like. This is exceptionally transparent and contradicts no annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The purpose and primary use case are front-loaded in the first two sentences, and the remaining paragraphs each address a distinct operational risk: reset clocks, dealloc churn, version differences, IO timing fields, and cross-database leakage. It is long, but every sentence earns its place because it prevents a real misinterpretation of the results.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers all the contextual knowledge an agent needs: extension prerequisite, exact return shape, reset-window semantics, dealloc caveats with a remedy, version-specific field availability, and scoping behavior. Combined with the rich input and output schemas, there are no meaningful gaps left for the agent to infer.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Both parameters already have full schema descriptions and the enum is well documented, so the baseline is 3. However, the description adds interpretive depth beyond the schema: the ranking values are measured over a reset-defined window, and a nonzero dealloc can invalidate conclusions drawn from any ordering. That context materially helps an agent choose and interpret orderBy.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific operation: returning top N queries by total or mean execution time, and names the underlying resource (pg_stat_statements). It also states the intended use ('find slow queries worth optimizing'), which distinguishes it from sibling tools like pg_explain or pg_index_advisor. Even without explicit sibling names, an agent can tell what this tool is for.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives a clear prerequisite (pg_stat_statements must be installed/enabled) and states its intended use case. It also warns against conflating its stats_reset timestamp with those from pg_seq_scan_tables/pg_unused_indexes. It stops short of explicitly saying 'when not to use this and use alternative X instead', so it earns a 4 rather than a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pg_unused_indexesFind unused indexesA
Read-onlyIdempotent

Indexes that have never been scanned or have very low usage, largest first. Each unused index costs write amplification (every INSERT/UPDATE maintains it) and disk space, so before adding a new index, check whether the fix is to drop a dead one. Returns {rows, stats_reset, stats_reset_age_seconds}. READ THIS BEFORE RECOMMENDING A DROP: scans is a counter, not a verdict. It only counts since the last statistics reset, which is why the top-level stats_reset and stats_reset_age_seconds are part of the answer. If the counters were reset an hour ago, EVERY index looks unused; if stats_reset is null, the start of the window is unknown and the counts prove nothing. This list is only trustworthy once the reset age comfortably exceeds the slowest cycle that could use the index - a monthly report, a quarterly close, a yearly job, a failover-only query path. PRIMARY KEY and UNIQUE indexes are already excluded from these results: they enforce a constraint and stay load-bearing at zero scans, so they never appear here and their absence is not evidence of anything. On PostgreSQL 16+ each row also carries last_idx_scan, the timestamp of the most recent scan (null = never scanned since the reset). 'Not scanned since 2026-02-14' is a far better basis for a decision than a bare count. On PostgreSQL 18+, do not fall back on the old 'the leading column is never filtered, so this index is dead weight' reasoning. Skip scan lets the planner use a multi-column btree whose leading column is unconstrained, so such an index can now be doing real work.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax rows to return (default 50).
schemaNoLimit to one schema. If omitted, all user schemas are included.
maxScansNoInclude indexes with scan count <= this (default 10). Use 0 for 'never scanned'.

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsYes
_warningsNo
stats_resetYesEvery counter in `rows` is cumulative SINCE this point. Null = start of the window unknown.
stats_reset_age_secondsYesSeconds since `stats_reset`; null whenever that is null.

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already mark the operation as read-only and non-destructive, but the description adds substantial behavioral context: scan counters only reflect the window since stats_reset, PK/UNIQUE indexes are excluded, PostgreSQL 16+ includes last_idx_scan, and PostgreSQL 18+ changes skip-scan reasoning. This goes far beyond what annotations provide.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with a clear definition and return shape, then each paragraph covers a distinct and necessary caveat. Though long, every sentence earns its place given the dangerous 'recommend a drop' use case, and there is no repetition of annotation data.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description is complete for safe invocation and interpretation: it explains the output envelope, reset-window caveats, excluded constraint indexes, version-specific fields, and Postgres 18 behavior. The output schema exists, so the description need not exhaustively specify every return field.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema already documents all three parameters fully, so the baseline is 3. The description enriches maxScans by explaining that the scan counter is only meaningful relative to stats_reset, which is critical for interpreting that parameter. It does not add much for limit or schema, but their schema descriptions are already clear.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

First line defines the output precisely: indexes never scanned or with very low usage, largest first, with the title supplying the 'find' verb. It is clearly distinct from the sibling pg_index_advisor, which is oriented toward recommending indexes to add rather than identifying dead ones.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides a concrete trigger: before adding a new index, check whether dropping a dead one is the real fix. It also warns when the data is not trustworthy after a stats reset, but it does not explicitly compare against sibling tools or state when not to use this tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

TDQS

A4.3/5.0
Disambiguation5/5

Each tool has a clearly distinct purpose. pg_readonly and pg_query are differentiated by write ability; other tools target specific schema inspection, performance analysis, or admin functions with no overlap.

Naming Consistency4/5

Most tools follow a 'pg_verb_noun' pattern (e.g., pg_list_tables, pg_describe_table). A few deviate (pg_readonly, pg_query, pg_health) but the naming remains clear and predictable.

Tool Count5/5

21 tools is well-scoped for a PostgreSQL database server, covering querying, schema exploration, performance diagnostics, and administration without being overwhelming.

Completeness4/5

The tool surface is comprehensive, covering read/write queries, schema inspection, performance tuning, and health checks. Minor omissions like explicit VACUUM or index creation tools are offset by the advisor and general query tool.

Maintenance

ActivityActive
ResponsivenessSyncing

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    Provides authenticated access to PostgreSQL databases for Claude AI, enabling users to browse database tables, discover schemas, and execute custom SQL queries through natural language interaction.
  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables natural language querying of PostgreSQL databases through the Model Context Protocol. It translates user questions into validated SQL, executes read-only queries safely, and returns results to MCP-compatible clients like Claude Desktop.
  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables AI assistants to safely interact with PostgreSQL databases, perform queries, inspect schemas, and analyze query performance.
    2

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/YawLabs/postgres-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server