@adevguide/mcp-database-server 1.0.2

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 ADDED
@@ -0,0 +1,1050 @@
1
+ # MCP Database Server
2
+
3
+ > **Enterprise-grade Model Context Protocol server for unified SQL database access**
4
+
5
+ A production-ready MCP server that provides seamless, intelligent access to multiple SQL databases with automatic schema discovery, relationship mapping, and built-in security controls.
6
+
7
+ ## Features
8
+
9
+ - πŸ—„οΈ **Multi-Database Support** - PostgreSQL, MySQL/MariaDB, SQLite, SQL Server, Oracle
10
+ - πŸ” **Automatic Schema Discovery** - Tables, columns, indexes, foreign keys, and relationships
11
+ - πŸ’Ύ **Intelligent Caching** - Persistent schema cache with TTL and version management
12
+ - πŸ”— **Relationship Inference** - Automatic foreign key detection plus heuristic pattern matching
13
+ - πŸ“Š **Query Intelligence** - Execution tracking, statistics, and performance insights
14
+ - 🎯 **Join Assistance** - Smart join path recommendations based on relationship graphs
15
+ - πŸ”’ **Enterprise Security** - Read-only mode, operation controls, dangerous operation protection, secret redaction
16
+ - ⚑ **High Performance** - Connection pooling, query timeouts, concurrent operation protection
17
+ - 🌐 **Environment Flexibility** - Environment variable interpolation for secure configuration
18
+
19
+ ## Architecture
20
+
21
+ ```
22
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
23
+ β”‚ MCP Client β”‚
24
+ β”‚ (Claude Desktop, IDEs, etc.) β”‚
25
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
26
+ β”‚ JSON-RPC over stdio
27
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
28
+ β”‚ MCP Database Server β”‚
29
+ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
30
+ β”‚ β”‚ Schema Cache (TTL + Versioning) β”‚ β”‚
31
+ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
32
+ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
33
+ β”‚ β”‚ Query Tracker (History + Statistics) β”‚ β”‚
34
+ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
35
+ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
36
+ β”‚ β”‚ Security Layer (Read-only, Operation Controls) β”‚ β”‚
37
+ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
38
+ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
39
+ β”‚ β”‚ β”‚ β”‚ β”‚
40
+ β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β” β”Œβ”€β”€β–Όβ”€β”€β”€β”€β” β”Œβ”€β”€β–Όβ”€β”€β”€β”€β”€β” β”Œβ”€β”€β–Όβ”€β”€β”€β”€β”€β”€β” β”Œβ–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”
41
+ β”‚Postgresβ”‚ β”‚ MySQL β”‚ β”‚ SQLite β”‚ β”‚ MSSQL β”‚ β”‚ Oracle β”‚
42
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
43
+ ```
44
+
45
+ ## Supported Databases
46
+
47
+ | Database | Driver | Status | Notes |
48
+ |----------|--------|--------|-------|
49
+ | PostgreSQL | `pg` | βœ… Full Support | Includes CockroachDB compatibility |
50
+ | MySQL/MariaDB | `mysql2` | βœ… Full Support | Includes Amazon Aurora MySQL compatibility |
51
+ | SQLite | `better-sqlite3` | βœ… Full Support | File-based databases |
52
+ | SQL Server | `tedious` | βœ… Full Support | Microsoft SQL Server / Azure SQL |
53
+ | Oracle | `oracledb` | ⚠️ Stub | Requires Oracle Instant Client |
54
+
55
+ ## Installation
56
+
57
+ ### Method 1: Install from npm (Recommended)
58
+
59
+ If this package is published to npm:
60
+
61
+ ```bash
62
+ npm install -g @adevguide/mcp-database-server
63
+ ```
64
+
65
+ Then you can run it directly:
66
+
67
+ ```bash
68
+ mcp-database-server --config /path/to/your/config.json
69
+ ```
70
+
71
+ ### Method 2: Install from source
72
+
73
+ Clone and build the project:
74
+
75
+ ```bash
76
+ git clone https://github.com/iPraBhu/mcp-database-server.git
77
+ cd mcp-database-server
78
+ npm install
79
+ npm run build
80
+ ```
81
+
82
+ Then run it:
83
+
84
+ ```bash
85
+ node dist/index.js --config ./.mcp-database-server.config
86
+ ```
87
+
88
+ ## Configuration
89
+
90
+ Create a `.mcp-database-server.config` file in your project root:
91
+
92
+ > **Note:** The config file is automatically discovered! If you don't specify `--config`, the tool searches for `.mcp-database-server.config` starting in the current directory and traversing up parent directories until found. This means you can run the tool from any subdirectory of your project.
93
+
94
+ ```json
95
+ {
96
+ "databases": [
97
+ {
98
+ "id": "postgres-main",
99
+ "type": "postgres",
100
+ "url": "${DB_URL_POSTGRES}",
101
+ "readOnly": false,
102
+ "pool": {
103
+ "min": 2,
104
+ "max": 10,
105
+ "idleTimeoutMillis": 30000
106
+ },
107
+ "introspection": {
108
+ "includeViews": true,
109
+ "excludeSchemas": ["pg_catalog"]
110
+ }
111
+ },
112
+ {
113
+ "id": "sqlite-local",
114
+ "type": "sqlite",
115
+ "path": "./data/app.db"
116
+ }
117
+ ],
118
+ "cache": {
119
+ "directory": ".sql-mcp-cache",
120
+ "ttlMinutes": 10
121
+ },
122
+ "security": {
123
+ "allowWrite": false,
124
+ "allowedWriteOperations": ["INSERT", "UPDATE"],
125
+ "disableDangerousOperations": true,
126
+ "redactSecrets": true
127
+ },
128
+ "logging": {
129
+ "level": "info",
130
+ "pretty": false
131
+ }
132
+ }
133
+ ```
134
+
135
+ ### Configuration Reference
136
+
137
+ #### Database Configuration
138
+
139
+ Each database in the `databases` array represents a connection to a SQL database.
140
+
141
+ ##### Core Properties
142
+
143
+ | Property | Type | Required | Default | Description |
144
+ |----------|------|----------|---------|-------------|
145
+ | `id` | string | βœ… Yes | - | Unique identifier for this database connection. Used in all MCP tool calls. Must be unique across all databases. |
146
+ | `type` | enum | βœ… Yes | - | Database system type. Valid values: `postgres`, `mysql`, `sqlite`, `mssql`, `oracle` |
147
+ | `url` | string | Conditional* | - | Database connection string. Required for all databases except SQLite. Supports environment variable interpolation: `${DB_URL}` |
148
+ | `path` | string | Conditional** | - | Filesystem path to SQLite database file. Required only for `type: sqlite`. Can be relative or absolute. |
149
+ | `readOnly` | boolean | No | `true` | When `true`, blocks all write operations (INSERT, UPDATE, DELETE, etc.). Recommended for production safety. |
150
+ | `eagerConnect` | boolean | No | `false` | When `true`, connects to database immediately at startup (fail-fast). When `false`, connects on first query (lazy loading). |
151
+
152
+ <sub>* Required for postgres, mysql, mssql, oracle</sub>
153
+ <sub>** Required for sqlite only</sub>
154
+
155
+ **Connection String Formats:**
156
+ ```
157
+ PostgreSQL: postgresql://username:password@host:5432/database
158
+ MySQL: mysql://username:password@host:3306/database
159
+ SQL Server: Server=host,1433;Database=dbname;User Id=user;Password=pass
160
+ SQLite: (use path property instead)
161
+ Oracle: username/password@host:1521/servicename
162
+ ```
163
+
164
+ ##### Connection Pool Configuration
165
+
166
+ The `pool` object controls connection pooling behavior. Improves performance by reusing database connections.
167
+
168
+ | Property | Type | Required | Default | Description |
169
+ |----------|------|----------|---------|-------------|
170
+ | `min` | number | No | `2` | Minimum number of connections to maintain in the pool. Kept alive even when idle. |
171
+ | `max` | number | No | `10` | Maximum number of concurrent connections. Do not exceed your database's connection limit. |
172
+ | `idleTimeoutMillis` | number | No | `30000` | Time (ms) to keep idle connections alive before closing. Example: `60000` = 1 minute. |
173
+ | `connectionTimeoutMillis` | number | No | `10000` | Time (ms) to wait when establishing a connection before timing out. Fail-fast if database is unreachable. |
174
+
175
+ **Recommendations:**
176
+ - **Development:** `min: 1`, `max: 5`
177
+ - **Production (Low Traffic):** `min: 2`, `max: 10`
178
+ - **Production (High Traffic):** `min: 5`, `max: 20`
179
+
180
+ ##### Introspection Configuration
181
+
182
+ The `introspection` object controls schema discovery behavior. Determines what database objects are analyzed.
183
+
184
+ | Property | Type | Required | Default | Description |
185
+ |----------|------|----------|---------|-------------|
186
+ | `includeViews` | boolean | No | `true` | Include database views in schema discovery. Set to `false` if views cause performance issues. |
187
+ | `includeRoutines` | boolean | No | `false` | Include stored procedures and functions. (Not fully implemented - planned feature) |
188
+ | `maxTables` | number | No | unlimited | Limit introspection to first N tables. Useful for databases with 1000+ tables. May result in incomplete relationship discovery. |
189
+ | `includeSchemas` | string[] | No | all | Whitelist of schemas to introspect. Only applicable to PostgreSQL and SQL Server. Example: `["public", "app"]` |
190
+ | `excludeSchemas` | string[] | No | none | Blacklist of schemas to skip. Common values: `["pg_catalog", "information_schema", "sys"]` |
191
+
192
+ **Schema vs Database:**
193
+ - **PostgreSQL/SQL Server:** Support multiple schemas per database. Use `includeSchemas`/`excludeSchemas`.
194
+ - **MySQL/MariaDB:** Schema = database. Use database name in connection string.
195
+ - **SQLite:** Single-file database, no schema concept.
196
+
197
+ #### Cache Configuration
198
+
199
+ Controls schema metadata caching to improve startup performance and reduce database load.
200
+
201
+ | Property | Type | Required | Default | Description |
202
+ |----------|------|----------|---------|-------------|
203
+ | `directory` | string | No | `.sql-mcp-cache` | Directory path where cached schema files are stored. One JSON file per database. |
204
+ | `ttlMinutes` | number | No | `10` | Time-To-Live in minutes. How long cached schema is considered valid before automatic refresh. |
205
+
206
+ **Cache Behavior:**
207
+ - **On Startup:** Loads schema from cache if available and not expired
208
+ - **After TTL Expiry:** Next query triggers automatic re-introspection
209
+ - **Manual Refresh:** Use `clear_cache` tool or `introspect_schema` with `forceRefresh: true`
210
+ - **Cache Files:** Stored as `{database-id}.json` (e.g., `postgres-main.json`)
211
+
212
+ **Recommended TTL Values:**
213
+ - **Development:** `5` minutes (schema changes frequently)
214
+ - **Staging:** `30-60` minutes
215
+ - **Production (Static):** `1440` minutes (24 hours)
216
+ - **Production (Active):** `60-240` minutes (1-4 hours)
217
+
218
+ #### Security Configuration
219
+
220
+ Comprehensive security controls to protect your databases from unauthorized or dangerous operations.
221
+
222
+ | Property | Type | Required | Default | Description |
223
+ |----------|------|----------|---------|-------------|
224
+ | `allowWrite` | boolean | No | `false` | Master switch for write operations. When `false`, all writes are blocked across all databases. |
225
+ | `allowedWriteOperations` | string[] | No | all | Whitelist of allowed SQL operations when `allowWrite: true`. Valid values: `INSERT`, `UPDATE`, `DELETE`, `CREATE`, `ALTER`, `DROP`, `TRUNCATE`, `REPLACE`, `MERGE` |
226
+ | `disableDangerousOperations` | boolean | No | `true` | **Extra safety layer.** When `true`, blocks `DELETE`, `TRUNCATE`, and `DROP` operations even if writes are allowed. Prevents accidental data loss. |
227
+ | `redactSecrets` | boolean | No | `true` | Automatically redact passwords and credentials in logs and error messages. |
228
+
229
+ **Security Layers (Evaluated in Order):**
230
+
231
+ 1. **Database-level `readOnly`** β†’ Blocks all writes for specific database
232
+ 2. **Global `allowWrite`** β†’ Master switch for all databases
233
+ 3. **`disableDangerousOperations`** β†’ Blocks DELETE/TRUNCATE/DROP specifically
234
+ 4. **`allowedWriteOperations`** β†’ Whitelist of permitted operations
235
+
236
+ **Example Configurations:**
237
+
238
+ ```json
239
+ // Read-only access (default - safest)
240
+ {
241
+ "allowWrite": false
242
+ }
243
+
244
+ // Allow INSERT and UPDATE only (no deletes)
245
+ {
246
+ "allowWrite": true,
247
+ "allowedWriteOperations": ["INSERT", "UPDATE"],
248
+ "disableDangerousOperations": true
249
+ }
250
+
251
+ // Full write access (development only - dangerous!)
252
+ {
253
+ "allowWrite": true,
254
+ "disableDangerousOperations": false
255
+ }
256
+ ```
257
+
258
+ #### Logging Configuration
259
+
260
+ Controls log output verbosity and formatting.
261
+
262
+ | Property | Type | Required | Default | Description |
263
+ |----------|------|----------|---------|-------------|
264
+ | `level` | enum | No | `info` | Log level. Valid values: `trace`, `debug`, `info`, `warn`, `error`. Lower levels include higher levels. |
265
+ | `pretty` | boolean | No | `false` | When `true`, formats logs as human-readable text. When `false`, outputs structured JSON (better for production log aggregation). |
266
+
267
+ **Log Levels:**
268
+ - **`trace`:** Everything (extremely verbose - use for debugging only)
269
+ - **`debug`:** Detailed diagnostic information
270
+ - **`info`:** General informational messages (recommended for production)
271
+ - **`warn`:** Warning messages that don't prevent operation
272
+ - **`error`:** Error messages only
273
+
274
+ **Recommendations:**
275
+ - **Development:** `level: "debug"`, `pretty: true`
276
+ - **Production:** `level: "info"`, `pretty: false`
277
+ - **Troubleshooting:** `level: "trace"`, `pretty: true`
278
+
279
+ ---
280
+
281
+ ### Complete Configuration Example
282
+
283
+ ```json
284
+ {
285
+ "databases": [
286
+ {
287
+ "id": "postgres-production",
288
+ "type": "postgres",
289
+ "url": "${DATABASE_URL}",
290
+ "readOnly": true,
291
+ "pool": {
292
+ "min": 5,
293
+ "max": 20,
294
+ "idleTimeoutMillis": 60000,
295
+ "connectionTimeoutMillis": 5000
296
+ },
297
+ "introspection": {
298
+ "includeViews": true,
299
+ "includeRoutines": false,
300
+ "excludeSchemas": ["pg_catalog", "information_schema"]
301
+ },
302
+ "eagerConnect": true
303
+ },
304
+ {
305
+ "id": "mysql-analytics",
306
+ "type": "mysql",
307
+ "url": "${MYSQL_URL}",
308
+ "readOnly": true,
309
+ "pool": {
310
+ "min": 2,
311
+ "max": 10
312
+ },
313
+ "introspection": {
314
+ "includeViews": true,
315
+ "maxTables": 100
316
+ }
317
+ },
318
+ {
319
+ "id": "sqlite-local",
320
+ "type": "sqlite",
321
+ "path": "./data/app.db",
322
+ "readOnly": false
323
+ }
324
+ ],
325
+ "cache": {
326
+ "directory": ".sql-mcp-cache",
327
+ "ttlMinutes": 60
328
+ },
329
+ "security": {
330
+ "allowWrite": false,
331
+ "allowedWriteOperations": ["INSERT", "UPDATE"],
332
+ "disableDangerousOperations": true,
333
+ "redactSecrets": true
334
+ },
335
+ "logging": {
336
+ "level": "info",
337
+ "pretty": false
338
+ }
339
+ }
340
+ ```
341
+
342
+ ---
343
+
344
+ ### Environment Variables
345
+
346
+ **Secure Configuration with Environment Variables:**
347
+
348
+ The server supports environment variable interpolation using `${VARIABLE_NAME}` syntax. This is the recommended approach for managing sensitive credentials.
349
+
350
+ **Example Configuration:**
351
+ ```json
352
+ {
353
+ "databases": [
354
+ {
355
+ "id": "production-db",
356
+ "type": "postgres",
357
+ "url": "${DATABASE_URL}"
358
+ }
359
+ ]
360
+ }
361
+ ```
362
+
363
+ **Environment File (`.env`):**
364
+ ```env
365
+ DATABASE_URL=postgresql://user:password@localhost:5432/dbname
366
+ DB_URL_MYSQL=mysql://user:password@localhost:3306/dbname
367
+ DB_URL_MSSQL=Server=host,1433;Database=db;User Id=sa;Password=pass
368
+ ```
369
+
370
+ **Best Practices:**
371
+ - βœ… Store `.env` file outside version control (add to `.gitignore`)
372
+ - βœ… Use different `.env` files for each environment (dev, staging, prod)
373
+ - βœ… Never commit credentials to git repositories
374
+ - βœ… Use secret management services (AWS Secrets Manager, HashiCorp Vault) in production
375
+
376
+ ---
377
+
378
+ ### Connection String Reference
379
+
380
+ | Database | Format | Example |
381
+ |----------|--------|---------|
382
+ | **PostgreSQL** | `postgresql://user:pass@host:port/db` | `postgresql://admin:secret@localhost:5432/myapp` |
383
+ | **MySQL** | `mysql://user:pass@host:port/db` | `mysql://root:password@localhost:3306/myapp` |
384
+ | **SQL Server** | `Server=host,port;Database=db;User Id=user;Password=pass` | `Server=localhost,1433;Database=myapp;User Id=sa;Password=secret` |
385
+ | **SQLite** | Use `path` property | `"path": "./data/app.db"` or `"path": "/var/db/app.sqlite"` |
386
+ | **Oracle** | `user/pass@host:port/service` | `admin/secret@localhost:1521/XEPDB1` |
387
+
388
+ **Additional Parameters:**
389
+
390
+ **PostgreSQL:**
391
+ ```
392
+ postgresql://user:pass@host:5432/db?sslmode=require&connect_timeout=10
393
+ ```
394
+
395
+ **MySQL:**
396
+ ```
397
+ mysql://user:pass@host:3306/db?charset=utf8mb4&timezone=Z
398
+ ```
399
+
400
+ **SQL Server:**
401
+ ```
402
+ Server=host;Database=db;User Id=user;Password=pass;Encrypt=true;TrustServerCertificate=false
403
+ ```
404
+
405
+ ---
406
+
407
+ ## MCP Client Integration
408
+
409
+ ### Configuration File Locations
410
+
411
+ | MCP Client | Configuration File Path |
412
+ |------------|------------------------|
413
+ | **Claude Desktop** (macOS) | `~/Library/Application Support/Claude/claude_desktop_config.json` |
414
+ | **Claude Desktop** (Windows) | `%APPDATA%\Claude\claude_desktop_config.json` |
415
+ | **Cline** (VS Code) | VS Code settings β†’ MCP Servers |
416
+ | **Other Clients** | Refer to client-specific documentation |
417
+
418
+ ### Setup Methods
419
+
420
+ #### Method 1: Global npm Installation
421
+
422
+ **Configuration:**
423
+ ```json
424
+ {
425
+ "mcpServers": {
426
+ "database": {
427
+ "command": "mcp-database-server",
428
+ "args": ["--config", "/absolute/path/to/.mcp-database-server.config"],
429
+ "env": {
430
+ "DATABASE_URL": "postgresql://user:pass@localhost:5432/db"
431
+ }
432
+ }
433
+ }
434
+ }
435
+ ```
436
+
437
+ #### Method 2: Source Installation
438
+
439
+ **Configuration:**
440
+ ```json
441
+ {
442
+ "mcpServers": {
443
+ "database": {
444
+ "command": "node",
445
+ "args": [
446
+ "/absolute/path/to/mcp-database-server/dist/index.js",
447
+ "--config",
448
+ "/absolute/path/to/.mcp-database-server.config"
449
+ ],
450
+ "env": {
451
+ "DATABASE_URL": "postgresql://user:pass@localhost:5432/db"
452
+ }
453
+ }
454
+ }
455
+ }
456
+ ```
457
+
458
+ ### Configuration Properties
459
+
460
+ | Property | Description | Example |
461
+ |----------|-------------|---------|
462
+ | `command` | Executable to run. Use `mcp-database-server` for npm install, `node` for source install. | `"mcp-database-server"` |
463
+ | `args` | Array of command-line arguments. First arg is usually `--config` followed by config file path. | `["--config", "/path/to/config"]` |
464
+ | `env` | Environment variables passed to the server. Used for secure credential management. | `{"DATABASE_URL": "..."}` |
465
+
466
+ **Finding Absolute Paths:**
467
+ ```bash
468
+ # macOS/Linux
469
+ cd /path/to/mcp-database-server
470
+ pwd # prints: /Users/username/projects/mcp-database-server
471
+
472
+ # Windows (PowerShell)
473
+ cd C:\path\to\mcp-database-server
474
+ $PWD.Path # prints: C:\Users\username\projects\mcp-database-server
475
+ ```
476
+
477
+ ---
478
+
479
+ ## Available MCP Tools
480
+
481
+ This server provides 9 tools for comprehensive database interaction.
482
+
483
+ ### Tool Reference
484
+
485
+ | Tool | Purpose | Write Access | Cached Data |
486
+ |------|---------|--------------|-------------|
487
+ | `list_databases` | List all configured databases with status | No | Uses cache |
488
+ | `introspect_schema` | Discover and cache database schema | No | Writes cache |
489
+ | `get_schema` | Retrieve cached schema metadata | No | Reads cache |
490
+ | `run_query` | Execute SQL queries with safety controls | Conditional* | Updates stats |
491
+ | `explain_query` | Analyze query execution plans | No | No cache |
492
+ | `suggest_joins` | Get intelligent join path recommendations | No | Uses cache |
493
+ | `clear_cache` | Clear schema cache and statistics | No | Clears cache |
494
+ | `cache_status` | View cache health and statistics | No | Reads cache |
495
+ | `health_check` | Test database connectivity | No | No cache |
496
+
497
+ <sub>* Requires `allowWrite: true` and respects security settings</sub>
498
+
499
+ ---
500
+
501
+ ### 1. list_databases
502
+
503
+ Lists all configured databases with their connection status and cache information.
504
+
505
+ **Input Parameters:**
506
+
507
+ None required.
508
+
509
+ **Response:**
510
+ ```json
511
+ [
512
+ {
513
+ "id": "postgres-main",
514
+ "type": "postgres",
515
+ "connected": true,
516
+ "cached": true,
517
+ "cacheAge": 45000,
518
+ "version": "abc123"
519
+ }
520
+ ]
521
+ ```
522
+
523
+ **Response Fields:**
524
+
525
+ | Field | Type | Description |
526
+ |-------|------|-------------|
527
+ | `id` | string | Database identifier from configuration |
528
+ | `type` | string | Database type (postgres, mysql, sqlite, mssql, oracle) |
529
+ | `connected` | boolean | Whether database connection is active |
530
+ | `cached` | boolean | Whether schema is currently cached |
531
+ | `cacheAge` | number | Age of cached schema in milliseconds (if cached) |
532
+ | `version` | string | Cache version hash (if cached) |
533
+
534
+ ---
535
+
536
+ ### 2. introspect_schema
537
+
538
+ Discovers and caches complete database schema including tables, columns, indexes, foreign keys, and relationships.
539
+
540
+ **Input Parameters:**
541
+
542
+ | Parameter | Type | Required | Description |
543
+ |-----------|------|----------|-------------|
544
+ | `dbId` | string | Yes | Database identifier to introspect |
545
+ | `forceRefresh` | boolean | No | Force re-introspection even if cache is valid (default: `false`) |
546
+ | `schemaFilter` | object | No | Filter which objects to introspect |
547
+ | `schemaFilter.includeSchemas` | string[] | No | Only introspect these schemas (PostgreSQL/SQL Server) |
548
+ | `schemaFilter.excludeSchemas` | string[] | No | Skip these schemas during introspection |
549
+ | `schemaFilter.includeViews` | boolean | No | Include database views (default: `true`) |
550
+ | `schemaFilter.maxTables` | number | No | Limit to first N tables |
551
+
552
+ **Example Request:**
553
+ ```json
554
+ {
555
+ "dbId": "postgres-main",
556
+ "forceRefresh": false,
557
+ "schemaFilter": {
558
+ "includeSchemas": ["public"],
559
+ "excludeSchemas": ["temp"],
560
+ "includeViews": true,
561
+ "maxTables": 100
562
+ }
563
+ }
564
+ ```
565
+
566
+ **Response:**
567
+ ```json
568
+ {
569
+ "dbId": "postgres-main",
570
+ "version": "a1b2c3d4",
571
+ "introspectedAt": "2026-01-26T10:00:00.000Z",
572
+ "schemas": [
573
+ {
574
+ "name": "public",
575
+ "tableCount": 15,
576
+ "viewCount": 3
577
+ }
578
+ ],
579
+ "totalTables": 15,
580
+ "totalRelationships": 12
581
+ }
582
+ ```
583
+
584
+ ---
585
+
586
+ ### 3. get_schema
587
+
588
+ Retrieves detailed schema metadata from cache without querying the database.
589
+
590
+ **Input Parameters:**
591
+
592
+ | Parameter | Type | Required | Description |
593
+ |-----------|------|----------|-------------|
594
+ | `dbId` | string | Yes | Database identifier |
595
+ | `schema` | string | No | Filter to specific schema name |
596
+ | `table` | string | No | Filter to specific table name |
597
+
598
+ **Example Request:**
599
+ ```json
600
+ {
601
+ "dbId": "postgres-main",
602
+ "schema": "public",
603
+ "table": "users"
604
+ }
605
+ ```
606
+
607
+ **Response:** Complete schema metadata including tables, columns, data types, indexes, foreign keys, and inferred relationships.
608
+
609
+ ---
610
+
611
+ ### 4. run_query
612
+
613
+ Executes SQL queries with automatic schema caching, relationship annotation, and comprehensive security controls.
614
+
615
+ **Input Parameters:**
616
+
617
+ | Parameter | Type | Required | Description |
618
+ |-----------|------|----------|-------------|
619
+ | `dbId` | string | Yes | Database identifier to query |
620
+ | `sql` | string | Yes | SQL query to execute |
621
+ | `params` | array | No | Parameterized query values (prevents SQL injection) |
622
+ | `limit` | number | No | Maximum number of rows to return |
623
+ | `timeoutMs` | number | No | Query timeout in milliseconds |
624
+
625
+ **Example Request:**
626
+ ```json
627
+ {
628
+ "dbId": "postgres-main",
629
+ "sql": "SELECT * FROM users WHERE active = $1 LIMIT $2",
630
+ "params": [true, 10],
631
+ "timeoutMs": 5000
632
+ }
633
+ ```
634
+
635
+ **Response:**
636
+ ```json
637
+ {
638
+ "rows": [
639
+ {"id": 1, "name": "Alice", "email": "alice@example.com", "active": true},
640
+ {"id": 2, "name": "Bob", "email": "bob@example.com", "active": true}
641
+ ],
642
+ "columns": ["id", "name", "email", "active"],
643
+ "rowCount": 2,
644
+ "executionTimeMs": 15,
645
+ "metadata": {
646
+ "relationships": [...],
647
+ "queryStats": {
648
+ "totalQueries": 10,
649
+ "avgExecutionTime": 20,
650
+ "errorCount": 0
651
+ }
652
+ }
653
+ }
654
+ ```
655
+
656
+ **Security Controls:**
657
+ - βœ… Write operations blocked by default (`allowWrite: false`)
658
+ - βœ… Dangerous operations (DELETE, TRUNCATE, DROP) disabled by default
659
+ - βœ… Specific operations can be whitelisted via `allowedWriteOperations`
660
+ - βœ… Per-database `readOnly` mode
661
+
662
+ ---
663
+
664
+ ### 5. explain_query
665
+
666
+ Retrieves database query execution plan without executing the query.
667
+
668
+ **Input Parameters:**
669
+
670
+ | Parameter | Type | Required | Description |
671
+ |-----------|------|----------|-------------|
672
+ | `dbId` | string | Yes | Database identifier |
673
+ | `sql` | string | Yes | SQL query to analyze |
674
+ | `params` | array | No | Query parameters (for parameterized queries) |
675
+
676
+ **Example Request:**
677
+ ```json
678
+ {
679
+ "dbId": "postgres-main",
680
+ "sql": "SELECT * FROM users JOIN orders ON users.id = orders.user_id WHERE users.active = $1",
681
+ "params": [true]
682
+ }
683
+ ```
684
+
685
+ **Response:** Database-native execution plan (format varies by database type).
686
+
687
+ ---
688
+
689
+ ### 6. suggest_joins
690
+
691
+ Analyzes relationship graph to recommend optimal join paths between multiple tables.
692
+
693
+ **Input Parameters:**
694
+
695
+ | Parameter | Type | Required | Description |
696
+ |-----------|------|----------|-------------|
697
+ | `dbId` | string | Yes | Database identifier |
698
+ | `tables` | string[] | Yes | Array of table names to join (2-10 tables) |
699
+
700
+ **Example Request:**
701
+ ```json
702
+ {
703
+ "dbId": "postgres-main",
704
+ "tables": ["users", "orders", "products"]
705
+ }
706
+ ```
707
+
708
+ **Response:**
709
+ ```json
710
+ [
711
+ {
712
+ "tables": ["users", "orders", "products"],
713
+ "joins": [
714
+ {
715
+ "fromTable": "users",
716
+ "toTable": "orders",
717
+ "relationship": {
718
+ "type": "one-to-many",
719
+ "confidence": 1.0
720
+ },
721
+ "joinCondition": "users.id = orders.user_id"
722
+ },
723
+ {
724
+ "fromTable": "orders",
725
+ "toTable": "products",
726
+ "relationship": {
727
+ "type": "many-to-one",
728
+ "confidence": 1.0
729
+ },
730
+ "joinCondition": "orders.product_id = products.id"
731
+ }
732
+ ],
733
+ "sql": "FROM users JOIN orders ON users.id = orders.user_id JOIN products ON orders.product_id = products.id"
734
+ }
735
+ ]
736
+ ```
737
+
738
+ ---
739
+
740
+ ### 7. clear_cache
741
+
742
+ Clears schema cache and query statistics for one or all databases.
743
+
744
+ **Input Parameters:**
745
+
746
+ | Parameter | Type | Required | Description |
747
+ |-----------|------|----------|-------------|
748
+ | `dbId` | string | No | Database to clear (omit to clear all) |
749
+
750
+ **Example Request:**
751
+ ```json
752
+ {
753
+ "dbId": "postgres-main"
754
+ }
755
+ ```
756
+
757
+ **Response:** Confirmation message.
758
+
759
+ ---
760
+
761
+ ### 8. cache_status
762
+
763
+ Retrieves detailed cache statistics and health information.
764
+
765
+ **Input Parameters:**
766
+
767
+ None required.
768
+
769
+ **Response:**
770
+ ```json
771
+ {
772
+ "directory": ".sql-mcp-cache",
773
+ "ttlMinutes": 10,
774
+ "databases": [
775
+ {
776
+ "dbId": "postgres-main",
777
+ "cached": true,
778
+ "version": "abc123",
779
+ "age": 120000,
780
+ "expired": false,
781
+ "tableCount": 15,
782
+ "sizeBytes": 45678
783
+ }
784
+ ]
785
+ }
786
+ ```
787
+
788
+ ---
789
+
790
+ ### 9. health_check
791
+
792
+ Tests database connectivity and returns status information.
793
+
794
+ **Input Parameters:**
795
+
796
+ | Parameter | Type | Required | Description |
797
+ |-----------|------|----------|-------------|
798
+ | `dbId` | string | No | Database to check (omit to check all) |
799
+
800
+ **Response:**
801
+ ```json
802
+ {
803
+ "databases": [
804
+ {
805
+ "dbId": "postgres-main",
806
+ "healthy": true,
807
+ "connected": true,
808
+ "version": "PostgreSQL 15.3",
809
+ "responseTimeMs": 12
810
+ }
811
+ ]
812
+ }
813
+ ```
814
+
815
+ ---
816
+
817
+ ### `cache_status`
818
+
819
+ Get cache status and statistics.
820
+
821
+ **Input:**
822
+ ```json
823
+ {
824
+ "dbId": "postgres-main"
825
+ }
826
+ ```
827
+
828
+ **Output:**
829
+ ```json
830
+ [
831
+ {
832
+ "dbId": "postgres-main",
833
+ "exists": true,
834
+ "age": 45000,
835
+ "ttlMinutes": 10,
836
+ "expired": false,
837
+ "version": "abc123",
838
+ "tableCount": 15,
839
+ "relationshipCount": 12
840
+ }
841
+ ]
842
+ ```
843
+
844
+ ### `health_check`
845
+
846
+ Check database connectivity and version information.
847
+
848
+ **Input:**
849
+ ```json
850
+ {
851
+ "dbId": "postgres-main"
852
+ }
853
+ ```
854
+
855
+ **Output:**
856
+ ```json
857
+ [
858
+ {
859
+ "dbId": "postgres-main",
860
+ "healthy": true,
861
+ "version": "PostgreSQL 15.3"
862
+ }
863
+ ]
864
+ ```
865
+
866
+ ## Resources
867
+
868
+ The server exposes cached schemas as MCP resources:
869
+
870
+ - **URI:** `schema://{dbId}`
871
+ - **MIME Type:** `application/json`
872
+ - **Content:** Complete cached schema metadata
873
+
874
+ ## Schema Introspection
875
+
876
+ ### Automatic Discovery
877
+
878
+ The server automatically discovers:
879
+
880
+ 1. **Tables and Views**: All user tables and optionally views
881
+ 2. **Columns**: Name, data type, nullability, defaults, auto-increment
882
+ 3. **Indexes**: Including primary keys and unique constraints
883
+ 4. **Foreign Keys**: Explicit relationship metadata
884
+ 5. **Relationships**: Both explicit and inferred
885
+
886
+ ### Relationship Inference
887
+
888
+ When foreign keys are not defined, the server infers relationships using heuristics:
889
+
890
+ - Column names matching `{table}_id` or `{table}Id`
891
+ - Data type compatibility with target primary key
892
+ - Confidence scoring for inferred relationships
893
+
894
+ ### Caching Strategy
895
+
896
+ - **Memory + Disk**: Dual-layer caching for performance
897
+ - **TTL-based**: Configurable time-to-live
898
+ - **Version Tracking**: Content-based versioning (hash)
899
+ - **Concurrency Safe**: Prevents duplicate introspection
900
+ - **On-Demand Refresh**: Manual or automatic refresh
901
+
902
+ ## Query Tracking
903
+
904
+ The server maintains per-database query history:
905
+
906
+ - Timestamp and SQL text
907
+ - Execution time and row count
908
+ - Referenced tables (best-effort extraction)
909
+ - Error tracking
910
+ - Aggregate statistics
911
+
912
+ Use this data to:
913
+ - Monitor query performance
914
+ - Identify frequently accessed tables
915
+ - Detect query patterns
916
+ - Debug issues
917
+
918
+ ## Development
919
+
920
+ ```bash
921
+ # Install dependencies
922
+ npm install
923
+
924
+ # Run in development mode
925
+ npm run dev
926
+
927
+ # Build
928
+ npm run build
929
+
930
+ # Run tests
931
+ npm test
932
+
933
+ # Run tests with coverage
934
+ npm run test:coverage
935
+
936
+ # Lint
937
+ npm run lint
938
+
939
+ # Format code
940
+ npm run format
941
+
942
+ # Type check
943
+ npm run typecheck
944
+ ```
945
+
946
+ ## Project Structure
947
+
948
+ ```
949
+ src/
950
+ β”œβ”€β”€ adapters/ # Database adapters
951
+ β”‚ β”œβ”€β”€ base.ts # Base adapter class
952
+ β”‚ β”œβ”€β”€ postgres.ts # PostgreSQL adapter
953
+ β”‚ β”œβ”€β”€ mysql.ts # MySQL adapter
954
+ β”‚ β”œβ”€β”€ sqlite.ts # SQLite adapter
955
+ β”‚ β”œβ”€β”€ mssql.ts # SQL Server adapter
956
+ β”‚ β”œβ”€β”€ oracle.ts # Oracle adapter (stub)
957
+ β”‚ └── index.ts # Adapter factory
958
+ β”œβ”€β”€ cache.ts # Schema caching
959
+ β”œβ”€β”€ config.ts # Configuration loader
960
+ β”œβ”€β”€ database-manager.ts # Database orchestration
961
+ β”œβ”€β”€ logger.ts # Logging setup
962
+ β”œβ”€β”€ mcp-server.ts # MCP server implementation
963
+ β”œβ”€β”€ query-tracker.ts # Query history tracking
964
+ β”œβ”€β”€ types.ts # TypeScript types
965
+ β”œβ”€β”€ utils.ts # Utility functions
966
+ └── index.ts # Entry point
967
+ ```
968
+
969
+ ## Adding New Database Adapters
970
+
971
+ 1. Implement the `DatabaseAdapter` interface in `src/adapters/`
972
+ 2. Follow the pattern from existing adapters
973
+ 3. Add to adapter factory in `src/adapters/index.ts`
974
+ 4. Update type definitions if needed
975
+ 5. Add tests
976
+
977
+ Example:
978
+
979
+ ```typescript
980
+ import { BaseAdapter } from './base.js';
981
+
982
+ export class CustomAdapter extends BaseAdapter {
983
+ async connect(): Promise<void> { /* ... */ }
984
+ async disconnect(): Promise<void> { /* ... */ }
985
+ async introspect(): Promise<DatabaseSchema> { /* ... */ }
986
+ async query(): Promise<QueryResult> { /* ... */ }
987
+ async explain(): Promise<ExplainResult> { /* ... */ }
988
+ async testConnection(): Promise<boolean> { /* ... */ }
989
+ async getVersion(): Promise<string> { /* ... */ }
990
+ }
991
+ ```
992
+
993
+ ## Troubleshooting
994
+
995
+ ### Connection Issues
996
+
997
+ - Verify connection strings and credentials
998
+ - Check network connectivity and firewall rules
999
+ - Enable debug logging: `"logging": { "level": "debug" }`
1000
+ - Use `health_check` tool to test connectivity
1001
+
1002
+ ### Cache Issues
1003
+
1004
+ - Clear cache: Use `clear_cache` tool
1005
+ - Check cache directory permissions
1006
+ - Verify TTL settings
1007
+ - Review cache status with `cache_status` tool
1008
+
1009
+ ### Performance
1010
+
1011
+ - Adjust connection pool settings
1012
+ - Use `maxTables` to limit introspection scope
1013
+ - Set appropriate cache TTL
1014
+ - Enable read-only mode when possible
1015
+
1016
+ ### Oracle Setup
1017
+
1018
+ The Oracle adapter requires additional setup:
1019
+
1020
+ 1. Install Oracle Instant Client
1021
+ 2. Set environment variables (`LD_LIBRARY_PATH` or `PATH`)
1022
+ 3. Install `oracledb` package
1023
+ 4. Implement stub methods in `src/adapters/oracle.ts`
1024
+
1025
+ ## Security Considerations
1026
+
1027
+ - Always use read-only mode in production unless write access is required
1028
+ - Use environment variables for credentials, never hardcode
1029
+ - Enable secret redaction in logs
1030
+ - Restrict write operations with `allowedWriteOperations`
1031
+ - Use connection string encryption where supported
1032
+ - Regular security audits of configurations
1033
+
1034
+ ## License
1035
+
1036
+ MIT
1037
+
1038
+ ## Contributing
1039
+
1040
+ Contributions welcome! Please:
1041
+
1042
+ 1. Fork the repository
1043
+ 2. Create a feature branch
1044
+ 3. Add tests for new functionality
1045
+ 4. Ensure all tests pass
1046
+ 5. Submit a pull request
1047
+
1048
+ ## Support
1049
+
1050
+ For issues, questions, or feature requests, please open an issue on GitHub.