mcp-database-server

by iprabhu

Not rated
GitHub

About

Production-grade Model Context Protocol (MCP) server for unified SQL database access. Connect multiple databases through a single MCP server with schema discovery, relationship mapping, caching, and safety controls.

Details

Author
iprabhu
Categories
Database, Other

Setup

Install mcp-database-server in your MCP client (Claude Desktop, Cursor, Windsurf, and others).

Repository: https://github.com/iprabhu/mcp-database-server

Follow the installation instructions in the repository README, then restart your MCP client.

Production-grade Model Context Protocol (MCP) server for unified SQL database access. Connect multiple databases through a single MCP server with schema discovery, relationship mapping, caching, and safety controls.

Production-grade Model Context Protocol (MCP) server for unified SQL database access. Connect multiple databases through a single MCP server with schema discovery, relationship mapping, caching, and safety controls.

- npm:https://www.npmjs.com/package/@adevguide/mcp-database-server
- GitHub:
https://github.com/iPraBhu/mcp-database-server

- Features
-
Why this exists
-
Installation
-
Configuration
-
MCP client integration

- Multi-database support: PostgreSQL, MySQL/MariaDB, SQLite, SQL Server, Oracle
- Automatic schema discovery: tables, columns, indexes, foreign keys, relationships
- Persistent schema caching: TTL + versioning, manual refresh, cache stats
- Relationship inference: foreign keys + heuristics
- Query intelligence: tracking, statistics, timeouts
- Join assistance: suggested join paths based on relationship graphs
- Safety controls: read-only mode, allow/deny write operations, secret redaction
- Query optimization: index recommendations, performance profiling, slow query detection
- Performance monitoring: detailed execution analytics, bottleneck identification
- Query rewriting: automated optimization suggestions with performance impact estimates

This project was originally vibe-coded to solve real issues I was facing when wiring LLM tools to multiple SQL databases (consistent connectivity, schema discovery, and safe query execution). It has since been hardened into a reusable MCP server with caching and security defaults.

┌─────────────────────────────────────────────────────────┐ │ MCP Client │ │ (Claude Desktop, IDEs, etc.) │ └────────────────┬────────────────────────────────────────┘ │ JSON-RPC over stdio ┌────────────────▼────────────────────────────────────────┐ │ MCP Database Server │ │ ┌──────────────────────────────────────────────────┐ │ │ │ Schema Cache (TTL + Versioning) │ │ │ └──────────────────────────────────────────────────┘ │ │ ┌──────────────────────────────────────────────────┐ │ │ │ Query Tracker (History + Statistics) │ │ │ └──────────────────────────────────────────────────┘ │ │ ┌──────────────────────────────────────────────────┐ │ │ │ Security Layer (Read-only, Operation Controls) │ │ │ └──────────────────────────────────────────────────┘ │ └────┬─────────┬─────────┬──────────┬──────────┬─────────┘ │ │ │ │ │ ┌────▼───┐ ┌──▼────┐ ┌──▼─────┐ ┌──▼──────┐ ┌▼────────┐ │Postgres│ │ MySQL │ │ SQLite │ │ MSSQL │ │ Oracle │ └────────┘ └───────┘ └────────┘ └─────────┘ └─────────┘
npm install -g @adevguide/mcp-database-server
mcp-database-server --config /absolute/path/to/.mcp-database-server.config
npx -y @adevguide/mcp-database-server --config /absolute/path/to/.mcp-database-server.config
git clone https://github.com/iPraBhu/mcp-database-server.git cd mcp-database-server npm install npm run build node dist/index.js --config ./.mcp-database-server.config

Create a.mcp-database-server.configfile in your project root:

Note:The config file is automatically discovered inside the current project tree. If you don't specify--config, the tool searches upward from the current directory until it reaches the detected project root (for example a directory containingpackage.jsonor.git). It does not continue past the project root. If you usecredentialCommand, pass--configexplicitly.

{ "databases": [ { "id": "postgres-main", "type": "postgres", "secretRef": "DB_URL_POSTGRES", "readOnly": true, "pool": { "min": 2, "max": 10, "idleTimeoutMillis": 30000 }, "introspection": { "includeViews": true, "excludeSchemas": ["pg_catalog"] } }, { "id": "mariadb-reporting", "type": "mysql", "secretRef": "DB_URL_MARIADB", "readOnly": true, "pool": { "min": 1, "max": 5 } }, { "id": "sqlite-local", "type": "sqlite", "path": "./data/app.db" } ], "cache": { "directory": ".sql-mcp-cache", "ttlMinutes": 10 }, "security": { "allowWrite": false, "allowedWriteOperations": ["INSERT", "UPDATE"], "disableDangerousOperations": true, "redactSecrets": true }, "logging": { "level": "info", "pretty": false } }

Each database in thedatabasesarray represents a connection to a SQL database.

Required for postgres, mysql, mssql, oracle
Required for sqlite only

PostgreSQL: postgresql://username:password@host:5432/database MySQL: mysql://username:password@host:3306/database SQL Server: Server=host,1433;Database=dbname;User Id=user;Password=pass SQLite: (use path property instead) Oracle: username/password@host:1521/servicename

Thepoolobject controls connection pooling behavior. Improves performance by reusing database connections.

- Development:min: 1,max: 5
-
Production (Low Traffic):min: 2,max: 10
-
Production (High Traffic):min: 5,max: 20

Theintrospectionobject controls schema discovery behavior. Determines what database objects are analyzed.

- PostgreSQL/SQL Server:Support multiple schemas per database. UseincludeSchemas/excludeSchemas.
-
MySQL/MariaDB:Schema = database. Use database name in connection string.
-
SQLite:Single-file database, no schema concept.

Controls schema metadata caching to improve startup performance and reduce database load.

- On Startup:Loads schema from cache if available and not expired
-
After TTL Expiry:Next query triggers automatic re-introspection
-
Manual Refresh:Useclear_cachetool orintrospect_schemawithforceRefresh: true
-
Cache Files:Stored as{database-id}.json(e.g.,postgres-main.json)

- Development:5minutes (schema changes frequently)
-
Staging:30-60minutes
-
Production (Static):1440minutes (24 hours)
-
Production (Active):60-240minutes (1-4 hours)

Comprehensive security controls to protect your databases from unauthorized or dangerous operations.
-
Database-levelreadOnly→ Blocks all writes for specific database
-
GlobalallowWrite→ Master switch for all databases
-
disableDangerousOperations→ Blocks DELETE/TRUNCATE/DROP specifically
-
allowedWriteOperations→ Whitelist of permitted operations

// Read-only access (default - safest) { "allowWrite": false } // Allow INSERT and UPDATE only (no deletes) { "allowWrite": true, "allowedWriteOperations": ["INSERT", "UPDATE"], "disableDangerousOperations": true } // Full write access (development only - dangerous!) { "allowWrite": true, "disableDangerousOperations": false }

Controls log output verbosity and formatting.

- trace:Everything (extremely verbose - use for debugging only)
-
debug:Detailed diagnostic information
-
info:General informational messages (recommended for production)
-
warn:Warning messages that don't prevent operation
-
error:Error messages only

- Development:level: "debug",pretty: true
-
Production:level: "info",pretty: false
-
Troubleshooting:level: "trace",pretty: true

{ "databases": [ { "id": "postgres-production", "type": "postgres", "url": "${DATABASE_URL}", "readOnly": true, "pool": { "min": 5, "max": 20, "idleTimeoutMillis": 60000, "connectionTimeoutMillis": 5000 }, "introspection": { "includeViews": true, "includeRoutines": false, "excludeSchemas": ["pg_catalog", "information_schema"] }, "eagerConnect": true }, { "id": "mysql-analytics", "type": "mysql", "url": "${MYSQL_URL}", "readOnly": true, "pool": { "min": 2, "max": 10 }, "introspection": { "includeViews": true, "maxTables": 100 } }, { "id": "sqlite-local", "type": "sqlite", "path": "./data/app.db", "readOnly": true } ], "cache": { "directory": ".sql-mcp-cache", "ttlMinutes": 60 }, "security": { "allowWrite": false, "allowedWriteOperations": ["INSERT", "UPDATE"], "disableDangerousOperations": true, "redactSecrets": true }, "logging": { "level": "info", "pretty": false } }

Keep secrets out of the MCP client config and out of the server config values themselves.

{ "databases": [ { "id": "production-db", "type": "postgres", "secretRef": "DATABASE_URL" } ] }

The server resolvessecretReffrom the process environment first, and then from a.envfile next to.mcp-database-server.config.

DATABASE_URL=postgresql://user:password@localhost:5432/dbname DB_URL_MYSQL=mysql://user:password@localhost:3306/dbname DB_URL_MARIADB=mysql://report_user:password@mariadb.local:3306/reporting DB_URL_MSSQL=Server=host,1433;Database=db;User Id=sa;Password=pass
{ "databases": [ { "id": "analytics-db", "type": "mysql", "credentialCommand": "op read op://analytics/mysql/url" } ] }

The command must print only the connection string to stdout. For safety,credentialCommandis only allowed when the server is launched with an explicit--configpath. Auto-discovered configs cannot execute credential commands.

Still Supported: direct env interpolation

You can still write"url": "${DATABASE_URL}", butsecretRefis the cleaner option because it makes the secret source explicit.

- ✅ Store.envfile outside version control (add to.gitignore)
- ✅ Use different.envfiles for each environment (dev, staging, prod)
- ✅ Never commit credentials to git repositories
- ✅ Use secret management services (AWS Secrets Manager, HashiCorp Vault) in production

postgresql://user:pass@host:5432/db?sslmode=require&connect_timeout=10
mysql://user:pass@host:3306/db?charset=utf8mb4&timezone=Z
mysql://user:pass@host:3306/db?charset=utf8mb4
Server=host;Database=db;User Id=user;Password=pass;Encrypt=true;TrustServerCertificate=false
{ "mcpServers": { "database": { "command": "mcp-database-server", "args": ["--config", "/absolute/path/to/.mcp-database-server.config"] } } }
{ "mcpServers": { "database": { "command": "node", "args": [ "/absolute/path/to/mcp-database-server/dist/index.js", "--config", "/absolute/path/to/.mcp-database-server.config" ] } } }
# macOS/Linux cd /path/to/mcp-database-server pwd # prints: /Users/username/projects/mcp-database-server # Windows (PowerShell) cd C:\path\to\mcp-database-server $PWD.Path # prints: C:\Users\username\projects\mcp-database-server

This server provides 15 tools for comprehensive database interaction and optimization.

RequiresallowWrite: trueand respects security settings

Lists all configured databases with their connection status and cache information.

[ { "id": "postgres-main", "type": "postgres", "connected": true, "cached": true, "cacheAge": 45000, "version": "abc123" } ]

Discovers and caches complete database schema including tables, columns, indexes, foreign keys, and relationships.

{ "dbId": "postgres-main", "forceRefresh": false, "schemaFilter": { "includeSchemas": ["public"], "excludeSchemas": ["temp"], "includeViews": true, "maxTables": 100 } }
{ "dbId": "postgres-main", "version": "a1b2c3d4", "introspectedAt": "2026-01-26T10:00:00.000Z", "schemas": [ { "name": "public", "tableCount": 15, "viewCount": 3 } ], "totalTables": 15, "totalRelationships": 12 }

Retrieves detailed schema metadata from cache without querying the database.

{ "dbId": "postgres-main", "schema": "public", "table": "users" }

Response:Complete schema metadata including tables, columns, data types, indexes, foreign keys, and inferred relationships.

Executes SQL queries with automatic schema caching, relationship annotation, and comprehensive security controls.

{ "dbId": "postgres-main", "sql": "SELECT  FROM users WHERE active = $1 ORDER BY id", "params": [true], "limit": 10, "offset": 0, "maxBytes": 32768, "includeMetadata": false, "trackQuery": false, "timeoutMs": 5000 }
{ "rows": [ {"id": 1, "name": "Alice", "email": "alice@example.com", "active": true}, {"id": 2, "name": "Bob", "email": "bob@example.com", "active": true} ], "columns": ["id", "name", "email", "active"], "rowCount": 2, "executionTimeMs": 15, "metadata": { "relationships": [...], "queryStats": { "totalQueries": 10, "avgExecutionTime": 20, "errorCount": 0 }, "pagination": { "limit": 10, "offset": 0, "hasMore": true, "nextOffset": 10 }, "responseSize": { "maxBytes": 32768, "rowsBytes": 1842, "rowsTrimmed": false, "omittedRowCount": 0 } } }

For the fastest MariaDB/MySQL read path, set"includeMetadata": falseand"trackQuery": falsewhen you only need result rows and do not need relationship annotations, query history, or performance analytics for that request.

- ✅ Write operations blocked by default (allowWrite: false)
- ✅ Dangerous operations (DELETE, TRUNCATE, DROP) disabled by default
- ✅ Specific operations can be whitelisted viaallowedWriteOperations
- ✅ Per-databasereadOnlymode

Retrieves database query execution plan without executing the query.

{ "dbId": "postgres-main", "sql": "SELECT  FROM users JOIN orders ON users.id = orders.user_id WHERE users.active = $1", "params": [true] }

Response:**Database-native execution plan (format varies by database type).

Exports large read-only query results to a local file under.sql-mcp-cache/exports.

- MySQL/MariaDB uses adapter-level row streaming to avoid loading the full result set into memory.
- PostgreSQL and SQLite use paged export by rewriting top-levelLIMIT/OFFSETwindows.
- SQL Server export requires a future adapter-specific streaming path and will currently fail unless paging rewrite is supported.

{ "dbId": "mariadb-reporting", "sql": "SELECT id, email, created_at FROM users ORDER BY id", "format": "jsonl", "fileName": "users-export.jsonl", "timeoutMs": 10000 }
{ "dbId": "mariadb-reporting", "outputPath": "/absolute/path/to/.sql-mcp-cache/exports/users-export.jsonl", "format": "jsonl", "strategy": "stream", "rowsExported": 250000, "columns": ["id", "email", "created_at"], "fileSizeBytes": 18342011, "executionTimeMs": 8421 }

Analyzes relationship graph to recommend optimal join paths between multiple tables.

{ "dbId": "postgres-main", "tables": ["users", "orders", "products"] }
[ { "tables": ["users", "orders", "products"], "joins": [ { "fromTable": "users", "toTable": "orders", "relationship": { "type": "one-to-many", "confidence": 1.0 }, "joinCondition": "users.id = orders.user_id" }, { "fromTable": "orders", "toTable": "products", "relationship": { "type": "many-to-one", "confidence": 1.0 }, "joinCondition": "orders.product_id = products.id" } ], "sql": "FROM users JOIN orders ON users.id = orders.user_id JOIN products ON orders.product_id = products.id" } ]

Clears schema cache and query statistics for one or all databases.

{ "dbId": "postgres-main" }

Retrieves detailed cache statistics and health information.

{ "directory": ".sql-mcp-cache", "ttlMinutes": 10, "databases": [ { "dbId": "postgres-main", "cached": true, "version": "abc123", "age": 120000, "expired": false, "tableCount": 15, "sizeBytes": 45678 } ] }

Tests database connectivity and returns status information.

{ "databases": [ { "dbId": "postgres-main", "healthy": true, "connected": true, "version": "PostgreSQL 15.3", "responseTimeMs": 12 } ] }

Get comprehensive performance analytics across all queries for a database.

No reviews yet — be the first

Sign in to leave a review

Use Google, GitHub, or an email account so ratings stay tied to real people.

Email sign in

No reviews posted yet.