Skip to main content
Glama
steveramos21

AI-DBA

by steveramos21
README.md
# AI-DBA

Universal database copilot — diagnostics, operations, and performance analysis via MCP and CLI.

## Features

- **MCP Server** — expose database diagnostics as tools for AI agents (Hermes, Claude Code, etc.)
- **CLI** — one-off commands for scripting and automation
- **Interactive REPL** — explore your databases interactively with standard DBA commands
- **Connection URLs** — connect via `mysql://`, `postgresql://`, `sqlserver://`, `oracle://`, or `mongodb://` URLs (no config file needed)
- **Database-agnostic commands** — `databases`, `tables`, `describe`, `indexes`, `processes` work across engines
- **Multi-engine support** — MySQL, PostgreSQL, SQL Server, Oracle, and MongoDB connectors
- **MySQL blocking chains** — detect and report row-level blocking with full query details
- **Table sizes** — list table/collection sizes with human-readable formatting (`table-sizes`)
- **Explain plans** — execution plan analysis with optional `--analyze` flag (PostgreSQL executes the query)
- **Slow queries** — surface slow query data from engine internals (performance_schema, pg_stat_statements, sys.dm_exec_query_stats, V$SQLAREA, currentOp)
- **Health checks** — orchestrate connectivity, blocking, processes, and slow queries into one status call
- **SQL guard** — shared validation module rejects destructive SQL before it reaches connectors
- **GitHub Actions CI** — build + test on Node 20/22, runs on every push/PR to main
- **Documentation site** — MkDocs Material with 8 pages, light/dark mode, search

## Documentation

Full docs site live at: https://steveramos21.github.io/ai-dba/

To build docs locally:
```bash
python3 -m venv .venv
.venv/bin/pip install -r requirements-docs.txt
.venv/bin/mkdocs serve
# Open http://127.0.0.1:8000
```

## Quick Start

### 1. Install dependencies

```bash
npm install
```

### 2. Build

```bash
npm run build
```

### 3. Connect and explore

Option A — **Config file** (for multiple engines):

```bash
cp config.yaml.example config.yaml
# Edit config.yaml with your database credentials
npm run repl
```

Option B — **Connection URL** (no config file needed):

```bash
node dist/index.js repl
# Then: connect mysql://root:password@127.0.0.1:3306/mydb
```

Option C — **Connect via URL** (drops into REPL):

```bash
# MySQL
npm run connect -- 'mysql://root:***@127.0.0.1:3306/mydb'

# PostgreSQL
npm run connect -- 'postgresql://postgres:***@127.0.0.1:5432/mydb'
# Connects and opens interactive REPL
```

## Commands

| Command | Description |
|---------|-------------|
| `serve` | Start MCP server over stdio (for AI agents) |
| `list-engines` | List configured database engines |
| `blocking-chains <engineId>` | Show current blocking chains |
| `table-sizes <engineId> [database]` | List table sizes with human-readable formatting |
| `explain <engineId> <query> [-a]` | Show execution plan (add `-a` for ANALYZE) |
| `slow-queries <engineId> [--limit N] [--min-duration-ms N]` | List slow queries from engine internals |
| `health-check <engineId>` | Run health check (connectivity, blocking, processes, slow queries) |
| `connect <url>` | Connect to a database via URL |
| `repl` | Interactive REPL for database diagnostics |

### Global Options

| Option | Description | Default |
|--------|-------------|---------|
| `-c, --config <path>` | Path to config.yaml | `config.yaml` |
| `-V, --version` | Show version | — |
| `-h, --help` | Show help | — |

### `connect <url>`

Connect to a database via URL and open an interactive REPL. No config file needed. Supports `mysql://`, `postgresql://`, and `postgres://` URL schemes.

```bash
# MySQL
npm run connect -- 'mysql://root:***@127.0.0.1:3306/mydb'

# PostgreSQL
npm run connect -- 'postgresql://postgres:***@127.0.0.1:5432/mydb'

# With --type override
npm run connect -- 'postgresql://user:***@host:5432/db' --type postgres
```

This connects, verifies the connection, then drops you into the REPL where you can run `databases`, `tables`, `describe`, `indexes`, `processes`, etc.

### `blocking-chains`

```bash
ai-dba blocking-chains <engineId>       # Table output
ai-dba blocking-chains <engineId> --json # JSON output
```

Detects blocking chains across MySQL and PostgreSQL. Returns a list of blocking chain entries:

| Field | Description |
|-------|-------------|
| `engine_id` | Engine identifier from config |
| `blocking_pid` | Process ID holding the lock |
| `blocked_pid` | Process ID waiting for the lock |
| `wait_duration_ms` | How long the blocked session has been waiting |
| `wait_event` | Wait event name (engine-specific) |
| `blocking_query` | SQL statement holding the lock |
| `blocked_query` | SQL statement waiting for the lock |
| `database_name` | Database context |
| `wait_type` | Wait type classification |
| `status` | Session status |
| `host_name` | Client host address |
| `program_name` | Client program name |
| `login_time` | Session login timestamp |

**MySQL** uses `INNODB_LOCK_WAITS` + `INNODB_TRX` + `performance_schema.threads` (4-join query). `wait_type` and `program_name` are NULL for MySQL (not exposed in the query).

**PostgreSQL** uses `pg_blocking_pids()` + `pg_stat_activity` with `query_start` for wait duration. All 12 fields are populated natively.

**Error cases:**

- Unknown engine ID → `Unknown engine "x". Available: mysql-primary`
- Unsupported engine type → error from the connector's `getBlockingChains()` method

### `repl`

```bash
ai-dba repl                # Uses config.yaml
ai-dba repl                # No config — start empty, use connect
```

Interactive commands:

| Command | Alias | Description |
|---------|-------|-------------|
| `help` | | Show available commands |
| `connect <url>` | | Connect to a database via URL |
| `databases` | `db` | List databases on the server |
| `tables` | `dt` | List tables (with rows, size, engine) |
| `describe <table>` | `desc` | Show column details (type, nullable, key, default) |
| `indexes <table>` | `idx` | List indexes on a table |
| `processes` | `ps` | Show active connections/processes |
| `engines` | `ls` | List configured engines (current marked with *) |
| `use <engineId>` | | Switch to a different engine |
| `status` | `s` | Show connection details for current engine |
| `blocking-chains` | `bc` | Show blocking chains on current engine |
| `sql <statement>` | | Run a raw SQL query (escape hatch) |
| `quit` | `q`, `exit` | Exit the REPL |

SQL keywords are auto-detected — just type `SHOW DATABASES` or `SELECT * FROM users` directly.

**Example session:**

```
AI-DBA REPL — type 'help' for commands
No engines configured. Use: connect <url>

ai-dba[no-engine]> connect mysql://root:password@127.0.0.1:13306/testdb
Connected to 127.0.0.1-testdb
  mysql://root:***@127.0.0.1:13306/testdb

ai-dba[127.0.0.1-testdb]> databases
┌────────────────────┐
│ Database           │
├────────────────────┤
│ information_schema │
│ testdb             │
└────────────────────┘
2 database(s)

ai-dba[127.0.0.1-testdb]> tables
┌────────────────┬──────┬─────────┬─────────┬─────────────────────┐
│ Table          │ Rows  │ Size    │ Engine  │ Collation           │
├────────────────┼──────┼─────────┼─────────┼─────────────────────┤
│ blocking_test  │ 3     │ 16.0 KB │ InnoDB  │ utf8mb4_0900_ai_ci  │
└────────────────┴──────┴─────────┴─────────┴─────────────────────┘
1 table(s)

ai-dba[127.0.0.1-testdb]> describe blocking_test
┌─────────┬──────────────┬──────┬─────┬─────────┬────────────────┐
│ Column  │ Type         │ Null │ Key │ Default  │ Extra          │
├─────────┼──────────────┼──────┼─────┼─────────┼────────────────┤
│ id      │ int          │ NO   │ PRI │ NULL     │ auto_increment │
│ name    │ varchar(100) │ YES  │     │ NULL     │                │
│ value   │ int          │ YES  │     │ NULL     │                │
└─────────┴──────────────┴──────┴─────┴─────────┴────────────────┘

ai-dba[127.0.0.1-testdb]> processes
┌──────┬──────┬──────────────────────┬─────────┬─────────┬──────┬─────────────┐
│ PID  │ User │ Host                 │ DB      │ Command │ Time │ State       │
├──────┼──────┼──────────────────────┼─────────┼─────────┼──────┼─────────────┤
│ 5    │ root │ 172.23.0.1:54312     │ testdb  │ Query   │ 0s   │ starting    │
└──────┴──────┴──────────────────────┴─────────┴─────────┴──────┴─────────────┘
1 process(es)
```

### `serve`

Starts an MCP server over stdio. Used by AI agents to call database diagnostics tools.

**MCP configuration** (e.g., `~/.hermes/config.yaml`):

```json
{
  "mcpServers": {
    "ai-dba-diagnostics": {
      "command": "node",
      "args": ["/path/to/ai-dba/dist/index.js", "serve", "--config", "/path/to/ai-dba/config.yaml"]
    }
  }
}
```

The server exposes six tools:

| Tool | Parameters | Description |
|------|-----------|-------------|
| `blocking-chains` | `engineId` (string, required) | Show current blocking chains |
| `databases` | `engineId` (string, required) | List databases/schemas on the server |
| `tables` | `engineId` (string, required), `database` (string, optional) | List tables in a database/schema |
| `describe-table` | `engineId` (string, required), `table` (string, required), `database` (string, optional) | Show column metadata for a table |
| `indexes` | `engineId` (string, required), `table` (string, required), `database` (string, optional) | List indexes on a table |
| `processes` | `engineId` (string, required) | List active database connections/processes |
| `table-sizes` | `engineId` (string, required), `database` (string, optional) | List table sizes with data/index/total breakdown |
| `explain` | `engineId` (string, required), `query` (string, required), `analyze` (boolean, optional) | Show execution plan for a query |
| `slow-queries` | `engineId` (string, required), `limit` (int, optional), `minDurationMs` (int, optional) | List slow queries from engine internals |
| `health-check` | `engineId` (string, required) | Run health check (connectivity, blocking, processes, slow queries) |

All tools return JSON. The `database` parameter overrides the engine's configured database (MySQL) or schema (PostgreSQL).

## Docker Test Environment

A Docker Compose file is included for local testing with MySQL 8.0 and PostgreSQL 16.

### Start databases

```bash
docker compose up -d
```

Wait until healthy:

```bash
docker inspect --format='{{.State.Health.Status}}' ai-dba-mysql-test
docker inspect --format='{{.State.Health.Status}}' ai-dba-postgres-test
# Repeat until both show "healthy"
```

### Seed MySQL test data

```bash
docker exec ai-dba-mysql-test mysql -uroot -ptestpassword testdb \
  -e "CREATE TABLE IF NOT EXISTS blocking_test (id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100), value INT); INSERT IGNORE INTO blocking_test (name, value) VALUES ('alpha', 1), ('beta', 2), ('gamma', 3);"
```

### Create a test config

```bash
cp config.yaml.example config.yaml
```

The example config points to both Docker MySQL on port 13306 and PostgreSQL on port 15432.

### Test blocking detection

MySQL blocking scenario:

```bash
node test/test-blocking.mjs
```

PostgreSQL blocking scenario:

```bash
# Seed test data first
docker exec ai-dba-postgres-test psql -U postgres -d testdb \
  -c "CREATE TABLE IF NOT EXISTS blocking_test (id SERIAL PRIMARY KEY, value INT); INSERT INTO blocking_test (value) VALUES (1) ON CONFLICT DO NOTHING;"

# Run the test
node test/test-blocking-postgres.mjs
```

### Stop databases

```bash
docker compose down
```

Add `-v` to also delete the data volume.

### Integration tests (require Docker)

```bash
# All connector methods against live MySQL + PostgreSQL (49 tests)
npm run test:integration

# Live blocking scenarios — creates real locks, validates detection (21 tests)
npm run test:blocking
```

These tests catch bugs that mocked unit tests cannot — they exercise real SQL against MySQL 8.0 and PostgreSQL 16.

## Configuration

`config.yaml` format:

### Connection URL (recommended)

```yaml
engines:
  mysql-prod:
    type: mysql
    url: mysql://readonly:***@prod-db.internal:3306/app_db?ssl=true

  postgres-prod:
    type: postgres
    url: postgresql://readonly:***@prod-db.internal:5432/app_db?sslmode=require
```

The `url` field takes priority over individual fields. MySQL supports additional URL params:
- `ssl=true` — enable SSL with certificate verification
- `ssl={"rejectUnauthorized":false}` — custom SSL options (JSON)
- `connectionLimit=10` — pool size (default: 5)

PostgreSQL URLs are passed directly to `pg.Pool({ connectionString })`, so any `pg`-supported parameter works (`sslmode`, `connect_timeout`, etc.).

### Individual fields (legacy)

```yaml
engines:
  <engine-id>:
    type: mysql          # or postgres, sqlserver, oracle, mongodb
    host: 127.0.0.1      # Hostname or IP (MySQL only)
    port: 3306            # Port (MySQL only)
    user: root            # Database user (MySQL only)
    password: secret      # Password (MySQL only)
    database: mydb        # Default database (MySQL only)
```

Note: Individual fields are only supported for MySQL. PostgreSQL requires a connection URL (`url` field).

Multiple engines are supported:

```yaml
engines:
  mysql-prod:
    type: mysql
    url: mysql://readonly:***@prod-db.internal:3306/app_db?ssl=true
  mysql-staging:
    type: mysql
    host: staging-db.internal
    port: 3306
    user: readonly
    password: ${MYSQL_STAGING_PASSWORD}
    database: app_db
  postgres-analytics:
    type: postgres
    url: postgresql://readonly:***@analytics-db.internal:5432/warehouse?sslmode=require
```

**Security:** Add `config.yaml` to `.gitignore` (already included by default).

## Architecture

```
src/
  index.ts              CLI entry point (commander, REPL)
  server.ts             MCP server setup + connector map
  config.ts             YAML config loader with URL parsing
  connector.ts          DatabaseConnector interface + shared types (BlockingChain, TableSizeInfo, ExplainResult, SlowQueryInfo, HealthCheckResult)
  sql-guard.ts          Shared SQL validation (validateReadOnlySql, validateExplainQuery, isJsonCommand)
  connectors/
    mysql.ts            MySQLConnector (implements DatabaseConnector)
    postgres.ts         PostgreSQLConnector (implements DatabaseConnector)
    sqlserver.ts        SqlServerConnector (implements DatabaseConnector)
    oracle.ts           OracleConnector (implements DatabaseConnector)
    mongodb.ts          MongoDbConnector (implements DatabaseConnector)
  tools/
    blocking-chains.ts  MCP tool — blocking chain diagnostics
    databases.ts        MCP tool — list databases/schemas
    tables.ts           MCP tool — list tables
    describe-table.ts   MCP tool — column metadata
    indexes.ts          MCP tool — list indexes
    processes.ts        MCP tool — active processes/connections
    table-sizes.ts      MCP tool — table size breakdown
    explain.ts          MCP tool — execution plans
    slow-queries.ts     MCP tool — slow query analysis
    health-check.ts     MCP tool — orchestrated health check
```

- **DatabaseConnector interface** — 10 methods: `listDatabases`, `listTables`, `describeTable`, `listIndexes`, `listProcesses`, `query`, `getBlockingChains`, `listTableSizes`, `explainQuery`, `listSlowQueries`. All 5 engines implement the interface.
- **sql-guard.ts** — shared validation module used by CLI, REPL, and MCP tool paths. Rejects destructive SQL (INSERT, UPDATE, DELETE, DROP, TRUNCATE, ALTER, CREATE, MERGE, GRANT, REVOKE) using `\b` word-boundary regex.
- **Lazy imports** — MCP SDK, mysql2, and pg are loaded dynamically only when needed. CLI commands like `list-engines` start instantly without loading database drivers.
- **Lazy connection pools** — Database connections are created on first use, not at startup.
- **One tool per file** — each `src/tools/*.ts` file is self-contained (schema + handler). Adding a new tool means adding a new file and registering it in `server.ts`.
- **Graceful degradation** — `slow-queries` and `explain` return empty results when engine features are unavailable (extension not installed, permission denied) rather than throwing errors.
- **GitHub Actions CI** — `.github/workflows/ci.yml` runs build + unit tests on Node 20/22 for every push/PR to main.

## Requirements

- Node.js 18+
- MySQL 8.0+ (with `performance_schema` enabled, which is the default)
- PostgreSQL 12+ (for PostgreSQL connector)

## License

MIT