mpx-db 1.1.3 โ†’ 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,293 +1,167 @@
1
- # mpx-db
1
+ # mpx-db ๐Ÿ—„๏ธ
2
2
 
3
- **Database management CLI** โ€” Connect, query, migrate, and manage databases from the terminal.
3
+ **Database management CLI โ€” connect, query, migrate, and manage databases from the terminal.**
4
4
 
5
- Stop juggling multiple database tools. `mpx-db` gives you one clean interface for SQLite, PostgreSQL, and MySQL.
5
+ Stop juggling multiple database tools. One clean interface for SQLite, PostgreSQL, and MySQL.
6
+
7
+ Part of the [Mesaplex](https://mesaplex.com) developer toolchain.
8
+
9
+ [![npm version](https://img.shields.io/npm/v/mpx-db.svg)](https://www.npmjs.com/package/mpx-db)
10
+ [![License: Dual](https://img.shields.io/badge/license-Dual-blue.svg)](LICENSE)
11
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D18-brightgreen.svg)](https://nodejs.org)
6
12
 
7
13
  ## Features
8
14
 
9
- โœ… **Multi-database support** โ€” SQLite, PostgreSQL, MySQL
10
- โœ… **Beautiful output** โ€” Colored tables, not raw dumps
11
- โœ… **Connection management** โ€” Save connections, no more copy-pasting URLs
12
- โœ… **Migration system** โ€” Git-friendly SQL migration files
13
- โœ… **Schema operations** โ€” Dump, describe, visualize database structure
14
- โœ… **Data export** โ€” Export to JSON/CSV with one command
15
- โœ… **Secure** โ€” Encrypted credential storage
16
- โœ… **AI-native** โ€” JSON output, MCP server, schema discovery for AI agents
15
+ - **Multi-database support** โ€” SQLite, PostgreSQL, MySQL
16
+ - **Beautiful output** โ€” Colored tables, not raw dumps
17
+ - **Connection management** โ€” Save connections, no more copy-pasting URLs
18
+ - **Migration system** โ€” Git-friendly SQL migration files
19
+ - **Schema operations** โ€” Dump, describe, visualize database structure
20
+ - **Data export** โ€” Export to JSON/CSV with one command
21
+ - **Secure** โ€” Encrypted credential storage (AES-256-GCM)
22
+ - **MCP server** โ€” Integrates with any MCP-compatible AI agent
23
+ - **Self-documenting** โ€” `--schema` returns machine-readable tool description
17
24
 
18
25
  ## Installation
19
26
 
20
27
  ```bash
21
28
  npm install -g mpx-db
29
+ ```
30
+
31
+ Install database drivers you need (optional peer dependencies):
22
32
 
23
- # Install database drivers you need (optional peer deps)
33
+ ```bash
24
34
  npm install -g better-sqlite3 # For SQLite
25
35
  npm install -g pg # For PostgreSQL
26
36
  npm install -g mysql2 # For MySQL
27
37
  ```
28
38
 
29
- ## Quick Start
39
+ Or run directly with npx:
30
40
 
31
41
  ```bash
32
- # Connect to a database
33
- mpx-db connect sqlite://./mydb.db
34
-
35
- # Save a connection for reuse
36
- mpx-db connect --save dev sqlite://./dev.db
37
- mpx-db connect --save prod postgres://user:pass@localhost:5432/mydb
42
+ npx mpx-db --help
43
+ ```
38
44
 
39
- # List saved connections
40
- mpx-db connections list
45
+ **Requirements:** Node.js 18+ ยท macOS, Linux, Windows
41
46
 
42
- # Query a database
43
- mpx-db query dev "SELECT * FROM users LIMIT 10"
47
+ ## Quick Start
44
48
 
45
- # Show database info
46
- mpx-db info dev
49
+ ```bash
50
+ # Connect and save
51
+ mpx-db connect --save dev sqlite://./dev.db
47
52
 
48
- # List all tables
53
+ # List tables
49
54
  mpx-db tables dev
50
55
 
56
+ # Run a query
57
+ mpx-db query dev "SELECT * FROM users LIMIT 10"
58
+
51
59
  # Describe a table
52
60
  mpx-db describe dev users
53
- ```
54
-
55
- ## Connection Strings
56
-
57
- ```bash
58
- # SQLite (file-based)
59
- sqlite://./database.db
60
- sqlite:///absolute/path/to/db.sqlite
61
-
62
- # PostgreSQL
63
- postgres://user:password@localhost:5432/database
64
- postgresql://user:password@host:5432/db
65
61
 
66
- # MySQL
67
- mysql://user:password@localhost:3306/database
62
+ # Export data
63
+ mpx-db export dev users --format json
68
64
  ```
69
65
 
70
- ## Commands
66
+ ## Usage
71
67
 
72
68
  ### Connection Management
73
69
 
74
70
  ```bash
75
- # Test and save a connection
76
- mpx-db connect --save <name> <url>
71
+ mpx-db connect --save <name> <url> # Save a connection
72
+ mpx-db connections list # List saved connections
73
+ mpx-db connections remove <name> # Remove a connection
74
+ ```
77
75
 
78
- # List saved connections
79
- mpx-db connections list
76
+ Connection string formats:
80
77
 
81
- # Remove a saved connection
82
- mpx-db connections remove <name>
78
+ ```
79
+ sqlite://./database.db
80
+ postgres://user:password@localhost:5432/database
81
+ mysql://user:password@localhost:3306/database
83
82
  ```
84
83
 
85
84
  ### Querying
86
85
 
87
86
  ```bash
88
- # Run a query
89
87
  mpx-db query <connection> "SELECT * FROM users WHERE active = 1"
90
-
91
- # Query with saved connection
92
- mpx-db query dev "SELECT COUNT(*) FROM orders"
93
88
  ```
94
89
 
95
90
  ### Schema Operations
96
91
 
97
92
  ```bash
98
- # Show database information
99
- mpx-db info <connection>
100
-
101
- # List all tables with row counts
102
- mpx-db tables <connection>
103
-
104
- # Describe table structure
105
- mpx-db describe <connection> <table>
106
-
107
- # Dump entire schema as SQL
108
- mpx-db schema dump <connection>
93
+ mpx-db info <connection> # Database information
94
+ mpx-db tables <connection> # List tables with row counts
95
+ mpx-db describe <connection> <table> # Table structure
96
+ mpx-db schema dump <connection> # Dump schema as SQL
109
97
  ```
110
98
 
111
99
  ### Migrations
112
100
 
113
101
  ```bash
114
- # Initialize migrations directory
115
- mpx-db migrate init
116
-
117
- # Create a new migration
118
- mpx-db migrate create add_users_table
119
-
120
- # Show migration status
121
- mpx-db migrate status <connection>
122
-
123
- # Run pending migrations
124
- mpx-db migrate up <connection>
125
-
126
- # Rollback last migration
127
- mpx-db migrate down <connection>
102
+ mpx-db migrate init # Initialize migrations directory
103
+ mpx-db migrate create <name> # Create a new migration
104
+ mpx-db migrate status <connection> # Show migration status
105
+ mpx-db migrate up <connection> # Run pending migrations
106
+ mpx-db migrate down <connection> # Rollback last migration
128
107
  ```
129
108
 
130
- #### Migration File Format
131
-
132
- Migrations are SQL files in `./migrations/` directory:
109
+ Migration file format (`./migrations/`):
133
110
 
134
111
  ```sql
135
- -- Migration: add_users_table
136
- -- Created: 2026-02-15T10:30:00.000Z
137
-
138
112
  -- Up migration
139
113
  CREATE TABLE users (
140
114
  id INTEGER PRIMARY KEY AUTOINCREMENT,
141
115
  email TEXT UNIQUE NOT NULL,
142
- name TEXT NOT NULL,
143
- created_at TEXT DEFAULT CURRENT_TIMESTAMP
116
+ name TEXT NOT NULL
144
117
  );
145
118
 
146
- CREATE INDEX idx_users_email ON users(email);
147
-
148
- -- Down migration (rollback)
149
119
  -- DOWN
150
- DROP INDEX idx_users_email;
151
120
  DROP TABLE users;
152
121
  ```
153
122
 
154
- ### Data Operations
123
+ ### Data Export
155
124
 
156
125
  ```bash
157
- # Export table data to JSON
158
- mpx-db export dev users --format json
159
-
160
- # Export to CSV
161
- mpx-db export dev users --format csv --output data.csv
162
- ```
163
-
164
- ## Examples
165
-
166
- ### Setting Up a New Project
167
-
168
- ```bash
169
- # Initialize migrations
170
- mpx-db migrate init
171
-
172
- # Create your first migration
173
- mpx-db migrate create create_initial_schema
174
-
175
- # Edit migrations/YYYYMMDD_HHMMSS_create_initial_schema.sql
176
- # Add your CREATE TABLE statements
177
-
178
- # Connect to database
179
- mpx-db connect --save dev sqlite://./dev.db
180
-
181
- # Run migrations
182
- mpx-db migrate up dev
183
-
184
- # Verify
185
- mpx-db tables dev
186
- mpx-db info dev
187
- ```
188
-
189
- ### Managing Multiple Environments
190
-
191
- ```bash
192
- # Save connections for each environment
193
- mpx-db connect --save dev sqlite://./dev.db
194
- mpx-db connect --save staging postgres://user:pass@staging-host/mydb
195
- mpx-db connect --save prod postgres://user:pass@prod-host/mydb
196
-
197
- # Run migrations on each
198
- mpx-db migrate up dev
199
- mpx-db migrate up staging
200
- mpx-db migrate up prod
201
-
202
- # Compare schemas (visual inspection)
203
- mpx-db schema dump dev > dev-schema.sql
204
- mpx-db schema dump prod > prod-schema.sql
205
- diff dev-schema.sql prod-schema.sql
206
- ```
207
-
208
- ### Daily Workflow
209
-
210
- ```bash
211
- # Check database status
212
- mpx-db info dev
213
- mpx-db tables dev
214
-
215
- # Run a quick query
216
- mpx-db query dev "SELECT COUNT(*) FROM orders WHERE created_at > date('now', '-1 day')"
217
-
218
- # Describe a table before modifying it
219
- mpx-db describe dev orders
220
-
221
- # Create a migration for changes
222
- mpx-db migrate create add_order_status_field
223
-
224
- # Export data for backup/analysis
225
- mpx-db export dev orders --format csv --output orders-backup.csv
126
+ mpx-db export <connection> <table> --format json
127
+ mpx-db export <connection> <table> --format csv --output data.csv
226
128
  ```
227
129
 
228
130
  ## AI Agent Usage
229
131
 
230
- `mpx-db` is **AI-native** โ€” designed for both humans and AI agents. Every command supports machine-readable output and schema discovery.
132
+ mpx-db is designed to be used by AI agents as well as humans.
231
133
 
232
134
  ### JSON Output
233
135
 
234
- Add `--json` to any command for structured output:
136
+ Add `--json` to any command for structured, machine-readable output:
235
137
 
236
138
  ```bash
237
- # Query with JSON output
238
139
  mpx-db query dev "SELECT * FROM users LIMIT 3" --json
140
+ ```
141
+
142
+ ```json
239
143
  {
240
144
  "success": true,
241
145
  "type": "query",
242
146
  "rows": [
243
- { "id": 1, "name": "Alice", "email": "alice@example.com" },
244
- { "id": 2, "name": "Bob", "email": "bob@example.com" },
245
- { "id": 3, "name": "Charlie", "email": "charlie@example.com" }
147
+ { "id": 1, "name": "Alice", "email": "alice@example.com" }
246
148
  ],
247
- "rowCount": 3,
149
+ "rowCount": 1,
248
150
  "duration": 12
249
151
  }
250
-
251
- # List tables with JSON
252
- mpx-db tables dev --json
253
- {
254
- "tables": [
255
- { "name": "users", "type": "table", "rowCount": 150 },
256
- { "name": "orders", "type": "table", "rowCount": 892 }
257
- ]
258
- }
259
-
260
- # Migration status
261
- mpx-db migrate status dev --json
262
- {
263
- "migrations": [
264
- { "name": "20260215_100000_create_users", "status": "applied", "appliedAt": "2026-02-15T10:05:00.000Z" },
265
- { "name": "20260216_120000_add_orders", "status": "pending", "appliedAt": null }
266
- ]
267
- }
268
152
  ```
269
153
 
270
154
  ### Schema Discovery
271
155
 
272
- Get the full command schema for AI agent integration:
273
-
274
156
  ```bash
275
157
  mpx-db --schema
276
158
  ```
277
159
 
278
- Returns a comprehensive JSON schema describing all commands, flags, inputs, outputs, and examples.
279
-
280
- ### MCP (Model Context Protocol) Server
281
-
282
- Run `mpx-db` as an MCP server for seamless AI agent integration:
160
+ Returns a complete JSON schema describing all commands, flags, inputs, outputs, and examples.
283
161
 
284
- ```bash
285
- mpx-db mcp
286
- ```
162
+ ### MCP Integration
287
163
 
288
- #### Claude Desktop Integration
289
-
290
- Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json`):
164
+ Add to your MCP client configuration (Claude Desktop, Cursor, Windsurf, etc.):
291
165
 
292
166
  ```json
293
167
  {
@@ -300,181 +174,84 @@ Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_
300
174
  }
301
175
  ```
302
176
 
303
- Now Claude can directly query your databases, inspect schemas, and run migrations!
177
+ The MCP server exposes these tools:
178
+ - **`query`** โ€” Execute SQL queries
179
+ - **`list_tables`** โ€” Get all tables with row counts
180
+ - **`describe_table`** โ€” Show table schema
181
+ - **`get_info`** โ€” Database information
182
+ - **`export_table`** โ€” Export table data as JSON
183
+ - **`get_schema`** โ€” Get full command schema
304
184
 
305
- #### Available MCP Tools
185
+ ### Exit Codes
306
186
 
307
- - `query` โ€” Execute SQL queries
308
- - `list_tables` โ€” Get all tables with row counts
309
- - `describe_table` โ€” Show table schema
310
- - `get_info` โ€” Database information
311
- - `export_table` โ€” Export table data as JSON
312
- - `get_schema` โ€” Get full command schema
187
+ | Code | Meaning |
188
+ |------|---------|
189
+ | 0 | Success |
190
+ | 1 | Error (connection failed, query failed, etc.) |
313
191
 
314
- ### Quiet Mode
192
+ ### PDF Reports
315
193
 
316
- Suppress non-essential output with `--quiet`:
194
+ Generate professional PDF reports from queries and schema dumps:
317
195
 
318
196
  ```bash
319
- # Just the data, no banners or progress messages
320
- mpx-db query dev "SELECT COUNT(*) FROM users" --quiet --json
321
- ```
322
-
323
- ### Exit Codes
324
-
325
- Predictable exit codes for CI/CD and scripting:
197
+ # Schema report โ€” full database structure as PDF
198
+ mpx-db schema dump <connection> --pdf schema-report.pdf
326
199
 
327
- - `0` โ€” Success
328
- - `1` โ€” Error (connection failed, query failed, etc.)
329
-
330
- ```bash
331
- #!/bin/bash
332
- if mpx-db query dev "SELECT 1" --quiet; then
333
- echo "Database is up"
334
- else
335
- echo "Database is down"
336
- exit 1
337
- fi
200
+ # Query results as PDF
201
+ mpx-db query <connection> "SELECT * FROM users" --pdf results.pdf
338
202
  ```
339
203
 
340
- ### Example: AI Agent Workflow
341
-
342
- ```javascript
343
- // AI agent discovers available commands
344
- const schema = await exec('mpx-db --schema');
204
+ PDFs include formatted tables, database metadata, and Mesaplex branding. Great for sharing with teammates or archiving.
345
205
 
346
- // Agent queries database
347
- const result = await exec('mpx-db query dev "SELECT * FROM orders WHERE status = \'pending\'" --json');
348
- const orders = JSON.parse(result.stdout);
206
+ ### Automation Tips
349
207
 
350
- // Agent inspects schema
351
- const tables = await exec('mpx-db tables dev --json');
208
+ - Use `--json` for machine-parseable output
209
+ - Use `--pdf <file>` for formatted reports
210
+ - Use `--quiet` to suppress banners and progress info
211
+ - Pipe output to `jq` for filtering
212
+ - Check exit codes for pass/fail in CI/CD
352
213
 
353
- // Agent runs migration
354
- await exec('mpx-db migrate up dev --json --quiet');
355
- ```
214
+ ## Database Support
356
215
 
357
- ## Architecture
216
+ | Database | Driver Package | Notes |
217
+ |------------|-------------------|--------------------------|
218
+ | SQLite | better-sqlite3 | File-based, great for dev|
219
+ | PostgreSQL | pg | Full support |
220
+ | MySQL | mysql2 | Full support |
358
221
 
359
- ```
360
- mpx-db/
361
- โ”œโ”€โ”€ bin/
362
- โ”‚ โ””โ”€โ”€ mpx-db.js # CLI entry point
363
- โ”œโ”€โ”€ src/
364
- โ”‚ โ”œโ”€โ”€ cli.js # Command definitions
365
- โ”‚ โ”œโ”€โ”€ commands/ # Command implementations
366
- โ”‚ โ”‚ โ”œโ”€โ”€ connections.js
367
- โ”‚ โ”‚ โ”œโ”€โ”€ query.js
368
- โ”‚ โ”‚ โ”œโ”€โ”€ schema.js
369
- โ”‚ โ”‚ โ”œโ”€โ”€ migrate.js
370
- โ”‚ โ”‚ โ””โ”€โ”€ data.js
371
- โ”‚ โ”œโ”€โ”€ db/ # Database adapters
372
- โ”‚ โ”‚ โ”œโ”€โ”€ base-adapter.js
373
- โ”‚ โ”‚ โ”œโ”€โ”€ sqlite-adapter.js
374
- โ”‚ โ”‚ โ”œโ”€โ”€ postgres-adapter.js
375
- โ”‚ โ”‚ โ”œโ”€โ”€ mysql-adapter.js
376
- โ”‚ โ”‚ โ””โ”€โ”€ connection.js
377
- โ”‚ โ””โ”€โ”€ utils/ # Utilities
378
- โ”‚ โ”œโ”€โ”€ crypto.js # Credential encryption
379
- โ”‚ โ””โ”€โ”€ config.js # Config management
380
- โ””โ”€โ”€ test/ # Test suite
381
- ```
222
+ Drivers are optional peer dependencies โ€” install only what you need. If a driver is missing, you'll get a helpful error message.
382
223
 
383
224
  ## Security
384
225
 
385
226
  - **Encrypted credentials** โ€” Connection strings with passwords are encrypted using AES-256-GCM
386
227
  - **Local storage** โ€” Credentials stored in `~/.mpx-db/connections.json` with 600 permissions
387
- - **Key management** โ€” Encryption key stored in `~/.mpx-db/.key` (auto-generated)
388
-
389
- โš ๏ธ **Note:** While credentials are encrypted at rest, this is not a substitute for proper secrets management in production. For production deployments, use environment variables or a secrets manager.
390
-
391
- ## Database Support
392
-
393
- | Database | Status | Driver Package | Notes |
394
- |------------|--------|-------------------|--------------------------|
395
- | SQLite | โœ… | better-sqlite3 | File-based, great for dev|
396
- | PostgreSQL | โœ… | pg | Full support |
397
- | MySQL | โœ… | mysql2 | Full support |
398
-
399
- **Note:** Database drivers are optional peer dependencies. Install only what you need:
400
-
401
- ```bash
402
- npm install -g better-sqlite3 # For SQLite
403
- npm install -g pg # For PostgreSQL
404
- npm install -g mysql2 # For MySQL
405
- ```
406
-
407
- If you try to connect without the required driver, you'll get a helpful error message:
228
+ - **Key management** โ€” Encryption key in `~/.mpx-db/.key` (auto-generated)
408
229
 
409
- ```
410
- โœ— SQLite driver not found. Install it with:
411
- npm install better-sqlite3
412
- ```
413
-
414
- ## Testing
415
-
416
- ```bash
417
- # Run all tests
418
- npm test
419
-
420
- # Run tests in watch mode
421
- npm run test:watch
422
- ```
423
-
424
- Test suite includes:
425
- - Connection management (4 tests)
426
- - Schema operations (4 tests)
427
- - Query operations (5 tests)
428
- - Migrations (4 tests)
429
- - Data export (3 tests)
230
+ โš ๏ธ For production, use environment variables or a secrets manager rather than saved connections.
430
231
 
431
- **Total: 23 tests** โœ…
232
+ ## Free vs Pro
432
233
 
433
- ## Why mpx-db?
234
+ All features are currently available in the free tier.
434
235
 
435
- **The problem:** Developers juggle multiple CLI tools (psql, mysql, sqlite3), each with different syntax. Schema changes require hand-written migrations. No easy way to compare schemas across environments.
236
+ **Upgrade to Pro:** [https://mesaplex.com/mpx-db](https://mesaplex.com/mpx-db)
436
237
 
437
- **The solution:** One tool, consistent interface, automatic schema inspection, git-friendly migrations.
438
-
439
- **Inspiration:** Tools like [Skeema](https://www.skeema.io/) prove this model works. But Skeema is MySQL-only and expensive for small teams. `mpx-db` is open source, multi-database, and focused on developer ergonomics.
440
-
441
- ## Roadmap
442
-
443
- **v1.1 (Current)** โœ…
444
- - SQLite, PostgreSQL, MySQL support
445
- - Connection management
446
- - Query execution with beautiful output
447
- - Schema inspection (dump, describe, tables, info)
448
- - Migration system (create, up, down, status)
449
- - Data export (JSON, CSV)
450
- - **AI-native features:** JSON output (`--json`), schema discovery (`--schema`), MCP server mode
451
- - **Quiet mode** (`--quiet`) for scripting
452
- - Predictable exit codes
453
-
454
- **v1.2 (Planned)**
455
- - Interactive query REPL mode
456
- - Query history and favorites
457
- - Auto-complete for table/column names
458
- - Migration templates (create table, add column, etc.)
238
+ ## License
459
239
 
460
- **v2.0 (Future)**
461
- - Schema diff between environments
462
- - Auto-generate migrations from schema changes
463
- - Visual schema diagrams (ASCII art)
464
- - Data seeding from JSON/CSV
465
- - Database backup & restore
466
- - Support for MongoDB, Redis
240
+ Dual License โ€” Free tier for personal use, Pro license for commercial use and advanced features. See [LICENSE](LICENSE) for full terms.
467
241
 
468
- ## Contributing
242
+ ## Links
469
243
 
470
- Issues and PRs welcome! This is an open-source project.
244
+ - **Website:** [https://mesaplex.com](https://mesaplex.com)
245
+ - **npm:** [https://www.npmjs.com/package/mpx-db](https://www.npmjs.com/package/mpx-db)
246
+ - **GitHub:** [https://github.com/mesaplexdev/mpx-db](https://github.com/mesaplexdev/mpx-db)
247
+ - **Support:** support@mesaplex.com
471
248
 
472
- ## License
249
+ ### Related Tools
473
250
 
474
- MIT
251
+ - **[mpx-scan](https://www.npmjs.com/package/mpx-scan)** โ€” Website security scanner
252
+ - **[mpx-api](https://www.npmjs.com/package/mpx-api)** โ€” API testing, mocking, and documentation
253
+ - **[mpx-secrets-audit](https://www.npmjs.com/package/mpx-secrets-audit)** โ€” Secret lifecycle tracking and audit
475
254
 
476
255
  ---
477
256
 
478
- **Built with:** Node.js, Commander.js, better-sqlite3, chalk, cli-table3
479
-
480
- **Made by Mesaplex** โ€” Build tools that actually work.
257
+ **Made with โค๏ธ by [Mesaplex](https://mesaplex.com)**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mpx-db",
3
- "version": "1.1.3",
4
- "description": "Database management CLI - Connect, query, migrate, and manage databases from the terminal",
3
+ "version": "1.2.0",
4
+ "description": "Database management CLI. Connect, query, migrate, and manage databases. AI-native with JSON output and MCP server.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
7
7
  "mpx-db": "bin/mpx-db.js"
@@ -12,20 +12,23 @@
12
12
  "test:watch": "node --test --watch test/**/*.test.js",
13
13
  "dev": "node bin/mpx-db.js"
14
14
  },
15
+ "funding": "https://mesaplex.com/pricing",
15
16
  "keywords": [
16
- "database",
17
17
  "cli",
18
+ "devtools",
19
+ "mesaplex",
20
+ "ai-native",
21
+ "mcp",
22
+ "model-context-protocol",
23
+ "automation",
24
+ "json-output",
25
+ "database",
18
26
  "migration",
19
27
  "postgresql",
20
28
  "mysql",
21
29
  "sqlite",
22
30
  "schema",
23
- "sql",
24
- "mcp",
25
- "ai-native",
26
- "model-context-protocol",
27
- "automation",
28
- "json-output"
31
+ "sql"
29
32
  ],
30
33
  "author": "Mesaplex <support@mesaplex.com>",
31
34
  "license": "SEE LICENSE IN LICENSE",
@@ -34,7 +37,9 @@
34
37
  "url": "git+https://github.com/mesaplexdev/mpx-db.git"
35
38
  },
36
39
  "homepage": "https://github.com/mesaplexdev/mpx-db#readme",
37
- "bugs": "https://github.com/mesaplexdev/mpx-db/issues",
40
+ "bugs": {
41
+ "url": "https://github.com/mesaplexdev/mpx-db/issues"
42
+ },
38
43
  "engines": {
39
44
  "node": ">=18.0.0"
40
45
  },
@@ -43,7 +48,8 @@
43
48
  "better-sqlite3": "^12.6.2",
44
49
  "chalk": "^5.3.0",
45
50
  "cli-table3": "^0.6.5",
46
- "commander": "^12.1.0"
51
+ "commander": "^12.1.0",
52
+ "pdfkit": "^0.17.2"
47
53
  },
48
54
  "peerDependencies": {
49
55
  "mysql2": "^3.11.5",
@@ -62,6 +68,6 @@
62
68
  "bin/",
63
69
  "README.md",
64
70
  "LICENSE",
65
- "package.json"
71
+ "CHANGELOG.md"
66
72
  ]
67
73
  }
package/src/cli.js CHANGED
@@ -31,15 +31,24 @@ program
31
31
  .version(pkg.version)
32
32
  .option('--json', 'Output as JSON (machine-readable)')
33
33
  .option('-q, --quiet', 'Suppress non-essential output')
34
+ .option('--no-color', 'Disable colored output')
34
35
  .option('--schema', 'Output JSON schema describing all commands and flags')
35
- .hook('preAction', (thisCommand) => {
36
+ .option('--pdf <file>', 'Export results as a PDF report');
37
+
38
+ // Error handling โ€” must be set BEFORE .command() so subcommands inherit exitOverride
39
+ program.exitOverride();
40
+ program.configureOutput({
41
+ writeErr: () => {} // Suppress Commander's own error output; we handle it in the catch below
42
+ });
43
+
44
+ program.hook('preAction', (thisCommand) => {
36
45
  // Merge parent options with command options
37
46
  const parentOpts = thisCommand.parent?.opts() || {};
38
47
  const opts = thisCommand.opts();
39
48
  globalOptions = { ...parentOpts, ...opts };
40
49
 
41
- // Disable chalk if JSON mode
42
- if (globalOptions.json) {
50
+ // Disable chalk if JSON mode or --no-color
51
+ if (globalOptions.json || globalOptions.color === false) {
43
52
  chalk.level = 0;
44
53
  }
45
54
  });
@@ -74,7 +83,8 @@ program
74
83
  .description('Execute a SQL query')
75
84
  .argument('<target>', 'Connection name or URL')
76
85
  .argument('<sql>', 'SQL query to execute')
77
- .action((target, sql) => handleQuery(target, sql, globalOptions));
86
+ .option('--pdf <file>', 'Export query results as PDF report')
87
+ .action((target, sql, options) => handleQuery(target, sql, { ...globalOptions, ...options }));
78
88
 
79
89
  // Info command
80
90
  program
@@ -107,7 +117,8 @@ schema
107
117
  .command('dump')
108
118
  .description('Dump database schema as SQL')
109
119
  .argument('<target>', 'Connection name or URL')
110
- .action((target) => dumpSchema(target, globalOptions));
120
+ .option('--pdf <file>', 'Export schema as PDF report')
121
+ .action((target, options) => dumpSchema(target, { ...globalOptions, ...options }));
111
122
 
112
123
  // Migration commands
113
124
  const migrate = program
@@ -218,7 +229,7 @@ program
218
229
  if (jsonMode) {
219
230
  console.log(JSON.stringify({ error: err.message, code: 'ERR_UPDATE' }, null, 2));
220
231
  } else {
221
- console.error(chalk.red.bold('\nโŒ Update check failed:'), err.message);
232
+ console.error(chalk.red('Error:'), err.message);
222
233
  console.error('');
223
234
  }
224
235
  process.exit(1);
@@ -245,9 +256,6 @@ if (process.argv.includes('--schema')) {
245
256
  process.exit(0);
246
257
  }
247
258
 
248
- // Error handling
249
- program.exitOverride();
250
-
251
259
  try {
252
260
  await program.parseAsync(process.argv);
253
261
  } catch (err) {
@@ -256,7 +264,8 @@ try {
256
264
  process.exit(0);
257
265
  }
258
266
  if (err.code !== 'commander.help' && err.code !== 'commander.helpDisplayed') {
259
- console.error(chalk.red(`Error: ${err.message}`));
260
- process.exit(1);
267
+ const msg = err.message.startsWith('error:') ? `Error: ${err.message.slice(7)}` : `Error: ${err.message}`;
268
+ console.error(chalk.red(msg));
269
+ process.exit(2);
261
270
  }
262
271
  }
@@ -2,6 +2,7 @@ import chalk from 'chalk';
2
2
  import Table from 'cli-table3';
3
3
  import { getConnection } from '../utils/config.js';
4
4
  import { createConnection } from '../db/connection.js';
5
+ import { generateQueryPDF } from '../reporters/pdf.js';
5
6
 
6
7
  /**
7
8
  * Execute a query or statement
@@ -27,8 +28,15 @@ export async function handleQuery(target, sql, options = {}) {
27
28
  const rows = await db.query(sql);
28
29
  const duration = Date.now() - startTime;
29
30
 
31
+ // PDF output
32
+ if (options.pdf) {
33
+ await generateQueryPDF(rows, { sql, duration }, options.pdf);
34
+ if (!options.quiet) {
35
+ console.log(chalk.green(`โœ“ Query report saved to ${options.pdf} (${rows.length} rows)`));
36
+ }
37
+ }
30
38
  // JSON output
31
- if (options.json) {
39
+ else if (options.json) {
32
40
  console.log(JSON.stringify({
33
41
  success: true,
34
42
  type: 'query',
@@ -2,6 +2,7 @@ import chalk from 'chalk';
2
2
  import Table from 'cli-table3';
3
3
  import { createConnection } from '../db/connection.js';
4
4
  import { resolveConnection } from './query.js';
5
+ import { generateSchemaPDF } from '../reporters/pdf.js';
5
6
 
6
7
  /**
7
8
  * Show database info
@@ -135,9 +136,9 @@ export async function describeTable(target, tableName, options = {}) {
135
136
  if (options.json) {
136
137
  console.log(JSON.stringify({ error: `Table "${tableName}" not found` }, null, 2));
137
138
  } else {
138
- console.log(chalk.yellow(`Table "${tableName}" not found or has no columns`));
139
+ console.error(chalk.yellow(`Table "${tableName}" not found or has no columns`));
139
140
  }
140
- return;
141
+ process.exit(1);
141
142
  }
142
143
 
143
144
  // JSON output
@@ -216,19 +217,38 @@ export async function dumpSchema(target, options = {}) {
216
217
  sqlOutput += `-- Table: ${table.name}\n`;
217
218
  sqlOutput += `-- Rows: ${table.rows}\n`;
218
219
 
219
- // This is a simplified dump - real implementations would generate proper DDL
220
+ const pkCols = schema.filter(c => c.primaryKey).map(c => c.name);
220
221
  const cols = schema.map(c => {
221
222
  let def = ` ${c.name} ${c.type}`;
222
- if (!c.nullable) def += ' NOT NULL';
223
+ if (c.primaryKey && pkCols.length === 1) def += ' PRIMARY KEY';
224
+ if (!c.nullable && !c.primaryKey) def += ' NOT NULL';
223
225
  if (c.default) def += ` DEFAULT ${c.default}`;
224
226
  return def;
225
227
  });
226
228
 
227
229
  sqlOutput += `CREATE TABLE "${table.name}" (\n`;
228
230
  sqlOutput += cols.join(',\n');
231
+ if (pkCols.length > 1) {
232
+ sqlOutput += `,\n PRIMARY KEY (${pkCols.join(', ')})`;
233
+ }
229
234
  sqlOutput += '\n);\n\n';
230
235
  }
231
236
 
237
+ // PDF output
238
+ if (options.pdf) {
239
+ const tableSchemas = [];
240
+ for (const table of tables) {
241
+ if (table.type !== 'table') continue;
242
+ const cols = await db.getTableSchema(table.name);
243
+ tableSchemas.push({ name: table.name, columns: cols });
244
+ }
245
+ await generateSchemaPDF(info, tables, tableSchemas, options.pdf);
246
+ if (!options.quiet) {
247
+ console.log(chalk.green(`โœ“ Schema report saved to ${options.pdf}`));
248
+ }
249
+ return;
250
+ }
251
+
232
252
  // JSON output
233
253
  if (options.json) {
234
254
  console.log(JSON.stringify({ sql: sqlOutput }, null, 2));
@@ -0,0 +1,286 @@
1
+ /**
2
+ * PDF Report Generator for mpx-db
3
+ *
4
+ * Generates professional database reports using PDFKit.
5
+ * Style consistent with mpx-scan and mpx-api PDF reports.
6
+ */
7
+
8
+ import PDFDocument from 'pdfkit';
9
+ import { createWriteStream, readFileSync } from 'fs';
10
+ import { fileURLToPath } from 'url';
11
+ import { dirname, join } from 'path';
12
+
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = dirname(__filename);
15
+ const pkg = JSON.parse(readFileSync(join(__dirname, '../../package.json'), 'utf8'));
16
+
17
+ // Color palette (consistent with mpx-scan / mpx-api)
18
+ const COLORS = {
19
+ primary: '#1a56db',
20
+ dark: '#1f2937',
21
+ gray: '#6b7280',
22
+ lightGray: '#e5e7eb',
23
+ white: '#ffffff',
24
+ pass: '#16a34a',
25
+ warn: '#ea580c',
26
+ fail: '#dc2626',
27
+ headerBg: '#1e3a5f',
28
+ sectionBg: '#f3f4f6',
29
+ };
30
+
31
+ /**
32
+ * Check for page overflow and add page if needed
33
+ */
34
+ function checkPage(doc, minSpace = 120) {
35
+ if (doc.y > doc.page.height - minSpace) {
36
+ doc.addPage();
37
+ doc.y = 50;
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Draw consistent header bar
43
+ */
44
+ function drawHeader(doc, title, subtitle) {
45
+ const pageWidth = doc.page.width - doc.page.margins.left - doc.page.margins.right;
46
+ doc.rect(0, 0, doc.page.width, 100).fill(COLORS.headerBg);
47
+ doc.fontSize(22).fillColor(COLORS.white).font('Helvetica-Bold')
48
+ .text(title, 50, 30);
49
+ doc.fontSize(10).fillColor('#a0b4cc').font('Helvetica')
50
+ .text(subtitle, 50, 60);
51
+ doc.y = 120;
52
+ return pageWidth;
53
+ }
54
+
55
+ /**
56
+ * Draw footers on all pages
57
+ */
58
+ function drawFooters(doc, pageWidth) {
59
+ const now = new Date().toLocaleDateString('en-US', {
60
+ year: 'numeric', month: 'long', day: 'numeric',
61
+ hour: '2-digit', minute: '2-digit',
62
+ });
63
+ const range = doc.bufferedPageRange();
64
+ for (let i = range.start; i < range.start + range.count; i++) {
65
+ doc.switchToPage(i);
66
+ const footerY = doc.page.height - 35;
67
+ doc.fontSize(7).fillColor(COLORS.gray).font('Helvetica')
68
+ .text(
69
+ `Generated by mpx-db v${pkg.version} on ${now}`,
70
+ 50, footerY, { width: pageWidth, align: 'center' }
71
+ );
72
+ doc.text(
73
+ `Page ${i + 1} of ${range.count}`,
74
+ 50, footerY + 12, { width: pageWidth, align: 'center' }
75
+ );
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Draw a data table
81
+ */
82
+ function drawTable(doc, headers, rows, pageWidth, options = {}) {
83
+ const colWidths = options.colWidths || headers.map(() => pageWidth / headers.length);
84
+ const startX = 50;
85
+
86
+ // Header row
87
+ checkPage(doc, 80);
88
+ let x = startX;
89
+ doc.roundedRect(startX, doc.y, pageWidth, 22, 3).fill(COLORS.primary);
90
+ for (let i = 0; i < headers.length; i++) {
91
+ doc.fontSize(9).fillColor(COLORS.white).font('Helvetica-Bold')
92
+ .text(headers[i], x + 6, doc.y + 5, { width: colWidths[i] - 12 });
93
+ x += colWidths[i];
94
+ }
95
+ doc.y += 26;
96
+
97
+ // Data rows
98
+ for (let r = 0; r < rows.length; r++) {
99
+ checkPage(doc, 40);
100
+ const row = rows[r];
101
+ const bg = r % 2 === 0 ? COLORS.sectionBg : COLORS.white;
102
+
103
+ // Calculate row height
104
+ let maxH = 16;
105
+ for (let i = 0; i < row.length; i++) {
106
+ const h = doc.heightOfString(String(row[i] ?? ''), { width: colWidths[i] - 12, fontSize: 8 });
107
+ if (h + 8 > maxH) maxH = h + 8;
108
+ }
109
+
110
+ doc.rect(startX, doc.y, pageWidth, maxH).fill(bg);
111
+ x = startX;
112
+ for (let i = 0; i < row.length; i++) {
113
+ const val = row[i] === null || row[i] === undefined ? 'โ€”' : String(row[i]);
114
+ const color = val === 'โ€”' ? COLORS.gray : COLORS.dark;
115
+ doc.fontSize(8).fillColor(color).font('Helvetica')
116
+ .text(val, x + 6, doc.y + 4, { width: colWidths[i] - 12 });
117
+ x += colWidths[i];
118
+ }
119
+ doc.y += maxH;
120
+ }
121
+ doc.y += 10;
122
+ }
123
+
124
+ /**
125
+ * Generate schema report PDF
126
+ */
127
+ export function generateSchemaPDF(dbInfo, tables, tableSchemas, outputPath) {
128
+ return new Promise((resolve, reject) => {
129
+ try {
130
+ const now = new Date().toLocaleDateString('en-US', {
131
+ year: 'numeric', month: 'long', day: 'numeric',
132
+ hour: '2-digit', minute: '2-digit',
133
+ });
134
+
135
+ const doc = new PDFDocument({
136
+ size: 'A4',
137
+ margins: { top: 50, bottom: 50, left: 50, right: 50 },
138
+ info: {
139
+ Title: `mpx-db Schema Report โ€” ${dbInfo.database || dbInfo.path || 'Database'}`,
140
+ Author: 'mpx-db',
141
+ Subject: 'Database Schema Report',
142
+ Creator: `mpx-db v${pkg.version}`,
143
+ },
144
+ bufferPages: true,
145
+ });
146
+
147
+ const stream = createWriteStream(outputPath);
148
+ doc.pipe(stream);
149
+
150
+ const dbName = dbInfo.database || dbInfo.path || 'Database';
151
+ const pageWidth = drawHeader(doc,
152
+ 'mpx-db Schema Report',
153
+ `v${pkg.version} โ€ข ${now} โ€ข ${dbName}`
154
+ );
155
+
156
+ // โ”€โ”€โ”€ Summary Box โ”€โ”€โ”€
157
+ doc.roundedRect(50, doc.y, pageWidth, 80, 6).fill(COLORS.sectionBg);
158
+ const summaryTop = doc.y + 12;
159
+
160
+ doc.circle(100, summaryTop + 28, 26).fill(COLORS.primary);
161
+ doc.fontSize(20).fillColor(COLORS.white).font('Helvetica-Bold')
162
+ .text(String(dbInfo.tables), 100 - 20, summaryTop + 16, { width: 40, align: 'center' });
163
+
164
+ doc.fontSize(14).fillColor(COLORS.dark).font('Helvetica-Bold')
165
+ .text(`${dbInfo.type} Database`, 145, summaryTop + 5);
166
+ doc.fontSize(10).fillColor(COLORS.gray).font('Helvetica')
167
+ .text(`${dbInfo.tables} tables โ€ข ${dbInfo.totalRows.toLocaleString()} total rows โ€ข ${dbInfo.sizeFormatted}`, 145, summaryTop + 25);
168
+
169
+ doc.y = summaryTop + 68;
170
+
171
+ // โ”€โ”€โ”€ Tables Overview โ”€โ”€โ”€
172
+ doc.y += 10;
173
+ doc.fontSize(14).fillColor(COLORS.dark).font('Helvetica-Bold')
174
+ .text('Tables', 50, doc.y);
175
+ doc.y += 20;
176
+
177
+ const tableRows = tables.map(t => [t.name, t.type, t.rows.toLocaleString()]);
178
+ drawTable(doc, ['Table', 'Type', 'Rows'], tableRows, pageWidth, {
179
+ colWidths: [pageWidth * 0.5, pageWidth * 0.25, pageWidth * 0.25]
180
+ });
181
+
182
+ // โ”€โ”€โ”€ Table Schemas โ”€โ”€โ”€
183
+ for (const { name, columns } of tableSchemas) {
184
+ checkPage(doc, 100);
185
+
186
+ doc.y += 5;
187
+ doc.roundedRect(50, doc.y, pageWidth, 26, 4).fill(COLORS.headerBg);
188
+ doc.fontSize(11).fillColor(COLORS.white).font('Helvetica-Bold')
189
+ .text(name, 60, doc.y + 7);
190
+ doc.fontSize(9).fillColor('#a0b4cc').font('Helvetica')
191
+ .text(`${columns.length} columns`, 60, doc.y + 7, { width: pageWidth - 20, align: 'right' });
192
+ doc.y += 32;
193
+
194
+ const colRows = columns.map(c => [
195
+ c.name,
196
+ c.type,
197
+ c.nullable ? 'YES' : 'NO',
198
+ c.default || 'โ€”',
199
+ c.primaryKey ? 'PRI' : 'โ€”'
200
+ ]);
201
+
202
+ drawTable(doc, ['Column', 'Type', 'Nullable', 'Default', 'Key'], colRows, pageWidth, {
203
+ colWidths: [pageWidth * 0.28, pageWidth * 0.22, pageWidth * 0.15, pageWidth * 0.2, pageWidth * 0.15]
204
+ });
205
+ }
206
+
207
+ drawFooters(doc, pageWidth);
208
+ doc.end();
209
+
210
+ stream.on('finish', () => resolve(outputPath));
211
+ stream.on('error', reject);
212
+ } catch (err) {
213
+ reject(err);
214
+ }
215
+ });
216
+ }
217
+
218
+ /**
219
+ * Generate query results PDF
220
+ */
221
+ export function generateQueryPDF(rows, meta, outputPath) {
222
+ return new Promise((resolve, reject) => {
223
+ try {
224
+ const now = new Date().toLocaleDateString('en-US', {
225
+ year: 'numeric', month: 'long', day: 'numeric',
226
+ hour: '2-digit', minute: '2-digit',
227
+ });
228
+
229
+ const doc = new PDFDocument({
230
+ size: 'A4',
231
+ layout: rows.length > 0 && Object.keys(rows[0]).length > 5 ? 'landscape' : 'portrait',
232
+ margins: { top: 50, bottom: 50, left: 50, right: 50 },
233
+ info: {
234
+ Title: `mpx-db Query Report`,
235
+ Author: 'mpx-db',
236
+ Subject: 'Database Query Results',
237
+ Creator: `mpx-db v${pkg.version}`,
238
+ },
239
+ bufferPages: true,
240
+ });
241
+
242
+ const stream = createWriteStream(outputPath);
243
+ doc.pipe(stream);
244
+
245
+ const pageWidth = drawHeader(doc,
246
+ 'mpx-db Query Report',
247
+ `v${pkg.version} โ€ข ${now}`
248
+ );
249
+
250
+ // Query info
251
+ doc.roundedRect(50, doc.y, pageWidth, 60, 6).fill(COLORS.sectionBg);
252
+ const summaryTop = doc.y + 10;
253
+
254
+ doc.fontSize(9).fillColor(COLORS.gray).font('Helvetica').text('SQL Query:', 60, summaryTop);
255
+ doc.fontSize(9).fillColor(COLORS.dark).font('Helvetica-Bold')
256
+ .text(meta.sql || 'โ€”', 60, summaryTop + 14, { width: pageWidth - 20 });
257
+
258
+ doc.fontSize(9).fillColor(COLORS.gray).font('Helvetica')
259
+ .text(`${rows.length} row(s) โ€ข ${meta.duration || 0}ms`, 60, summaryTop + 34);
260
+
261
+ doc.y = summaryTop + 56;
262
+
263
+ if (rows.length === 0) {
264
+ doc.y += 20;
265
+ doc.fontSize(12).fillColor(COLORS.gray).font('Helvetica')
266
+ .text('No rows returned.', 50, doc.y, { width: pageWidth, align: 'center' });
267
+ } else {
268
+ doc.y += 10;
269
+ const columns = Object.keys(rows[0]);
270
+ const colWidth = pageWidth / columns.length;
271
+ const dataRows = rows.map(row => columns.map(c => row[c]));
272
+ drawTable(doc, columns, dataRows, pageWidth, {
273
+ colWidths: columns.map(() => colWidth)
274
+ });
275
+ }
276
+
277
+ drawFooters(doc, pageWidth);
278
+ doc.end();
279
+
280
+ stream.on('finish', () => resolve(outputPath));
281
+ stream.on('error', reject);
282
+ } catch (err) {
283
+ reject(err);
284
+ }
285
+ });
286
+ }
package/src/schema.js CHANGED
@@ -139,7 +139,7 @@ export function getSchema() {
139
139
  },
140
140
  query: {
141
141
  description: 'Execute a SQL query',
142
- usage: 'mpx-db query <target> <sql> [--json]',
142
+ usage: 'mpx-db query <target> <sql> [--json] [--pdf file.pdf]',
143
143
  arguments: {
144
144
  target: {
145
145
  type: 'string',
@@ -158,6 +158,10 @@ export function getSchema() {
158
158
  default: false,
159
159
  description: 'Output results as JSON'
160
160
  },
161
+ '--pdf': {
162
+ type: 'string',
163
+ description: 'Export query results as a formatted PDF report'
164
+ },
161
165
  '--quiet': {
162
166
  type: 'boolean',
163
167
  default: false,
@@ -318,7 +322,7 @@ export function getSchema() {
318
322
  },
319
323
  'schema dump': {
320
324
  description: 'Dump database schema as SQL',
321
- usage: 'mpx-db schema dump <target>',
325
+ usage: 'mpx-db schema dump <target> [--pdf file.pdf]',
322
326
  arguments: {
323
327
  target: {
324
328
  type: 'string',
@@ -332,6 +336,10 @@ export function getSchema() {
332
336
  default: false,
333
337
  description: 'Output as JSON with SQL content'
334
338
  },
339
+ '--pdf': {
340
+ type: 'string',
341
+ description: 'Export schema as a formatted PDF report'
342
+ },
335
343
  '--quiet': {
336
344
  type: 'boolean',
337
345
  default: false,
@@ -586,6 +594,35 @@ export function getSchema() {
586
594
  }
587
595
  }
588
596
  },
597
+ globalFlags: {
598
+ '--json': {
599
+ type: 'boolean',
600
+ default: false,
601
+ description: 'Output results as structured JSON'
602
+ },
603
+ '--pdf': {
604
+ type: 'string',
605
+ description: 'Export results as a formatted PDF report (supported by query, schema dump)'
606
+ },
607
+ '-q, --quiet': {
608
+ type: 'boolean',
609
+ default: false,
610
+ description: 'Suppress non-essential output'
611
+ },
612
+ '--schema': {
613
+ type: 'boolean',
614
+ default: false,
615
+ description: 'Output this schema as JSON'
616
+ },
617
+ '--version': {
618
+ type: 'boolean',
619
+ description: 'Show version number'
620
+ },
621
+ '--help': {
622
+ type: 'boolean',
623
+ description: 'Show help information'
624
+ }
625
+ },
589
626
  exitCodes: {
590
627
  0: 'Success',
591
628
  1: 'Error (connection failed, query failed, etc.)'