Skip to main content
Glama
multidb-mcp

multidb-mcp

by hmrnsp

multidb-mcp

A Model Context Protocol server that gives Claude Code guarded access to PostgreSQL, MySQL/MariaDB, IBM Db2 LUW, SQLite, and Redis through one small set of tools.

Every connection declares what it may do (read, insert, update, delete, ddl, call) in a config file. A SQL guard checks each request before it reaches the database, results are truncated and masked, and every tool call is written to an audit log.

IMPORTANT

The guard is the first layer of defense, not the only one. Always connect with a database user that has only the privileges the connection needs. SeeSecurity model.

Bahasa Indonesia

Contents

Related MCP server: MCP Database Server

Features

  • One config, many databases. PostgreSQL, MySQL, MariaDB, Db2 LUW, SQLite, and Redis.

  • Explicit permissions per connection, the same rules for every environment, including production.

  • A SQL guard built on a dependency-free, dialect-aware lexer: one statement per call, permission checks, a deny-list of dangerous functions and statements, and a schema whitelist.

  • Redis allowlist of safe commands, with limits on commands that could stall the server.

  • Limits and masking: a row limit, a timeout, an output size limit, and masking of sensitive columns, keys, and fields.

  • No credentials in output: passwords and hosts are redacted from errors, logs, and tool results, and checked by a leak test.

  • Audit log with one JSON line per tool call, rotated daily.

  • Reconnects after a dropped connection (reads are retried once; writes never are).

  • Adding a database kind means writing one adapter; tools and the guard do not change.

Tools

The server only registers the tools that the configured connections need.

Tool

Purpose

Registered when

list_databases

List connections: id, driver, env, permissions, limits, status, purpose

always

list_tables

List tables and views, with an optional schema and name search

a SQL connection

describe_table

Columns, primary key, foreign keys, and indexes of one table

a SQL connection

execute_sql

Run one SQL statement; parameters are bound, never concatenated

a SQL connection

redis_command

Run one Redis command, e.g. HGETALL user:42 or SCAN 0 MATCH ...

a Redis connection

The db parameter of every tool is an enum of the connection ids from your config, so the model cannot invent a connection name. Table contents are returned labeled as data, not instructions.

Quick start

Requirements: Node.js 24 LTS and a database to connect to. Docker is needed to run Db2 (see Running with Docker) and for the development databases.

git clone https://github.com/hmrnsp/multidb-mcp.git
cd multidb-mcp
npm install
npm run build

Create databases.json next to your project (see Configuration) and a separate env file for the credentials, then register the server in the project's .mcp.json:

{
  "mcpServers": {
    "multidb": {
      "command": "node",
      "args": [
        "/absolute/path/to/multidb-mcp/dist/index.js",
        "--config",
        "databases.json",
        "--dotenv",
        ".env.multidb"
      ]
    }
  }
}

Open the project in Claude Code, approve the multidb server, and run /mcp: the server should be connected. Then ask, for example, "which databases are available?"

To try it without any setup of your own, use the development databases of this repository:

cp .env.example .env
npm run db:up          # PostgreSQL, MySQL, MariaDB, Redis in Docker
npm run seed:sqlite    # a sample SQLite file in .data/
npm run seed:redis     # sample cache/session/queue keys
npm run build

The .mcp.json of this repository already starts the server with databases.dev.json.

Configuration

A project needs three files:

File

Contents

Commit it?

.mcp.json

How Claude Code starts the server

yes

databases.json

The list of connections

yes (no secrets)

.env.multidb

Credentials, referenced from databases.json

never

{
  "defaults": { "maxRows": 200, "timeoutSec": 30, "maxChars": 40000 },
  "maskColumns": ["salary", "account_no"],
  "databases": [
    {
      "id": "local_mysql",
      "driver": "mysql",
      "env": "local",
      "access": ["read", "insert", "update", "delete", "ddl"],
      "description": "Laravel application database for development",
      "url": "${LOCAL_MYSQL_URL}"
    },
    {
      "id": "prod_db2",
      "driver": "db2",
      "env": "production",
      "access": ["read"],
      "description": "Core transaction data, for reports only",
      "url": "${PROD_DB2_CONN}",
      "maxRows": 500,
      "schemas": ["CORE", "REPORT"]
    }
  ]
}

Complete examples live in templates/.

Field

Required

Default

Meaning

id

yes

Unique id (lower-case letters, digits, _); becomes the db enum value

driver

yes

postgres, mysql, mariadb, db2, sqlite, or redis

env

yes

local, staging, or production

access

yes

Permissions, see below

description

yes

What the connection is for; the model uses it to pick a connection

url

yes

Connection string; secrets must come from ${VAR}

maxRows / timeoutSec / maxChars

no

from defaults

Limits for this connection

schemas

no

all

Schema whitelist for list, describe, and queries (not for SQLite or Redis)

lazy

no

true

Open the connection on first use

maskColumns adds to the built-in patterns (password, token, secret, nik, card_number, and others); it never replaces them.

Rules enforced when the server starts (it refuses to start otherwise, naming the problem):

  • Unknown fields are rejected, so a typo such as readOnly cannot silently do nothing.

  • A ${VAR} that is missing or empty is an error; every missing variable is reported at once.

  • A literal password in url is rejected for staging and production.

  • access must be present and contain read; unknown or repeated values are rejected.

Command line: --config <file>, --dotenv <file> (not --env-file, which Node.js intercepts), --log-level, --log-file, --audit-dir, --audit-keep-days, --no-audit. Run with --help.

Passwords inside a URL must be URL-encoded. The simplest approach is to keep the whole connection string in one variable: "url": "${PROD_PG_URL}".

Permissions (access)

Every connection must list its permissions. They are the same for every environment; there is no hidden default.

{ "id": "reports_db2", "env": "production", "access": ["read"] }
{ "id": "prod_pg",     "env": "production", "access": ["read", "update", "delete"] }
{ "id": "local_pg",    "env": "local",      "access": ["read", "insert", "update", "delete", "ddl", "call"] }

Value

Allows

read

SELECT, WITH…SELECT, VALUES, SHOW, DESCRIBE, EXPLAIN. Required

insert

INSERT

update

UPDATE, including FOR UPDATE / LOCK IN SHARE MODE

delete

DELETE

ddl

CREATE, ALTER, DROP, VACUUM, SELECT … INTO (PostgreSQL creates a new table)

call

CALL (stored procedures). Not applicable to SQLite

The guard looks for write words in the whole statement, so one statement can need several permissions:

Statement

Permissions needed

INSERT … ON CONFLICT DO UPDATE, INSERT … ON DUPLICATE KEY UPDATE

insert + update

MERGE

insert + update + delete

REPLACE (MySQL)

insert + delete

TRUNCATE

ddl + delete

WITH d AS (DELETE …) SELECT …, FINAL TABLE (INSERT …)

delete / insert

  • A ["read"] connection also uses a read-only transaction in the database (PostgreSQL, MySQL, MariaDB, SQLite). Connections that may write use a normal transaction; the guard enforces which statement kinds are allowed, and the database user's privileges remain the last layer.

  • list_databases shows each connection's permissions, and the server instructions ask Claude to explain a change and get your approval before writing to production.

  • The old readonly field is rejected at startup with a message explaining the replacement.

The SQL guard

Queries rejected by the guard never touch the database.

Rule

Read-only (["read"])

With write permissions

More than one statement

Rejected

Rejected

SELECT, WITH…SELECT, VALUES, SHOW, DESCRIBE, EXPLAIN

Allowed

Allowed

INSERT, UPDATE, DELETE, MERGE, DDL, CALL

Rejected

According to access

Hidden write words (a deleting CTE, FINAL TABLE (INSERT …))

Rejected

According to access

FOR UPDATE, LOCK IN SHARE MODE, USE AND KEEP … LOCKS

Rejected

Needs update

SELECT … INTO

Rejected

Needs ddl

GRANT, REVOKE, CREATE/ALTER/DROP USER/ROLE, DROP DATABASE/SCHEMA, ALTER SYSTEM

Rejected

Rejected

BEGIN/COMMIT/ROLLBACK, SET/RESET/USE

Rejected

Rejected

DO, EXECUTE, PREPARE, HANDLER, COPY, LOAD DATA, INTO OUTFILE

Rejected

Rejected

Dangerous functions (pg_sleep, set_config, pg_read_file, dblink, crosstab, *_to_xml, SLEEP, LOAD_FILE, ADMIN_CMD)

Rejected

Rejected

MySQL executable comments /*! … */

Rejected

Rejected

A schema-qualified table outside schemas (system catalogs stay readable)

Rejected

Rejected

SQL that cannot be analyzed (an unterminated string or comment)

Rejected

Allowed with a warning, only for env: local with every write permission

How it works:

  • The guard uses a dependency-free SQL lexer per dialect. A keyword inside a string, a comment, or a quoted identifier is not treated as a command.

  • Where the lexer could read the text differently from the server, it picks the reading that sees more code: block comments do not nest, the -- rule follows MySQL exactly, and strings with backslashes are checked in two modes.

  • A table name without a schema is limited by the connection's search_path (PostgreSQL) or default schema, not by the guard.

Column masking

The values of columns whose names match maskColumns are replaced with ***. Matching is per name segment: no_nik and password_hash are masked, technique is not.

Masking works on the result's column names, so an alias (SELECT password AS x) can get past it. For data that is truly secret, use a database user without access to that column, or expose a VIEW.

Per-database notes

PostgreSQL

  • Every query runs in its own transaction: BEGIN [READ ONLY], then statement_timeout and search_path (when schemas is set), then a cursor that stops at maxRows + 1, then ROLLBACK (read-only) or COMMIT (write permissions).

  • A connection with write permissions runs DISCARD ALL after each query, so a session SET does not carry over to the next query.

  • date, timestamp, time, and interval come back as text exactly as the database sends them, with no time zone conversion. bigint and numeric are text too, so no precision is lost.

MySQL and MariaDB

  • One adapter for both (mysql2). The driver in the config must match the server: on connect the adapter checks VERSION() and refuses a mysql connection that is really MariaDB, or the reverse, because the guard's lexer rules and the timeout mechanism differ.

  • Each query sets a session timeout (max_execution_time in ms for MySQL, max_statement_time in seconds for MariaDB), starts START TRANSACTION [READ ONLY], reads maxRows + 1 rows, then ends with ROLLBACK or COMMIT.

  • MySQL's max_execution_time only covers SELECT, so a watchdog sends KILL QUERY from a separate connection one second after the limit (for example for a slow UPDATE).

  • When a read result exceeds maxRows, the connection is dropped and the query is KILLed: closing the socket alone does not stop the query on the server. On a connection that may write, the remaining rows are still read (and discarded) so a statement such as MariaDB's DELETE … RETURNING completes before COMMIT.

  • A connection that may write is reset (COM_RESET_CONNECTION) after each query, the equivalent of PostgreSQL's DISCARD ALL.

  • Queries with params run as prepared statements (values are bound on the server and the statement is closed again afterwards).

  • When schemas is set, the default database in the connection string must be one of them.

  • Only the ssl and charset URL parameters are accepted (so ?multipleStatements=true is refused), and LOAD DATA LOCAL INFILE is disabled at the protocol level.

  • BIGINT and DECIMAL come back as text, and so do DATE, DATETIME, and TIMESTAMP.

IBM Db2 LUW

  • Db2 for i and Db2 for z/OS need a DB2 Connect license and are not supported (SQL1598N).

  • The server must run in the Docker image on most modern Linux distributions. IBM's CLI driver needs libxml2.so.2, which distributions such as Ubuntu 26.04 no longer ship. See Running with Docker.

  • Each query: prepare, SQL_ATTR_QUERY_TIMEOUT, read maxRows + 1 rows, then ROLLBACK or COMMIT. Closing the result set closes the server-side cursor.

  • Db2 does not enforce read-only transactions (the CLI read-only access mode is ignored; this was tested). A ["read"] connection is protected by the guard, an always-executed ROLLBACK, and a database user that only has SELECT. For a read-only production connection a SELECT-only user is a must; for one with write permissions, grant the database user no more than access allows.

  • When schemas is set, the adapter appends CurrentSchema=<first schema> to the connection string.

  • Unquoted names are stored upper-case. list_tables, describe_table, and the schema parameter accept lower-case: orders means ORDERS.

  • DECIMAL: ibm_db always reads DECIMAL as a double. A value below 10^(15 − scale) is restored exactly as text with the column's scale (1.50), including SUM() results. Larger values, and all DECFLOAT values, may be inexact; the result then carries a note suggesting CAST(column AS VARCHAR(40)).

  • On a query timeout ibm_db drops the SQL0952N message (an empty error). The adapter recognizes it from the elapsed time and reports a timeout.

SQLite

  • No extra dependency: it uses Node's built-in node:sqlite.

  • url looks like sqlite:<file path>. A relative path is resolved from the folder of the config file, e.g. "url": "sqlite:./database/database.sqlite". An absolute path works too.

  • The file must already exist; the adapter never creates a database. A ["read"] connection opens the file read-only, so SQLite itself refuses writes.

  • node:sqlite is synchronous: a running query cannot be stopped from inside, even with worker.terminate() (tested). So each SQLite connection runs in its own child process. A timeout kills that process; the operating system releases the file lock, SQLite rolls back the unfinished transaction, and the connection reopens on the next query.

  • Always rejected: ATTACH/DETACH, PRAGMA, VACUUM INTO, load_extension, readfile, and writefile. To inspect a table use describe_table or SELECT * FROM pragma_table_info('orders').

  • schemas is not used (a SQLite file has only the main schema).

  • SQLite is a file, not a server. To reach a file on another machine, run multidb-mcp there over SSH. Do not write to a SQLite file over a network share (SQLite's documentation warns of corruption).

Redis (redis_command)

Redis has no SQL, so a driver: "redis" connection is served by its own tool:

{ "db": "cache_prod", "command": "HGETALL", "args": ["user:42"] }
{ "db": "cache_prod", "command": "SCAN",    "args": ["0", "MATCH", "laravel_cache:*", "COUNT", "100"] }
  • url: redis://user:password@host:6379/0 (rediss:// for TLS). The database number is part of the url; a SELECT from the model is rejected.

  • access for Redis: read = read commands; insert = create a new key or element (LPUSH, SADD, XADD, SETNX); update = change a value or TTL (EXPIRE, LSET); delete = remove (DEL, HDEL, LPOP, ZREM). A command that can both create and overwrite (SET, HSET, INCR, ZADD) needs insert + update; SET … NX needs only insert, SET … XX only update. ddl and call do not apply.

  • Allowlist: only commands the guard knows can run. Always rejected, whatever the permissions: FLUSHALL, FLUSHDB, KEYS (use SCAN), EVAL/FCALL, CONFIG, ACL, MODULE, SHUTDOWN, SELECT, MULTI/EXEC, blocking commands (BLPOP, SUBSCRIBE, XREAD), MONITOR, PUBLISH, SORT.

  • Heavy commands are limited before they run, because Redis runs commands one at a time and one huge command can stall every application: HGETALL/HKEYS/HVALS/SMEMBERS are rejected when the key holds more than maxRows elements (with a hint to use HSCAN/SSCAN); LRANGE/ZRANGE are narrowed to maxRows; XRANGE gets a COUNT; the COUNT of SCAN is capped.

  • Masking: the value of a key whose name contains session, sess, csrf, otp, token, password, secret (and your maskColumns) is replaced with ***, and so are hash/stream fields with sensitive names. Sensitive key names are hidden from SCAN results, because a session id is a credential; such a key can still be deleted when the user names it.

  • A timeout only disconnects the client; Redis cannot cancel a running command, so the limits above are the main protection.

  • In production use a least-privilege ACL user. Read-only: ACL SETUSER mcp on >PASSWORD ~* &* +@read +@connection -@dangerous. May only delete cache entries: ACL SETUSER mcp_cache on >PASSWORD ~laravel_cache:* +@read +del +unlink -@dangerous.

Running with Docker

IBM's Db2 CLI driver (used by ibm_db) needs libxml2.so.2, which distributions such as Ubuntu 26.04 no longer provide. The Docker image is based on Debian 12 and removes the problem without installing anything on the host. A server started directly with node still works for every other database; only its Db2 connections report a failed status with a message that suggests Docker.

npm run docker:build        # runtime image: multidb-mcp:<version> and multidb-mcp:latest
npm run docker:build:dev    # dev image for the Db2 tests (rebuild when dependencies change)

# a local Db2 (the first start takes 5-10 minutes and needs about 4 GB of RAM)
npm run db:up:db2
npm run docker:dev -- node scripts/seed-db2.mjs
npm run docker:dev -- npm run check:db2
npm run docker:dev -- npm run test:db2

To let Claude Code run the image, use templates/mcp.docker.example.json:

docker run -i --rm --init --pull=never --network host -e TZ=Europe/Berlin \
  -v "$PWD/databases.json:/work/databases.json:ro" \
  -v "$PWD/.env.multidb:/work/.env.multidb:ro" \
  -v "$PWD/.multidb:/work/.multidb" \
  multidb-mcp:latest --config /work/databases.json --dotenv /work/.env.multidb
  • The image never contains an env file; config and credentials are mounted read-only.

  • --network host lets the container reach databases on 127.0.0.1 and on your VPN. --pull=never prevents Docker from pulling an image of the same name from a registry if yours is not built yet.

  • The container runs in UTC; pass -e TZ=<your zone> so audit files are split by your local date.

  • For SQLite, mount the folder of the database file (SQLite needs it for its -wal, -shm, and -journal files) at the same relative path under /work.

  • docker:dev mounts the source into a dev container; the runtime image needs a rebuild after code changes.

  • The Db2 driver is downloaded from IBM's mirror on GitHub at build time (version pinned in the Dockerfile), because IBM's own download server is very slow.

Using it in an application project

  1. Copy templates/databases.example.json to databases.json in the project root and adjust it.

  2. Keep credentials in a separate file such as .env.multidb, and add it to .gitignore.

  3. Copy templates/mcp.example.json (plain node) or templates/mcp.docker.example.json (Docker, needed for Db2 on newer distributions) to .mcp.json. The path to dist/index.js must be absolute.

  4. Append templates/CLAUDE.md.example to the project's CLAUDE.md and merge templates/settings.example.json into .claude/settings.json (it makes Claude Code ask before execute_sql and redis_command).

  5. Add .multidb/ (the audit log) to .gitignore.

Claude Code permissions apply per tool, not per connection, which is why production protection is enforced by the guard and not only by those permissions.

Audit log and reconnecting

Every tool call is appended as one JSON line to .multidb/audit-YYYY-MM-DD.jsonl (next to databases.json):

{"ts":"2026-11-03T09:12:44+08:00","tool":"execute_sql","db":"prod_db2","env":"production","sql":"SELECT ... FROM CORE.ORDERS WHERE ID = ?","paramCount":1,"statement":"SELECT","rows":1,"truncated":false,"masked":false,"outcome":"ok","durationMs":84}
{"ts":"2026-11-03T09:13:02+08:00","tool":"execute_sql","db":"prod_db2","env":"production","sql":"DELETE FROM CORE.ORDERS","paramCount":0,"outcome":"rejected","reason":"readonly","durationMs":0}
{"ts":"2026-11-03T09:14:10+08:00","tool":"redis_command","db":"prod_redis","env":"production","command":"DEL","keys":["laravel_session:***"],"masked":false,"outcome":"ok","durationMs":2}
  • outcome is ok, rejected (a guard rule; reason names it), or error (reason is the kind of error).

  • It never stores params values, result rows, Redis values, or passwords. SQL text is redacted, and sensitive Redis key names are masked after the last colon.

  • A new file starts every (local) day; files older than 90 days are deleted. Options: --audit-dir <folder>, --audit-keep-days <n> (0 keeps everything), --no-audit.

  • If the folder cannot be written, the server keeps running and logs audit log disabled: write failed once. With Docker, create the folder first and mount it without :ro.

When a connection that was already open drops (a database restart, a firewall closing an idle connection), reads (list_tables, describe_table, SELECT, Redis read commands) are repeated once on a fresh connection. Writes are never repeated; Claude is told the change may or may not have been saved and must check first. Failing to open the very first connection is not retried. The Db2 pool closes connections that have been idle for 30 seconds.

Security model

multidb-mcp reduces risk; it does not replace database permissions.

  • Use a dedicated database user per connection with only the privileges access grants: SELECT only for ["read"], and an ACL user for Redis. Do not use an application or DBA account.

  • Everything the database user can read can end up in the model's context. Prefer VIEWs that leave out secret columns, and grant minimal privileges; masking is an extra layer, not the main control.

  • Reach production over a VPN or an SSH tunnel; do not expose database ports publicly. SSH tunnels are not built in.

  • Table contents are data: the server's instructions tell the model to ignore commands found in results.

  • Credentials are wrapped so they print as [secret], and errors, logs, and tool results are redacted (host, port, and user names too, for text sent to the model). A protocol test checks that a password planted in the environment appears in no tool result, stderr, log file, or audit log.

  • The development passwords in docker-compose.yml and .env.example are for local containers bound to 127.0.0.1 only. Never reuse them.

To report a vulnerability, see SECURITY.md.

Development

npm install
npm run typecheck
npm run lint
npm test                    # unit tests, no database needed

npm run db:up               # PostgreSQL, MySQL, MariaDB, Redis in Docker
npm run seed:sqlite         # .data/app.sqlite
npm run seed:redis          # sample keys in Redis database 0
npm run test:integration    # adapters against the real databases
npm run test:protocol       # build, then drive the server over stdio with a real MCP client

npm run db:up:db2           # Db2 Community Edition (heavy)
npm run docker:build:dev
npm run docker:dev -- npm run test:db2

npm run check:env           # check this machine for development readiness
npm run db:down

npm 11 blocks dependency install scripts by default; ibm_db is approved in the allowScripts field of package.json, pinned to its version. After upgrading ibm_db, approve it again with npm install-scripts approve ibm_db.

src/
├─ index.ts, cli.ts, server.ts, instructions.ts   # entry point, options, tool registration
├─ config/                  # zod schema, ${VAR} interpolation, loader, Secret
├─ registry/                # connection registry (lazy connect, status per connection)
├─ adapters/                # DbAdapter / KeyValueAdapter contract and one folder per database
├─ guard/                   # SQL lexer, analyzer, policy, masking, Redis allowlist
├─ tools/                   # the MCP tools, their input schemas, and reconnect logic
├─ output/                  # compact text tables and result formatting
└─ observability/           # JSON logger, audit log, redaction
test/{unit,integration,db2,protocol}
docker/seed/                # seed SQL for the development databases
scripts/                    # seeds, Db2 check, environment check, dev-container runner
templates/                  # example .mcp.json, databases.json, CLAUDE.md, settings.json

The server speaks MCP over stdio, so stdout is reserved for the protocol: a single console.log would corrupt the connection. ESLint forbids console.* in src/; diagnostics go to stderr.

Adding a database kind means implementing DbAdapter (or KeyValueAdapter), registering it in src/adapters/index.ts, and adding a dialect to the guard if it speaks SQL. See CONTRIBUTING.md.

Troubleshooting

Symptom

Likely cause

Fix

libxml2.so.2: cannot open shared object file

A newer distribution (Ubuntu 26.04) only ships libxml2.so.16

Run the server through the Docker image; run Db2 tests with npm run docker:dev

npm install is very slow at ibm_db

IBM's download server is slow

Set IBM_DB_INSTALLER_URL=https://github.com/ibmdb/db2drivers/raw/main/clidriver/v12.1.4 first

/mcp shows failed

Invalid config or a missing variable

Run node dist/index.js --config databases.json --dotenv .env.multidb; the error is on stderr

SQLite file app.sqlite not found

Wrong path in url, or the folder is not mounted (Docker)

Relative paths start at the config file's folder; with Docker mount the database folder under /work

database is locked (SQLite)

An application is writing to the same file

Retry; raise timeoutSec if it happens often

WRONGPASS from Redis

Wrong user or password in the url

Check the variable; URL-encode special characters in the password

NOPERM from Redis

The Redis ACL user lacks the command or key

Expected for a read-only user; to allow writes, extend the ACL and access

Unable to find image 'multidb-mcp:latest'

The image is not built (--pull=never stops a download)

npm run docker:build

SQL30081N

Db2 host, port, or firewall unreachable

Check PORT (local: 55000), the VPN, and that the db2 container finished its setup

SQL1598N

The server is Db2 for i or z/OS

Not supported (needs a DB2 Connect license)

Port already in use

Ports 55432/53306/53307/55000/56379 are taken

Change the left-hand port in docker-compose.yml and in .env

Container db2 keeps restarting

Not enough Docker memory

Give Docker 4 GB of RAM or more

Seeds did not change

Database seeds only run when the volume is empty

docker compose down -v, then npm run db:up (this deletes the container data)

audit log disabled: write failed

The audit folder is not writable (common with Docker)

Create .multidb first and mount it without :ro, or use --audit-dir / --no-audit

Contributing

Contributions are welcome. Please read CONTRIBUTING.md first; it explains the workflow, the tests to run, and the rules for changes that touch the guard.

License

MIT

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    Provides Claude Desktop with secure access to multiple database connections, allowing users to query MySQL, PostgreSQL, SQLite, and SQL Server databases directly through natural language.
    -
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables Claude to connect to and interact with SQLite, SQL Server, PostgreSQL, and MySQL databases through natural language. Supports executing queries, managing tables, exporting data, and storing business insights with authentication options including AWS IAM.
    1,108 npm
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables Claude Desktop to interact with MySQL, PostgreSQL, and Redis databases using natural language for data querying and schema analysis. It provides a secure interface with a default read-only mode to prevent unauthorized database modifications.
    127 npm
    943
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables Claude Code to execute read-only queries and DML operations on Oracle and PostgreSQL databases, and to list or describe database tables.
    9 npm
    1
    MIT