Agent Skills

pg_licht

A PostgreSQL MCP server that exposes schema exploration tools

Install

Install and configure the MCP from https://sqlambda.github.io/pg_licht/ now. Follow the documentation's installation instructions, ask me for anything you can't complete yourself, and verify its tools load.
README

One read-only PostgreSQL MCP server for every database you run.

pg-licht gives an AI assistant 75 tools and 12 guided investigations for exploring schemas, reading statistics and diagnosing live servers. It can never change your data, it never returns rows from your tables, and it answers for a whole fleet from a single process.

Install Browse the 75 tools

75 read-only tools in 11 groups

12 guided investigations

1 process for any number of connections

0 statements that can write

14–18 PostgreSQL majors tested on every change

One server for the whole fleet

Every connected tool takes a connection argument, so one pg-licht answers for one database or for hundreds. Connections are named once, in one file, and opened only when a call needs them.

One pg-licht

A hundred databases, one entry in your MCP client

  • One process, one connections file, credentials kept in libpq's own .pgpass and service files.
  • One tool listing in the agent's session. Its size is the same with 1, 150 or 1,000 connections configured, and startup stays under a tenth of a second.
  • Connections open when a call needs them; up to 32 idle ones stay warm, and the least recently used is closed first.
  • Ask a replication group, an instance or any labelled group in one call: up to 32 members per call, 16 at a time, one result per member, and any beyond 32 listed as skipped rather than silently left out.

One server per database

A hundred entries, a hundred sets of settings

  • A hundred processes to start, update and keep logged in.
  • A hundred copies of the same tools, each under a different server name, for the agent to tell apart.
  • A client that loads tool definitions up front carries every copy in the session.
  • No way to ask the same question of a replica and its primary in one call.

Measured with 1, 150 and 1,000 configured connections. One caveat worth knowing: listConnections is roughly 400 bytes a connection, so on a large fleet ask for the ones you want with pattern. listConnections and listTopology both take one, matched against names and labels.

Read-only by construction, not by convention

Safety is enforced by PostgreSQL on every call, not left to how the assistant behaves.

A read-only transaction per call

Every call to a database runs inside its own READ ONLY transaction. A write, even one reached through a bug, fails in PostgreSQL rather than succeeding. The pooler tools send PgBouncer only SHOW, as the stats user you give them, which PgBouncer allows nothing else.

A timeout on every statement

statement_timeout is set on the same transaction, two minutes by default and set per connection, so no call can hold a backend indefinitely.

No SQL built from arguments

Catalog queries are parameterized. No schema, table or search term is ever concatenated into SQL text.

Plans are proven before anything runs

explainQuery executes a statement only after its plan is shown to contain no write, and only within a memory and CPU budget taken from the host you declared.

Safe behind PgBouncer

The guards are transaction-scoped, so they hold under transaction pooling. The whole suite runs through a real PgBouncer on every change.

Honest about privileges

checkPrivileges says which tools the connecting role can really use: 59 of 72 database tools for a bare login role, 67 with pg_monitor.

It reads the catalog, not your tables

pg-licht describes structure and statistics. It is built so that an assistant can understand a database without seeing what is in it, and it is exact about the few places where values can still appear.

Never

  • Writes, alters or deletes anything.
  • Returns rows from a table. The two tools that read one return only a yes or no (checkKey) or a plan with row counts (explainQuery).
  • Returns a password it holds, a user mapping's options, or a subscription's connection string.
  • Returns a constant pg_qualstats recorded. It keeps the literal of every predicate it samples; predicateStats and suggestIndexes read table.column = ?, and a test fails the build if a seeded literal ever appears in either.

Where values can appear (13 tools, each marked)

  • Statement text: what sessions are running now, and what pg_stat_statements recorded.
  • Column statistics: most common values and histogram bounds.
  • Plans and definitions, which repeat the literals written into them.
  • Settings, such as a standby's primary_conninfo.

Each of these is limited further by PostgreSQL's own permissions: a role without SELECT on a column gets none of its statistics, and a role without pg_read_all_stats sees only its own statements. Connect as the narrowest role that answers the question. The full list is under What reaches the caller in the manual.

Built for the questions operators actually ask

Guided investigations

12 prompts walk the assistant through slow queries, lock contention, deadlocks, bloat, disk space, replication slots and schema changes, tool by tool.

Beyond what PostgreSQL measures

Where pg_wait_sampling, pg_stat_kcache and pg_qualstats are installed: what the server has been waiting on, the CPU and real disk reads behind each statement, which predicates throw rows away, and the index that would serve them — all joined on the query_id statementStats reports, and each suggestion ready to test with evaluateIndex without building it. A counter the server's platform does not measure is reported as unavailable, with the reason, never as zero.

Answers you can cap

The listings and searches that grow with a database take a pattern or a narrower search, and the largest answers narrow themselves by default. Set a cap in budgets.ini and an answer past it is refused with the arguments that make it smaller, rather than returned.

The pooler too

Point a section at PgBouncer's admin console and ask whether the pools keep up: clients waiting for a server and for how long, servers in use against each pool's size, and the connections and settings behind them — read with SHOW alone, as the stats user you give them.

Topology aware

Label connections by instance and replication group. Roles are observed on each call, never configured, and verifyTopology checks the labels against the servers.

Answers a client can check

Every tool declares its output schema and is marked read-only, and clients on MCP 2025-06-18 or later receive structured results.

Tested where it matters

PostgreSQL 14 through 18, under AddressSanitizer, UndefinedBehaviorSanitizer, ThreadSanitizer and valgrind, through a pooler, with a standby, a cascading standby, a logical subscriber and a split brain, built with both g++ and clang, and on FreeBSD 14 and 15. The protocol and both configuration parsers are fuzzed on every change, and clang-tidy fails the build on any finding.

Packages that are installed before they ship

Every .deb and .rpm is installed on the distribution it targets and run before a release is published, its version checked against the binary inside it, and the Homebrew formula is built from source on every change.

Downloads you can verify

Every release asset carries signed build provenance: gh attestation verify proves a file was built by this repository's release workflow from the tagged commit. A SHA256SUMS file is published beside them for a check that needs no GitHub CLI.

Hardened binaries

The Linux binaries are position-independent, with full RELRO, a non-executable stack, stack protection and fortified libc calls — and each release binary is inspected for them before it ships, rather than trusting the flags that were asked for.

Documentation generated from the binary

The reference and llms.txt are built from the release itself, so they cannot describe a version that does not exist.

75 tools, grouped by what they answer

Two arguments narrow an answer by a string. The listings take pattern, a literal substring of a name: user_ finds user_x, not users. The three search tools take web_search, full-text search over names, comments and source, where order finds orders but ord finds nothing. Which tool takes which.

Install

brew tap sqlambda/pg-licht
brew install pg-licht

claude mcp add --transport stdio pg-licht \
  -e DATABASE_URL="postgresql://user@host/db" \
  -- pg_licht_mcp
# ~/.config/pg_licht/connections.ini
[billing_prod]
service  = billing_ro
instance = pg-prod-01

[billing_replica]
service           = billing_replica_ro
replication_group = billing-ha

Debian 13 and Rocky Linux 9 packages and tarballs are on the releases page; see INSTALL for each.

Search skills and MCP servers

Search across 31,816 skills and MCPs