db-audit
Skill1 starsChanged 26 days agoMITView on GitHub
Use when the user asks to audit, health-check or review the performance of their own PostgreSQL or MySQL database, including RDS and Aurora. Read-only: connects with a read-only role, reads statistics and catalogs (top statements, cache hit, unused, duplicate and missing indexes, sequential scans, vacuum and purge lag, long transactions, lock waits, connections) and proposes fixes as migration files without applying them.
1 other project uses a version of this.
Adopted by
Most recently taken up by
- greglas75/zuvotheir own version, first seen Oct 16, 2026
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 1
- greglas75/zuvo★ 7same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository22 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- new-skillSkill
- review-skillSkill
- db-connection-budgetSkill
- db-explainSkill
- mysql-connectionsSkill
- mysql-indexingSkill
- mysql-keys-and-typesSkill
- mysql-online-ddlSkill
- mysql-query-patternsSkill
- mysql-throughput-per-connectionSkill
- mysql-transactions-and-lockingSkill
- pg-config-by-sizeSkill
- pg-connectionsSkill
- pg-indexingSkill
- pg-keys-and-typesSkill
- pg-observabilitySkill
- pg-online-migrationsSkill
- pg-query-patternsSkill
- pg-throughput-per-connectionSkill
- pg-transactions-and-lockingSkill
- pg-vacuum-and-bloatSkill
The file
We split this skill into the rules it gives an agent
10 rules. About 0 are common, 4 are distinctive, and 6 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈4
Distinctive · estimate
This author's own choices. Worth a look.
6
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 49
Never select QUERY_SAMPLE_TEXT, pg_stats.most_common_vals/histogram_bounds or SHOW ENGINE INNODB STATUS: they carry real values.
- Distinctive: line 50
Respect the timeouts: If a query hits the 5 s timeout, record "not checked: timed out" and move on.
- Very specific: line 54
Connect: Ask which environment variable or secret holds the connection string for the audit role.
- Very specific: line 55
Guard: Run queries/pg/00-session.sql or queries/mysql/00-session.sql and apply the stop rules from Safety.
- Very specific: line 56
Context: Run 01-version-settings.sql and record:
- Distinctive: line 62
Collect: Run the remaining files in order:
- Very specific: line 66
If pg_stat_statements is not installed, or performance_schema is OFF, skip that file and report the gap as a finding (pg-observability, or …
- Very specific: line 93
Cross-check usage findings: Before you propose dropping an index or calling a table "scanned", ask the user to run 04/07 (04/06 on MySQL) …
- Distinctive: line 114
Start with a header:
- Distinctive: line 136
After the table, give each migration in full (file name, up, down, the lock it takes, expected duration) and the operator steps that are …
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Database audit
Purpose
The user gets a short report of what costs the most on their database right now, ordered by impact, where each finding carries its evidence (numbers and the statistics window they cover), the decision skill that explains the fix, a migration with its rollback, and the query that proves the fix worked. Nothing is changed: the user reviews the migrations and ships them through their normal pipeline.
Safety
Read-only is enforced by the database role, not by you and not by an MCP server. Client-side read-only modes keep failing. AWS's own MCP servers shipped two read-only bypasses in September 2026:
- CVE-2026-87911 / GHSA-fph8-pg5w-78fv (critical): in
awslabs.postgres-mcp-serverbefore 1.1.7, read-only mode letCOPY ... TO PROGRAMthrough. It ran OS commands when the role had superuser orpg_execute_server_program. Roles with minimal privileges were not affected. - CVE-2026-85788 / GHSA-x25m-ph3m-3r9q: in
awslabs.mysql-mcp-serverup to 1.0.21, SQL comments placed between keywords got past the read-only check. The impact depended on the account holdingFILE.
AWS's advisory calls read-only mode a best-effort safeguard and not a hard security boundary, and says the database role's permissions are the effective limit. Its Postgres MCP server docs describe the SQL guard as best-effort, defense in depth. So:
- The user creates the role, not you. Give them setup/pg-audit-role.sql or setup/mysql-audit-role.sql to run as an administrator. Never run them yourself and never ask for admin credentials. The role setup is:
- PostgreSQL:
pg_monitor(settings and statistics, no table data), plusdefault_transaction_read_only,statement_timeout,lock_timeoutandidle_in_transaction_session_timeoutset on the role.pg_read_all_datais optional and only with the user's consent. Neverpg_write_all_data,pg_maintain,pg_signal_backend, or the server file and program roles: predefined roles warns these can grant superuser-level access. - MySQL:
PROCESS,SELECTonperformance_schema, andSELECT, EXECUTEonsys(sys schema prerequisites). Each session then setstransaction_read_only,max_execution_timeandlock_wait_timeout. NeverFILEorSUPER.
- PostgreSQL:
Check the session before anything else with
queries/<engine>/00-session.sql. Stop and point the user to the setup file if:- the role is a superuser,
- any risky role or privilege row comes back,
writable_tables> 0,- or
transaction_read_onlyis off.
Do not "continue carefully". A role with
CREATEon a schema or database is a warning, not a stop, because the read-only transaction blocks DDL.default_transaction_read_onlyis only a default that a session can turn off (client settings). That is why the absence of write grants is the real boundary.- Prefer a read replica. Confirm it with
on_replica(Postgrespg_is_in_recovery()) orread_only/super_read_only(MySQL). Statistics are per instance, so usage-based findings must be checked on every instance (see Steps). Run only the files in
queries/, as written, one session, in order. Never run any of these:- writes or DDL;
- maintenance:
VACUUM,ANALYZE,REINDEX,OPTIMIZE TABLE,ANALYZE TABLE; - server settings:
SET GLOBAL; - extension installs:
CREATE EXTENSION; - session control:
pg_cancel_backend,pg_terminate_backend,KILL, or thesql_kill_*text the sys views offer; - statistics resets:
pg_stat_statements_reset(),pg_stat_reset(),FLUSH. A reset destroys the user's history.
EXPLAIN ANALYZEbelongs to/db-skills:db-explain, with consent. When a finding needs one of these, it becomes a step in the report for the user.- Redact before you show.
- pg_stat_statements replaces constants with
$1, but its docs warn that texts can show literal values when many entries are being deallocated. Reportpg_stat_statements_info.dealloc. pg_stat_activity.queryand MySQLprocesslist.INFOare raw.- The query files replace quoted strings and numbers with
?. Before you print any text, still read it for emails, tokens or names in identifiers and comments, and cut them. - Never select
QUERY_SAMPLE_TEXT,pg_stats.most_common_vals/histogram_boundsorSHOW ENGINE INNODB STATUS: they carry real values.
- pg_stat_statements replaces constants with
- Respect the timeouts. If a query hits the 5 s timeout, record "not checked: timed out" and move on. Do not raise a timeout without asking.
Steps
- Connect. Ask which environment variable or secret holds the connection string for the audit role. Never ask the user to paste a password into the chat. Detect the engine from the URL or from
SELECT version(). - Guard. Run
queries/pg/00-session.sqlorqueries/mysql/00-session.sqland apply the stop rules from Safety. Context. Run
01-version-settings.sqland record:- the version (
server_version_numorVERSION()) and whether this is a replica; - the statistics window: time since
stats_reset, server start, orUptime(MySQL); - the extensions present.
Mark every usage-based finding (top statements, unused indexes, sequential scans, cache hit) provisional when the window is under 7 days. Never propose dropping an index on a window shorter than one full business cycle, at least 30 days including month-end jobs. Both thresholds are this skill's, not an official number.
- the version (
Collect. Run the remaining files in order:
- Postgres:
02to11, plus12-vacuum-time-pg18.sqlonly whenserver_version_num>= 180000. - MySQL:
02to10.
If
pg_stat_statementsis not installed, orperformance_schemais OFF, skip that file and report the gap as a finding (pg-observability, or the MySQL observability skill).- Postgres:
Interpret with the table below. The thresholds are this skill's triage defaults; the fix comes from the decision skill named. MySQL decision skills arrive in v1.1: until then, name the topic ("MySQL indexing skill", "MySQL online DDL skill") and cite the MySQL manual page.
Check PG file MySQL file Flag when Severity Decision skill Top statements 02 02 one statement ≥ 20% of total time, or rows examined per row sent ≥ 100 HIGH pg-query-patterns, pg-indexing; hand the statement to /db-skills:db-explainCache / buffer pool hit 03 03 below 95% on an OLTP workload, after a window of at least a day MEDIUM pg-config-by-size Unused indexes 04 04 0 scans over a window ≥ 30 days on every instance MEDIUM above 1 GB, else LOW pg-indexing Duplicate or prefix-redundant 05 05 any MEDIUM above 1 GB, else LOW pg-indexing Invalid index 05 n/a any MEDIUM pg-online-migrations FK without index 06 n/a any, when parent rows are deleted or their keys updated HIGH if the child is large, else MEDIUM pg-indexing Heavy sequential / full scans 07 06 large table with avg rows per scan near the table size, many times a day HIGH or MEDIUM pg-indexing, pg-query-patterns Dead tuples, autovacuum lag 08 n/a n_dead_tupabovevacuum_triggerwith an old or emptylast_autovacuum;dead_pct≥ 20%MEDIUM pg-vacuum-and-bloat XID age 08 n/a pct_of_freeze_max_age≥ 100 is HIGH;xid_age≥ 1.5 billion is CRITICALHIGH / CRITICAL pg-vacuum-and-bloat History list length n/a 07 growing with an old transaction open HIGH MySQL transactions skill Long or idle-in-transaction sessions 09 08 idle in transaction ≥ 5 min, or a session holding the oldest xmin HIGH pg-transactions-and-locking Replication slots 09 n/a inactive slot retaining WAL or holding xmin CRITICAL if it is growing pg-vacuum-and-bloat Lock waits 10 09 any blocking chain; a DDL waiting on a metadata lock CRITICAL for a chain, else HIGH pg-transactions-and-locking, pg-online-migrations Connections 11 10 ≥ 90% of max_connectionsis CRITICAL, ≥ 80% is HIGHCRITICAL / HIGH pg-connections; run /db-skills:db-connection-budgetSettings 01 01 no statement_timeout/idle_in_transaction_session_timeoutfor app roles,track_io_timingoff, defaults on a large instanceLOW pg-config-by-size, pg-observability Facts behind the checks, from official docs:
- Postgres does not index foreign-key referencing columns, so deleting or updating a parent row scans the child table (constraints).
- InnoDB creates an index for each foreign key itself, so MySQL has no missing-FK check (FOREIGN KEY constraints).
- Postgres forces an anti-wraparound vacuum at
autovacuum_freeze_max_age, and the server stops assigning XIDs as unfrozen age nears 2^31 (routine vacuuming). - Open read views block purge and grow the history list (purge configuration).
sys.schema_unused_indexescounts from server start, and every sys index view reads only tables the account has a privilege on (sys views). With noSELECTon the app schema, report "index checks: app schema not visible".
- Cross-check usage findings. Before you propose dropping an index or calling a table "scanned", ask the user to run
04/07(04/06on MySQL) on each replica with the same role. A replica serves its own reads and keeps its own counters. - Write the fixes as migrations.
- Match the format. Detect the repo's migration tool (Rails, Django, Alembic, Flyway, Liquibase, Prisma, golang-migrate, Knex, plain SQL) and use its file format. Show the files in the report, and create them only if the user asks. Never run them.
Postgres, new index:
SET lock_timeout = '5s'; CREATE INDEX CONCURRENTLY IF NOT EXISTS ... ;CONCURRENTLYcannot run inside a transaction, so turn off the tool's per-migration transaction. Rollback:DROP INDEX CONCURRENTLY IF EXISTS .... A failed build leaves an INVALID index (CREATE INDEX).- Postgres, drop an index:
DROP INDEX CONCURRENTLY. Rollback: the definition frompg_get_indexdefwithCONCURRENTLYinserted,CREATE INDEX CONCURRENTLY name ON ..., becausepg_get_indexdefreturns a plainCREATE INDEXthat blocks writes while it builds. Write it that way in comments too: someone will copy the rollback onto the live database. Note how long the rebuild takes. - MySQL, new index:
ALTER TABLE t ADD INDEX ix (...), ALGORITHM=INPLACE, LOCK=NONEafterSET SESSION lock_wait_timeout = 5. Rollback:ALTER TABLE t DROP INDEX ix. Asking for an algorithm or lock level the server cannot honor makes it fail instead of blocking writes (online DDL). - MySQL, drop an index: first
ALTER TABLE t ALTER INDEX ix INVISIBLE(rollback:VISIBLE). Ship theDROP INDEXas a later migration, after a business cycle with no regressions (invisible indexes). - Postgres, per-table autovacuum or role timeouts: for example
ALTER TABLE ... SET (autovacuum_vacuum_scale_factor = ...)orALTER ROLE app SET idle_in_transaction_session_timeout = .... Rollback withRESET (...)orALTER ROLE app RESET .... Take the values from the decision skill. - Server parameters (
shared_buffers,max_connections,innodb_buffer_pool_size) are not migrations. List them as parameter-group or config changes, and say whether they need a restart. - One-off maintenance the user may choose to run, such as
ANALYZEof a table with stale statistics or dropping an inactive slot: list it as an operator step with its lock and I/O cost. Never run it.
- Report in the format below. Write it to a Markdown file in the repo only if the user asks.
Output
Start with a header:
Database audit: <engine> <version> on <host alias, never the full URL>, <primary|replica>
Stats window: <since> (<n> days) pg_stat_statements dealloc: <n> Checked replicas: <list or "none">
Not checked: <files skipped and why: extension missing, timed out, schema not visible>Then one table, ordered by severity, then by estimated impact (share of DB time, size, rows):
| # | Severity | Finding | Evidence | Related skill | Proposed migration (rollback) | How to verify |
| F1 | HIGH | orders has no index for FK orders_customer_id_fkey | 8.4 GB table, customers deletes 1.2k/day, top statement 31% of DB time is DELETE FROM customers WHERE id = $1 | pg-indexing | 20261018_orders_customer_id_idx.sql: CREATE INDEX CONCURRENTLY orders_customer_id_idx ON orders (customer_id) (rollback DROP INDEX CONCURRENTLY orders_customer_id_idx) | indisvalid true; plain EXPLAIN of the FK check shows an Index Scan; mean_exec_time of the delete drops after 24 h |
The row above is an example of the format.
Severity:
- CRITICAL: risk of an outage soon, such as wraparound, connection exhaustion, a blocking chain, or a slot filling the disk.
- HIGH: a large share of DB time or lock time with a clear fix.
- MEDIUM: waste with a measurable cost, such as large unused or duplicate indexes, low hit ratio, or vacuum lag.
- LOW: hygiene and configuration drift.
After the table, give each migration in full (file name, up, down, the lock it takes, expected duration) and the operator steps that are not migrations. Query texts appear only redacted. End with what the user should rerun to confirm, and when.
When not to use
- One query is slow and the user knows which one: use
/db-skills:db-explain. - The question is how many connections the fleet may open: use
/db-skills:db-connection-budget. - The only credentials available are a superuser, master or owner account, and the user will not create the audit role: do not connect. Hand over the setup file and stop.
- Statistics were just reset or the server just restarted: usage counters are empty. Run only the structural checks (
00,01,05,06,08,11) and suggest a full audit after a business cycle. - Reviewing a schema or migration that has not been deployed yet: use the decision skills (pg-indexing, pg-online-migrations, pg-keys-and-types) on the code instead.
- Engines other than PostgreSQL 15+ and MySQL 8.4+ (MariaDB, older majors): the queries are not tested there.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

