{"name":"io.github.YawLabs/postgres-mcp","slug":"yawlabs-postgres-mcp","title":"PostgreSQL MCP Server","description":"PostgreSQL MCP server - query, schema introspection, explain, and health checks for AI assistants","url":"https://mcp.market/server/yawlabs-postgres-mcp","rating":null,"grade":"B","score":83,"certified":false,"status":"active","category":"data","tags":["data"],"presence":{"score":43,"stars":5,"forks":2,"downloads_week":3165,"last_push_at":"2026-09-19T03:59:02.000Z","license":"MIT"},"uptime":null,"claimed":false,"transport":"npm","callable_via_gateway":false,"default_price_micros":0,"repository":"https://github.com/YawLabs/postgres-mcp","website":"https://yaw.sh/mcp-servers/postgres-mcp/","version":"0.13.4","remotes":[],"packages":[{"registryType":"npm","identifier":"@yawlabs/postgres-mcp","version":"0.13.4","transport":{"type":"stdio"}}],"tools":[{"name":"pg_describe_table","description":"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 view","write_action":false,"price_micros":0,"input_schema":null},{"name":"pg_explain","description":"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 rolled back, so you can inspect a plan for an INSERT/UPDATE/DELETE without persisting the rows -- but ","write_action":false,"price_micros":0,"input_schema":null},{"name":"pg_health","description":"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_trans","write_action":false,"price_micros":0,"input_schema":null},{"name":"pg_inspect_locks","description":"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 mult","write_action":false,"price_micros":0,"input_schema":null},{"name":"pg_io_stats","description":"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:","write_action":false,"price_micros":0,"input_schema":null},{"name":"pg_kill","description":"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 ","write_action":true,"price_micros":0,"input_schema":null},{"name":"pg_list_extensions","description":"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.","write_action":false,"price_micros":0,"input_schema":null},{"name":"pg_list_functions","description":"List functions, procedures, and aggregates in a schema. Returns name, arguments, return type, kind (function/procedure/aggregate/window), and implementation language.","write_action":false,"price_micros":0,"input_schema":null},{"name":"pg_list_roles","description":"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.","write_action":false,"price_micros":0,"input_schema":null},{"name":"pg_list_schemas","description":"List non-system schemas in the database. Excludes `pg_catalog`, `information_schema`, and other `pg_*` internals.","write_action":false,"price_micros":0,"input_schema":null},{"name":"pg_list_tables","description":"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.","write_action":false,"price_micros":0,"input_schema":null},{"name":"pg_list_views","description":"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.","write_action":false,"price_micros":0,"input_schema":null},{"name":"pg_query","description":"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 `BEG","write_action":true,"price_micros":0,"input_schema":null},{"name":"pg_readonly","description":"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` constrai","write_action":true,"price_micros":0,"input_schema":null},{"name":"pg_replication_status","description":"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.","write_action":false,"price_micros":0,"input_schema":null},{"name":"pg_search_columns","description":"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'.","write_action":false,"price_micros":0,"input_schema":null},{"name":"pg_seq_scan_tables","description":"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 do","write_action":false,"price_micros":0,"input_schema":null},{"name":"pg_table_bloat","description":"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_singl","write_action":false,"price_micros":0,"input_schema":null},{"name":"pg_table_privileges","description":"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_pri","write_action":false,"price_micros":0,"input_schema":null},{"name":"pg_top_queries","description":"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 fi","write_action":false,"price_micros":0,"input_schema":null},{"name":"pg_unused_indexes","description":"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 th","write_action":false,"price_micros":0,"input_schema":null}],"scan":{"score":83,"grade":"B","scanned_at":"2026-09-19T19:28:36.272Z","report":{"scannerVersion":"0.1.5","scannedAt":"2026-09-19T19:28:36.164Z","components":{"code":{"score":20,"max":25,"notes":["3 source files scanned"]},"reliability":{"score":-1,"max":20,"notes":["no gateway calls yet and no remote to probe"]},"poisoning":{"score":-1,"max":15,"notes":["tools not inspected (local package is not executed); not counted"]},"auth":{"score":12,"max":15,"notes":["local package, no credentials required"]},"maintenance":{"score":15,"max":15,"notes":["last push 1 days ago"]},"identity":{"score":7,"max":10,"notes":["registry namespace matches repository owner"]}},"findings":[{"id":"exec.eval","severity":"medium","component":"code","title":"eval / new Function used","evidence":"dist/index.js: …ourceCode, sch); const validate = new Function(`${names_1.default.self}`, `${names_1.de…"}],"inputs":{"packages":[{"registryType":"npm","identifier":"@yawlabs/postgres-mcp","version":"0.13.4","found":true,"license":"MIT","hasInstallScripts":false,"dependencyCount":0,"publishedAt":"2026-09-19T03:56:38.449Z","repositoryUrl":"git+https://github.com/YawLabs/postgres-mcp.git","weeklyDownloads":3165}],"repo":{"found":true,"owner":"YawLabs","repo":"postgres-mcp","archived":false,"pushedAt":"2026-09-19T03:59:02Z","stars":5,"forks":2,"openIssues":2,"ownerType":"Organization","ownerAvatarUrl":"https://avatars.githubusercontent.com/u/260788729?v=4","ownerCreatedAt":"2026-02-11T00:00:33Z","license":"MIT"},"icon":{"url":"https://avatars.githubusercontent.com/u/260788729?v=4&s=128","source":"registry","width":128,"height":128},"presence":{"stars":5,"forks":2,"downloadsWeek":3165,"license":"MIT","lastPushAt":"2026-09-19T03:59:02.000Z","score":43}}}},"grade_history":[],"reviews":[]}