db-mcp
Provides guarded SQL access to multiple MySQL databases, with per-key scoping, SQL safety filtering, schema documentation, and support for read-only, read-write, and admin modes.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@db-mcpWhat's the schema of the orders table?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
db-mcp
Enterprise MCP server that exposes N MySQL databases to AI services through one endpoint, with API-key auth + per-key database scoping, a layered SQL guard, and a markdown schema-knowledge graph that teaches AI clients what your columns actually mean.
┌─────────────────────────────────────────────┐
Claude Code ──stdio──▶ │ db-mcp │──▶ MySQL #1 (crm, read-only)
Claude Desktop ──────▶ │ auth keys ─▶ per-key scope ─▶ SQL guard │──▶ MySQL #2 (warehouse)
Remote agents ──HTTP─▶ │ knowledge graph (markdown schema docs) │──▶ MySQL #N (ops, read-write)
Bearer <api-key> │ audit trail (JSONL) │
└─────────────────────────────────────────────┘Why this exists
One server, many databases. Each database gets a stable id, its own credentials, pool, access mode and limits — AI clients address
crmorwarehouse, never a connection string. Cross-database access is blocked; scoping is structural.Keys are capabilities. Every AI service gets its own key with a database allowlist and a mode ceiling (
read_only<read_write<admin). A key never sees databases outside its scope — not even their names.Schema ≠ semantics.
information_schemasaysstatus tinyint; the knowledge doc says1=active, 2=paused, 3=settled — soft deletes, filter deleted_at. Everydescribe_table/search_schemaresponse merges both.Defense in depth. Guard layers (below) + driver hardening + session caps + audit trail. A hostile prompt should at worst produce a polite error.
Related MCP server: MySQL MCP Server
Quick start
npm install
cp .env.yml.example .env.yml # the config — array of databases, keys, defaults
touch .env # secrets referenced from .env.yml via ${VAR}
npm run keys:generate -- --id my-agent # mint an API key (secret goes into .env)
npm run config:validate # sanity-check everything
npm run knowledge:generate # scaffold schema docs from live DBs
npm run dev # stdio | npm run dev:http for HTTPWire it into Claude Code / Desktop / Cursor / remote services: see examples/client-configs.md.
Try it instantly against the bundled demo (dockerized MySQL, two databases):
npm run e2e:up # starts MySQL on 127.0.0.1:3307 with demo schemas
npm run smoke # full end-to-end check (38 assertions)
# point a client at test/e2e/databases.e2e.json to explore the demo
npm run e2e:downConfiguration
The setup lives in .env.yml (gitignored): a YAML file declaring the
databases as an array, the API keys as an array, and global defaults. Secrets
never live in it — ${ENV_VAR} / ${ENV_VAR:-fallback} pull them from the
environment or a slim .env next to it:
auth:
keys:
- id: reporting-agent # npm run keys:generate -- --id reporting-agent
key: ${DB_MCP_KEY_REPORTING_AGENT}
databases: [crm_replica, warehouse] # or "*"
modeCap: read_only
defaults:
mode: read_only # read_only | read_write | admin
maxRows: 200
databases:
- id: crm_replica
label: CRM production replica
description: Read replica of the CRM primary (~5s lag).
connection:
host: replica.internal.example.com
user: dbmcp_readonly
password: ${CRM_DB_PASSWORD}
database: crm
tableDenylist: [_migrations, "audit_*"]
columnMasks:
users: [password_hash, remember_token]
- id: warehouse
label: Analytics warehouse
connection: { host: wh.internal, user: ro, password: ${WH_DB_PASSWORD}, database: warehouse }.env.yml.example is the complete annotated reference —
every setting, with the full per-database knob list (maxRows,
queryTimeoutMs, cellMaxChars, ssl, connectionLimit,
allowSystemSchemas, allowStoredProcedures, safeUpdates, knowledgeDir,
…). Validation stays strict: invalid values fail with the precise field,
unknown keys are rejected as probable typos, missing ${VARS} are reported
all at once, and inline passwords trigger a warning. Relative paths resolve
against the config file's directory. Also auto-discovered: .env.yaml,
config/databases.{yml,yaml,json}; or pass any path with --config /
$DB_MCP_CONFIG (an explicit file always wins).
The entire setup can also travel in flat DB_MCP_* variables — no file at
all. DB_MCP_DATABASES=crm,warehouse declares ids, then
DB_MCP_DB_<ID>_HOST/_USER/_PASSWORD/_DATABASE/... configure each one
(<ID> = id uppercased, - → _), and DB_MCP_KEYS +
DB_MCP_KEY_<ID>_SECRET/... declare the auth keys. See
.env.example for the annotated reference. Note: when
DB_MCP_DATABASES is set, env mode takes over and .env.yml is ignored
(a startup warning tells you so). JSON configs remain supported too — see
config/databases.example.json; keys starting
with _ or // are comments.
Security model
Access modes — effective mode = min(database.mode, key.modeCap):
mode | allows |
| SELECT, SHOW, DESCRIBE, EXPLAIN |
| + INSERT, UPDATE, DELETE, REPLACE (WHERE required on UPDATE/DELETE) |
| + table-level DDL (CREATE/ALTER/DROP/TRUNCATE …) |
Guard layers (all before any SQL reaches a pool):
Versioned-comment (
/*!) rejection; single-statement enforcement.Keyword scanning on a string-literal-blanked skeleton —
WHERE note = 'please INSERT'can't false-positive; keywords in comments can't hide.Always-denied statements regardless of mode:
SET,USE,GRANT,KILL,LOAD DATA, transactions, prepared statements, plugins, …Dangerous constructs:
INTO OUTFILE/DUMPFILE,LOAD_FILE,SLEEP,BENCHMARK, named locks,INTO @var.System schemas (
mysql.*,sys.*,performance_schema.*) blocked unlessallowSystemSchemas;information_schemaalways readable.AST deep checks (node-sql-parser, MySQL dialect): cross-database refs, table denylist (joins/subqueries included),
LIMITceiling, WHERE-less UPDATE/DELETE. Statements that can't be parsed run only if read-only.User/role/database-level DDL is denied even in
adminmode.
Runtime backstops (can't be bypassed by clever SQL):
multipleStatements: falseat the driver.SET SESSION TRANSACTION READ ONLYon every read-only pool connection;sql_safe_updates=1where enabled.sql_select_limit+max_execution_timeper query, hard client timeout withKILL QUERYvia a second connection, result re-capping, cell truncation, column masking.Secrets (DB passwords, API keys) are scrubbed from every error message and audit line; MySQL creds live only in env vars.
Auth: keys are compared by SHA-256 digest with timingSafeEqual;
per-IP backoff after repeated failures; HTTP refuses to start without active
keys (override: DB_MCP_ALLOW_INSECURE_HTTP=1); sessions are pinned to the
key that created them; DNS-rebinding protection is on. stdio runs under the
launching user's OS trust — keys there provide audit attribution and scoping.
MySQL-side: give db-mcp a dedicated MySQL user with least privilege
(e.g. only SELECT on replicas). The guard is a second wall, not the first.
Knowledge graph
Markdown docs under knowledge/<db-id>/ describe databases, tables, columns,
enum values, relationships and gotchas — searchable (BM25 with field boosts),
graph-connected (relates_to + links + live FKs), merged into every schema
response, and exposed as knowledge://<db>/<table> resources.
Bootstrap from the live schema, then curate:
npm run knowledge:generate # writes status: generated skeletons
$EDITOR knowledge/crm/tables/… # fill meanings, set status: documented
# curated files are never overwritten; live-vs-doc drift is reportedAuthoring guide: knowledge/README.md.
Tools
tool | purpose |
| ids, purpose, access mode, knowledge coverage, health |
| tables/views + row estimates + documented flags |
| live columns/keys/FKs merged with documented meanings/values |
| find data by meaning across knowledge + live names/comments |
| FK + documented edges, Mermaid ER snippet |
| sample rows without SQL (filters/order/limit; masked) |
| guarded SQL with |
| execution plan (traditional/json/tree) without executing |
| full doc for a table, or database overview + doc index |
| hot-reload docs from disk |
| uptime, pools, per-db health, knowledge state |
Every call is audit-logged to logs/audit.jsonl:
{ts, keyId, tool, database, sqlPreview, rowCount, durationMs, ok, error}.
Operations
npm run build # tsc -> dist/
npm start # stdio
npm run start:http # HTTP on server.http.host:port (/healthz for probes)
npm test # 113 unit tests (guard matrix, config, auth, knowledge)
npm run smoke # 38-check E2E against dockerized MySQL
docker build -t db-mcp . # container (HTTP mode; --env-file .env carries the config)Logs are structured JSON on stderr (stdout belongs to the MCP protocol).
DB_MCP_LOG_LEVEL=debug for verbosity. Graceful shutdown drains pools and
flushes the audit log on SIGINT/SIGTERM.
Layout
src/
├── index.ts CLI entry (--config/--transport/--validate-config)
├── server.ts per-session McpServer factory (auth-scoped)
├── config/ zod schema + env assembler + loader (strict validation)
├── auth/ API keys, scoping, mode ceilings
├── db/ guard (SQL policy) · manager (pools) · executor (limits/KILL)
│ introspect (information_schema) · service (glue)
├── knowledge/ parser (frontmatter+columns) · store (index/graph) · search (BM25)
├── tools/ catalog · query · knowledge · status (+ audit wrapper)
├── transport/ stdio · streamable HTTP (sessions, key auth, backoff)
└── logging/ util/ pino->stderr · audit JSONL · errors+secret scrubbing
scripts/ generate-knowledge · generate-key · smoke-client
test/ unit tests · e2e (docker MySQL + demo config)
knowledge/ your schema docs (see knowledge/README.md)Design notes
Patterns adopted from prior art: per-connection read-only sessions, KILL-on- timeout and layered SQL validation (prism-mcp); multi-database config file and row/time caps (bytebase/dbhub); per-database write gating (benborla mcp-server-mysql); markdown-bundle knowledge with frontmatter contracts, index files and link graphs (Google OKF v0.1). Deliberate divergences: policy scanning happens on a literal-blanked skeleton (no false rejections on string contents), the parser runs in MySQL dialect, per-key servers fix visibility- vs-execution permission drift, and HTTP simply refuses to run unauthenticated.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- Flicense-qualityDmaintenanceA high-performance MCP server that enables AI assistants to safely interact with MySQL databases through secure CRUD operations, schema inspection, and parameterized queries with built-in SQL injection prevention.Last updated1
- Alicense-qualityDmaintenanceA MySQL MCP server for secure database interaction, enabling schema inspection, query execution, and RBAC via AI coding assistants.Last updated4485MIT
- AlicenseAqualityAmaintenanceSecure and token-efficient MySQL MCP server built specifically for AI agents. Prevents hallucinations, optimizes context windows and blocks dangerous queries.Last updated562MIT
- Alicense-qualityDmaintenanceConfig-driven MCP server that gives AI scoped, auditable database access without exposing the entire database.Last updated146MIT
Related MCP Connectors
Self-hosted MCP gateway: turn any API, database or MCP server into AI connectors — no code.
GibsonAI MCP server: manage your databases with natural language
Butterbase MCP server — manage your backend: schemas, auth, functions, storage, RAG, deploys.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/shubhneet-walia/database-mcp-shubh'
If you have feedback or need assistance with the MCP directory API, please join our Discord server