Marking a connection read-only blocks writes through both the query and execute tools and sets the engine's own read-only transaction mode on PostgreSQL and MySQL, with SQLite opened in read-only mode. Combined with max_rows truncation that says so explicitly and column masking by regex on result column names, this is the rare database bridge you can point at something that matters.
A server that connects to multiple databases concurrently and generates a set of tools per connection, so each database gets its own query, execute, transaction, schema and performance surface.
- query_<db_id>, execute_<db_id> and transaction_<db_id> — SELECTs, data modification, and begin/commit/rollback per database
- schema_<db_id> and generate_schema_<db_id> — tables, columns, indexes and foreign keys, and SQL or code generated from them
- explain_<db_id> and describe_<db_id> — execution plans without running the statement, and one table's columns, indexes and row estimate
- performance_<db_id> — slow-query stats, static SQL lint, index suggestions, and on PostgreSQL a hypopg-backed check of whether the planner would actually use each suggested index
- health_<db_id> — connectivity, ping latency, pool state, and engine cache statistics
- TimescaleDB extras on PostgreSQL databases that have the extension: time-bucketed queries, hypertable and continuous-aggregate listings, compression and retention settings
- A unified tool mode that collapses the per-database naming into one set taking a database parameter, for when 5+ connections blow past a client's tool limit
- Lazy loading, so connections are only established on first use
A JSON connections file listing each database with an id, type, host, port, name, user and password. Oracle also accepts TNS entries and wallets. Per-database guardrails are set in the same file: read_only, max_rows, query_timeout and masking_rules. Set DB_MCP_AUDIT_LOG to a path for a JSONL record of every executed statement.
