About dbmcp
dbmcp is a single-binary MCP server for connecting AI assistants to MySQL, MariaDB, PostgreSQL, and SQLite databases. It provides schema and query tools, with read-only operation enabled by default and optional SSH tunneling for supported databases.
Key features
- 01MySQL, MariaDB, PostgreSQL, and SQLite support
- 02Schema discovery and SQL query tools
- 03Read-only mode enabled by default
- 04stdio and HTTP transports
- 05Embedded SSH tunneling for supported databases
Use cases
- 01Inspect database schemas and objects
- 02Run queries against supported SQL databases
- 03Connect to a database through an SSH bastion
MCP support
Exposes database discovery, query, DDL, and explain tools to MCP clients. Clients can launch it over stdio or connect to its HTTP transport.
Configuration
Local server config is the same for every client. Copy it into your client’s MCP config file.
docker run -i --rm -e HTTP_HOST -e HTTP_PORT -e HTTP_ALLOWED_ORIGINS -e HTTP_ALLOWED_HOSTS -e HTTP_AUTH_TOKEN ghcr.io/haymon-ai/dbmcp:0.15.2HTTP_HOSTHTTP bind hostHTTP_PORTHTTP bind portHTTP_ALLOWED_ORIGINSAllowed CORS origins (comma-separated)HTTP_ALLOWED_HOSTSAllowed host names (comma-separated)HTTP_AUTH_TOKENSecretBearer token required on every request; mandatory for non-loopback bindsLocal servers need a runtime on your machine (e.g. Node.js 18+). Config file locations differ by client; see your client’s docs.
ghcr.io/haymon-ai/dbmcp:0.15.2stdioStreamable HTTPTools
Local servers expose tools only once running on your machine, and the maintainer has not provided a tool list.
Details
From the project READMEDatabase MCP
A single-binary MCP server for SQL databases. Connect your AI assistant to MySQL/MariaDB, PostgreSQL, or SQLite with zero runtime dependencies.
Website · Documentation · Releases

Features ✨
- Multi-database — MySQL/MariaDB, PostgreSQL, and SQLite from one binary
- MCP tools — schema discovery (
listDatabases,listTables,listViews,listTriggers,listFunctions,listProcedures,listMaterializedViews), data access (readQuery,writeQuery), DDL (createDatabase,dropDatabase,dropTable), andexplainQuery. Read-only mode hides the write tools (writeQuery,createDatabase,dropDatabase,dropTable). See MCP Tools for per-backend availability. - Single binary — ~7 MB, no Python/Node/Docker needed
- Multiple transports — stdio (for Claude Desktop, Cursor) and HTTP (for remote/multi-client)
- Two-layer config — CLI flags > environment variables, with sensible defaults per backend
- SSH tunnel — reach MySQL/MariaDB/PostgreSQL through an SSH bastion with an embedded client: password or key auth,
known_hostsverification, automatic reconnect
Install 📦
macOS, Linux, WSL:
curl -fsSL https://dbmcp.haymon.ai/install.sh | bash
Windows PowerShell:
irm https://dbmcp.haymon.ai/install.ps1 | iex
Windows CMD:
curl -fsSL https://dbmcp.haymon.ai/install.cmd -o install.cmd && install.cmd && del install.cmd
See the installation docs for Docker, Cargo, and other methods.
Quick Start 🚀
Using .mcp.json (recommended)
Add a .mcp.json file to your project root. MCP clients read this file and configure the server automatically.
Stdio transport — the client starts and manages the server process:
{
"mcpServers": {
"dbmcp": {
"command": "dbmcp",
"args": ["stdio"],
"env": {
"DB_BACKEND": "mysql",
"DB_HOST": "127.0.0.1",
"DB_PORT": "3306",
"DB_USER": "root",
"DB_PASSWORD": "secret",
"DB_NAME": "mydb"
}
}
}
}
HTTP transport — you start the server yourself, the client connects to it:
# Start the server first
dbmcp http --db-backend mysql --db-user root --db-name mydb --port 9001
{
"mcpServers": {
"dbmcp": {
"type": "http",
"url": "http://127.0.0.1:9001/mcp"
}
}
}
Note: The
"type": "http"field is required for HTTP transport. Without it, clients like Claude Code will reject the config.
To require a bearer token, start the server with HTTP_AUTH_TOKEN and send it from the client:
HTTP_AUTH_TOKEN="$(openssl rand -hex 32)" dbmcp http --db-backend mysql --db-user root --db-name mydb
{
"mcpServers": {
"dbmcp": {
"type": "http",
"url": "http://127.0.0.1:9001/mcp",
"headers": { "Authorization": "Bearer ${DBMCP_TOKEN}" }
}
}
}
Using CLI flags
# MySQL/MariaDB
dbmcp stdio --db-backend mysql --db-host localhost --db-user root --db-name mydb
# PostgreSQL
dbmcp stdio --db-backend postgres --db-host localhost --db-user postgres --db-name mydb
# SQLite
dbmcp stdio --db-backend sqlite --db-name ./data.db
# PostgreSQL through an SSH bastion (DB host is resolved by the bastion)
dbmcp stdio --db-backend postgres --db-host db.internal --db-user postgres --db-name mydb \
--ssh-host bastion.example.com --ssh-user deploy --ssh-key ~/.ssh/id_ed25519
# HTTP transport (loopback only, no token required)
dbmcp http --db-backend mysql --db-user root --db-name mydb --port 9001
# HTTP transport on all interfaces — a bearer token is REQUIRED
HTTP_AUTH_TOKEN=change-me dbmcp http --db-backend mysql --db-user root --db-name mydb --host 0.0.0.0 --port 9001
Warning: Binding a non-loopback address (e.g.
--host 0.0.0.0) exposes the database to anyone who can reach the port.dbmcprefuses to start in that case unlessHTTP_AUTH_TOKENis set. The Host/Origin allowlists protect against browser DNS rebinding only — they are not authentication.
Using environment variables
DB_BACKEND=mysql DB_USER=root DB_NAME=mydb dbmcp stdio
Configuration ⚙️
Configuration is loaded with clear precedence:
CLI flags > environment variables > defaults
Environment variables are typically set by your MCP client (via env or envFile in the server config).
Subcommands
| Subcommand | Description |
|---|---|
stdio | Run in stdio mode |
http | Run in HTTP/SSE mode |
version | Print version information and exit |
A subcommand is required — running dbmcp with no subcommand prints usage help and exits with a non-zero status.
Database Options (shared across subcommands)
| Flag | Env Variable | Default | Description |
|---|---|---|---|
--db-backend | DB_BACKEND | (required) | mysql, mariadb, postgres, or sqlite |
--db-host | DB_HOST | localhost | Database host (as seen from the SSH server when tunnelling) |
--db-port | DB_PORT | backend default | 3306 (MySQL/MariaDB), 5432 (PostgreSQL) |
--db-user | DB_USER | backend default | root (MySQL/MariaDB), postgres (PostgreSQL) |
--db-password | DB_PASSWORD | (empty) | Database password |
--db-name | DB_NAME | (empty) | Database name or SQLite file path |
--db-charset | DB_CHARSET | Character set (MySQL/MariaDB only) |
SSL/TLS Options
| Flag | Env Variable | Default | Description |
|---|---|---|---|
--db-ssl | DB_SSL | false | Enable SSL |
--db-ssl-ca | DB_SSL_CA | CA certificate path | |
--db-ssl-cert | DB_SSL_CERT | Client certificate path | |
--db-ssl-key | DB_SSL_KEY | Client key path | |
--db-ssl-verify-cert | DB_SSL_VERIFY_CERT | true | Verify server certificate |
SSH Tunnel Options
| Flag | Env Variable | Default | Description |
|---|---|---|---|
--ssh-host | SSH_HOST | SSH server host; enables the tunnel when set | |
--ssh-port | SSH_PORT | 22 | SSH server port |
--ssh-user | SSH_USER | SSH user | |
--ssh-password | SSH_PASSWORD | SSH password | |
--ssh-key | SSH_KEY | Path to SSH private key (OpenSSH, PKCS#1, PKCS#8, SEC1) | |
--ssh-key-passphrase | SSH_KEY_PASSPHRASE | Passphrase for the SSH private key | |
--ssh-known-hosts | SSH_KNOWN_HOSTS | ~/.ssh/known_hosts | Path to known_hosts file (default: ~/.ssh/known_hosts) |
--ssh-strict-host-key-checking | SSH_STRICT_HOST_KEY_CHECKING | yes | Host key policy: yes rejects unknown hosts, accept-new trusts on first use, no disables verification (insecure) |
--ssh-keepalive-interval | SSH_KEEPALIVE_INTERVAL | 30 | SSH keepalive interval in seconds (0 disables) |
--ssh-local-port | SSH_LOCAL_PORT | ephemeral | Local tunnel port (default: ephemeral) |
The tunnel is MySQL/MariaDB/PostgreSQL only and uses an embedded SSH client (no ssh binary). --db-host/--db-port are resolved by the SSH server. With both a key and a password set, the key is tried first. See SSH Tunnel for host-key policies, limitations, and troubleshooting.
Server Options
| Flag | Env Variable | Default | Description |
|---|---|---|---|
--db-read-only | DB_READ_ONLY | true | Block write queries |
--db-max-pool-size | DB_MAX_POOL_SIZE | 5 | Max connection pool size (min: 1) |
--db-connection-timeout | DB_CONNECTION_TIMEOUT | (unset) | Connection timeout in seconds (min: 1) |
--db-query-timeout | DB_QUERY_TIMEOUT | 30 | Query execution timeout in seconds |
--db-page-size | DB_PAGE_SIZE | 100 | Max items per paginated tool response (range 1–500) |
Logging Options
| Flag | Env Variable | Default | Description |
|---|---|---|---|
--log-level | LOG_LEVEL | info | Log level (trace/debug/info/warn/error) |
HTTP-only Options (only available with http subcommand)
| Flag | Default | Description |
|---|---|---|
--host | 127.0.0.1 | Bind host |
--port | 9001 | Bind port |
--allowed-origins | localhost variants, any port | Allowed browser origins (comma-separated, :* matches any port). Drives both CORS preflight and server-side Origin rejection. |
--allowed-hosts | localhost,127.0.0.1,::1 | Trusted Host headers (comma-separated). Enforced server-side; HTTP/2 :authority is honored. |
--auth-token | (unset) | Bearer token required on every request (Authorization: Bearer <token>). Prefer the HTTP_AUTH_TOKEN env var — CLI args are visible in ps. Required for non-loopback binds. |
--dangerously-allow-unauthenticated | false | Allow a non-loopback bind without a token (HTTP_DANGEROUSLY_ALLOW_UNAUTHENTICATED). Only behind a reverse proxy that authenticates callers. |
Security note: The default
--allowed-originsaccept browser pages served from any port onlocalhost/127.0.0.1/[::1], so any local web app can call the server. On shared or development machines, setHTTP_AUTH_TOKENor narrow--allowed-originsto the explicit origins you trust (e.g.http://localhost:5173).
MCP Tools 🧩
listDatabases
Lists accessible databases, paginated via cursor / nextCursor. See Cursor Pagination for iteration details. Not available for SQLite.
listTables
Lists tables in a database, paginated via cursor / nextCursor. See Cursor Pagination for iteration details.
Parameters: database (defaults to the active database; SQLite has no database parameter), cursor, search, detailed.
search is an optional case-insensitive LIKE/ILIKE pattern with % (any sequence) and _ (single character) as wildcards — pass users% to match names beginning with users, or %order% for substring matching. A bare word with no wildcards matches only an exact table name.
detailed (default false) switches the response shape:
- Brief (default) —
tablesis a sorted JSON array of bare table-name strings. - Detailed (
detailed: true) —tablesis a JSON object keyed by table name; each value carries the table'sschema,kind,owner,comment,columns[],constraints[],indexes[], andtriggers[]. One call returns both the table list and the per-table metadata.
listViews
Lists views in a database, paginated via cursor / nextCursor. Available on MySQL/MariaDB, PostgreSQL (public schema), and SQLite. Parameters: database (defaults to the active database; SQLite has no database parameter), cursor, search, detailed. SQLite returns the brief shape only — search and detailed are not accepted there.
search is an optional case-insensitive LIKE/ILIKE pattern with % (any sequence) and _ (single character) as wildcards. The search value must remain identical across paginated calls for cursor continuity.
detailed (default false) switches the response shape:
- Brief (default) —
viewsis a sorted JSON array of bare view-name strings. View names are unique per schema, so no duplicates appear. - Detailed (
detailed: true) —viewsis a JSON object keyed by bare view name; each value carries the per-backend metadata payload. PostgreSQL exposesschema,owner,description,definition. MySQL/MariaDB exposesschema,definer,security,checkOption,updatable,characterSetClient,collationConnection,definition. See thelistViewsreference for source columns, enumerated value sets, and intentional omissions per backend.
See Cursor Pagination for iteration details.
listTriggers
Lists user-defined triggers on tables, paginated via cursor / nextCursor. Internal constraint and foreign-key triggers are excluded. Available on MySQL/MariaDB, PostgreSQL (public schema), and SQLite. Parameters: database (defaults to the active database; SQLite has no database parameter), cursor, search, detailed.
search is an optional case-insensitive LIKE/ILIKE pattern with % (any sequence) and _ (single character) as wildcards. The search value must remain identical across paginated calls for cursor continuity.
detailed (default false) switches the response shape:
- Brief (default) —
triggersis a sorted JSON array of bare trigger-name strings. - Detailed (
detailed: true) —triggersis a JSON object keyed by trigger name; each value carries the per-backend metadata payload (timing, events, definition, and backend-specific extras like PostgreSQLstatus/functionNameor MySQL/MariaDB session-context fields). See thelistTriggersreference for the full per-backend field list.
See Cursor Pagination for iteration details.
listFunctions
Lists user-defined SQL functions, paginated via cursor / nextCursor. PostgreSQL excludes aggregates, window functions, and procedures; MySQL/MariaDB excludes loadable UDFs (mysql.func). Available on MySQL/MariaDB and PostgreSQL (public schema). Not available for SQLite. Parameters: database (defaults to the active database), cursor, search, detailed.
search is an optional case-insensitive LIKE/ILIKE pattern with % (any sequence) and _ (single character) as wildcards. The search value must remain identical across paginated calls for cursor continuity.
detailed (default false) switches the response shape:
- Brief (default) —
functionsis a sorted JSON array of bare function-name strings. PostgreSQL overloads appear once per overload (duplicate name strings are expected). - Detailed (
detailed: true) —functionsis a JSON object keyed by function signature; each value carries the per-backend metadata payload (language, arguments, return type, definition, and backend-specific extras such as PostgreSQLvolatility/strict/parallelSafetyor MySQL/MariaDB session-context fields). PostgreSQL keys arename(arguments)(overloads disambiguate); MySQL/MariaDB keys are bare names (no overloading). See thelistFunctionsreference for the full per-backend field list.
See Cursor Pagination for iteration details.
listProcedures
Lists user-defined stored procedures, paginated via cursor / nextCursor. Available on MySQL/MariaDB and PostgreSQL (public schema, PostgreSQL 11+). Not available for SQLite. Parameters: database (defaults to the active database), cursor, search, detailed.
search is an optional case-insensitive LIKE/ILIKE pattern with % (any sequence) and _ (single character) as wildcards. The search value must remain identical across paginated calls for cursor continuity.
detailed (default false) switches the response shape:
- Brief (default) —
proceduresis a sorted JSON array of bare procedure-name strings. PostgreSQL overloads appear once per overload (duplicate name strings are expected). - Detailed (
detailed: true) —proceduresis a JSON object keyed by procedure signature; each value carries the per-backend metadata payload (language, arguments, security, definition, and backend-specific extras such as PostgreSQLowneror MySQL/MariaDBdeterministic/sqlDataAccess/session-context fields). PostgreSQL keys arename(arguments)(overloads disambiguate; zero-arg procedures key asname()); MySQL/MariaDB keys are bare names (no overloading). See thelistProceduresreference for the full per-backend field list.
See Cursor Pagination for iteration details.
listMaterializedViews
Lists materialized views in the public schema, paginated via cursor / nextCursor. PostgreSQL only — not available for MySQL/MariaDB or SQLite. Parameters: database (defaults to the active database), cursor, search, detailed.
search is an optional case-insensitive ILIKE pattern with % (any sequence) and _ (single character) as wildcards. SQL meta-characters (', ;, --) are bound as parameter values and never interpolated. The search value must remain identical across paginated calls for cursor continuity.
detailed (default false) switches the response shape:
- Brief (default) —
materializedViewsis a sorted JSON array of bare matview-name strings. Matview names are unique per schema, so no duplicates appear. - Detailed (
detailed: true) —materializedViewsis a JSON object keyed by bare matview name; each value carriesschema,owner,description(ornullwhen noCOMMENT ON MATERIALIZED VIEW),definition(the SELECT body verbatim frompg_matviews.definition),populated(falsefor matviews createdWITH NO DATAand never refreshed), andindexed(truewhen at least one index exists;REFRESH MATERIALIZED VIEW CONCURRENTLYadditionally requires a unique index). Detailed mode deliberately omits column metadata,tablespace, storage parameters, and unique-index detection — recoverable viadefinition,listTables(detailed=true), orreadQueryagainstpg_indexes. See thelistMaterializedViewsreference for source columns and operational semantics.
See Cursor Pagination for iteration details.
readQuery
Executes a read-only SQL query (SELECT, SHOW, DESCRIBE, USE, EXPLAIN). Always enforces SQL validation as defence-in-depth. Parameters: query, database, cursor. SELECT results paginate via cursor / nextCursor; SHOW, DESCRIBE, USE, and EXPLAIN return a single page and ignore cursor. See Cursor Pagination for iteration details.
writeQuery
Executes a write SQL query (INSERT, UPDATE, DELETE, CREATE, ALTER, DROP). Only available when read-only mode is disabled. Parameters: query, database.
createDatabase
Creates a database if it doesn't exist. Only available when read-only mode is disabled. Not available for SQLite. Parameters: database.
dropDatabase
Drops an existing database. Refuses to drop the currently connected database. Only available when read-only mode is disabled. Not available for SQLite. Parameters: database.
dropTable
Drops a table from a database. If the table has foreign key dependents, the database error is surfaced to the user. On PostgreSQL, a cascade parameter is available to force the drop with CASCADE. Only available when read-only mode is disabled. Parameters: database, table, cascade (PostgreSQL only).
explainQuery
Returns the execution plan for a SQL query. Supports an optional analyze parameter for actual execution statistics (PostgreSQL and MySQL/MariaDB). In read-only mode, EXPLAIN ANALYZE is only allowed for read-only statements since it actually executes the query. SQLite uses EXPLAIN QUERY PLAN (no ANALYZE support). Always available regardless of read-only mode. Parameters: query, database, analyze (PostgreSQL/MySQL only).
Security 🔒
- Read-only mode (default) — write tools hidden from AI assistant;
readQueryenforces AST-based SQL validation - Single-statement enforcement — multi-statement injection blocked at parse level
- Dangerous function blocking —
LOAD_FILE(),INTO OUTFILE,INTO DUMPFILEdetected in the AST - Identifier validation — database/table names validated against control characters and empty strings
- HTTP bearer authentication —
HTTP_AUTH_TOKENenforcesAuthorization: Bearer <token>(constant-time compare, 401 on failure); non-loopback binds refuse to start without a token - Origin + Host allowlists — server-side rejection (403) plus CORS preflight; configurable for HTTP transport. Defends against browser DNS rebinding — not a substitute for authentication
- SSL/TLS — configured via individual
DB_SSL_*variables - SSH host-key verification — the SSH tunnel checks the server key against
known_hosts(yesby default); SSH passwords and passphrases are never logged - PII redaction (opt-in, off by default) — when enabled, query tool output passes through a regex-based redactor that rewrites detected PII spans across 46 built-in entity types spanning seven categories: personal (email), financial (cards, IBAN, UK bank accounts, sort and US ABA routing codes, CVV), government IDs (SSN, ITIN, EIN, UK/US passports, NHS, NINO, SIN, VAT), contact (phone), network (IP, URL, MAC), digital identity (API keys, JWTs, PEM private keys, password hashes), and crypto wallets. Toggle:
--pii/PII_ENABLE. Operator:--pii-operator/PII_OPERATOR— one ofreplace(default, entity-aware placeholders like<EMAIL_ADDRESS>),mask(length-preserving*),redact(drop),hash(SHA-256 hex). Optional subset via--pii-categories/PII_CATEGORIES(comma-separated, e.g.financial,government); unset enables all built-ins. Scope: query tool output payloads only. See PII configuration for the full surface. - ML/NER redaction (opt-in at runtime, off by default) — adds
PERSON,LOCATION,ORGANIZATION,NATIONALITY_RELIGION_POLITICS, andFACILITYdetection that regex cannot catch, enabled via the--pii-ner/PII_NER_ENABLEtoggle plus a user-supplied model directory. Which entities are produced depends on the model's labels (CoNLL models give person/location/organization; OntoNotes-class models add NRP and facility). Inference uses ONNX Runtime (model directory holdsconfig.json,tokenizer.json,model.onnx; recommended: the MIT-licenseddslim/bert-base-NERexported to ONNX, int8-quantized for speed). Fail-closed: a model that cannot load aborts startup and an inference error fails the request — never a silent fallback. Respects--pii-categories. English for v1. - Credential redaction — database password is never shown in logs or debug output
Testing 🧪
# Unit tests
cargo test --workspace --lib --bins
# Integration tests (requires Docker)
./tests/run.sh
# Filter by engine
./tests/run.sh --filter mariadb
./tests/run.sh --filter mysql
./tests/run.sh --filter postgres
./tests/run.sh --filter sqlite
# With MCP Inspector
npx @modelcontextprotocol/inspector ./target/release/dbmcp stdio
# HTTP mode testing
curl -X POST http://localhost:9001/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"0.1"}}}'
Project Structure 🗂️
This is a Cargo workspace with the following crates:
| Crate | Path | Description |
|---|---|---|
dbmcp | . (root) | Binary shim |
dbmcp-cli | crates/cli/ | CLI parsing, transports, server bootstrap |
dbmcp-sql | crates/sql/ | Shared error types, validation, and identifier utilities |
dbmcp-config | crates/config/ | Configuration structs and validation |
dbmcp-server | crates/server/ | Shared MCP tool implementations and server info |
dbmcp-mysql | crates/mysql/ | MySQL/MariaDB backend handler and operations |
dbmcp-postgres | crates/postgres/ | PostgreSQL backend handler and operations |
dbmcp-sqlite | crates/sqlite/ | SQLite backend handler and operations |
dbmcp-pii | crates/pii/ | PII detection and anonymisation of query output |
dbmcp-sql-lineage | crates/sql-lineage/ | Column-level SQL lineage extraction |
dbmcp-ssh | crates/ssh/ | SSH tunnel for database connections |
sqlx-json | crates/sqlx-json/ | Type-safe row-to-JSON conversion for sqlx (RowExt trait) |
Development 🧰
cargo build # Development build
cargo build --release # Release build (~7 MB)
cargo test # Run tests
cargo clippy --workspace --tests -- -D warnings # Lint
cargo fmt # Format
cargo doc --no-deps # Build documentation
License 📄
This project is licensed under the MIT License — see the LICENSE file for details.
Comments
New to MCP?
From what the protocol is to installing your first server, these answers cover most first questions.
An open protocol that defines how AI applications connect to external tools and data in a uniform way. Clients talk to the model; MCP servers provide tools, resources and prompt templates. They exchange standard messages, so one server can be reused by many clients.