database-mcp

database-mcp

Read-only Text-to-SQL MCP server for PostgreSQL and MySQL that lets users query databases using natural language, with robust multi-layer safety guarantees against writes.

Category
访问服务器

README

database-mcp

CI

Read-only Text-to-SQL over PostgreSQL and MySQL, as an MCP (Model Context Protocol) server. Ask a question in natural language in your MCP client (e.g. Claude Code); the client's LLM turns it into SQL and calls this server's tools to inspect the schema and run the query. No SQL generation happens inside this server — it only exposes safe, structured primitives (schema introspection, query execution) over stdio, and enforces that whatever SQL arrives is provably read-only before it ever touches your database.

That read-only guarantee is enforced in four independent layers, so that no single bug (in the client LLM, in this server, or in the database driver) can turn a "question" into a write:

  1. Parse-based statement allowlist — every query is parsed with node-sql-parser and only SELECT (including CTEs), EXPLAIN, SHOW, and DESCRIBE are permitted. SELECT ... INTO and INTO OUTFILE (which write data despite looking like a SELECT) are rejected, as is EXPLAIN ANALYZE in both its plain and parenthesized (EXPLAIN (ANALYZE)) forms, because it actually executes the query.
  2. Single-statement enforcement — stacked queries (SELECT 1; DROP TABLE users;) are rejected outright, so a semicolon can't smuggle in a second, unvalidated statement.
  3. Engine-level read-only transactions — every query additionally runs inside BEGIN/START TRANSACTION READ ONLY, so even a parser miss can't result in a write; the database itself refuses.
  4. Timeouts and row caps — every query has a statement timeout, and an automatic LIMIT is applied and truncation is detected (by fetching one row past the limit), preventing runaway or oversized result sets.

Errors returned by the server are structured ({ error, message }) and credential-sanitized: connection strings and passwords are never echoed back, even in raw driver error messages.

Tools

Tool Description
list_connections List the configured database connections (name, engine, description). Never returns credentials.
get_schema Compact, cached overview of an entire database: tables, columns, and foreign-key relationships.
list_tables List tables and views in a database, optionally filtered by schema.
describe_table Full detail for one table: columns, foreign keys, and indexes.
explain_query Dry-run a SQL query (EXPLAIN, nothing executes) and get warnings such as missing LIMIT or sequential scans.
run_query Execute a read-only SQL query and get back columns, rows, row count, a truncated flag, and a ready-to-display markdown table.

Install

Option A: npx from GitHub (any MCP client)

Add this to your MCP client's config (e.g. claude_desktop_config.json, or Claude Code's .mcp.json):

{
  "mcpServers": {
    "database-mcp": {
      "command": "npx",
      "args": ["-y", "github:KaushalKishorMishra/database-mcp"],
      "env": {
        "DBMCP_PROD_PG": "postgres://mcp_readonly:password@host:5432/mydb"
      }
    }
  }
}

Option B: claude mcp add (Claude Code CLI)

claude mcp add database-mcp -e DBMCP_PROD_PG=postgres://mcp_readonly:password@host:5432/mydb -- npx -y github:KaushalKishorMishra/database-mcp

Option C: Claude Code plugin

/plugin marketplace add KaushalKishorMishra/database-mcp
/plugin install database-mcp

Then set your DBMCP_<NAME> connection env vars (see Configuration below) in your shell or client environment before starting Claude Code.

Option D: Clone and build

git clone https://github.com/KaushalKishorMishra/database-mcp.git
cd database-mcp
bun install
bun run build

Then point your MCP client at the built server:

{
  "mcpServers": {
    "database-mcp": {
      "command": "node",
      "args": ["/absolute/path/to/database-mcp/dist/index.js"],
      "env": {
        "DBMCP_PROD_PG": "postgres://mcp_readonly:password@host:5432/mydb"
      }
    }
  }
}

Configuration

Connections

Each database connection is declared as an environment variable named DBMCP_<NAME>, where <NAME> becomes the connection's identifier (lowercased) that you pass to tools as the connection argument. The value is a standard connection URL; the scheme determines the engine:

Scheme Engine
postgres:// or postgresql:// PostgreSQL
mysql:// MySQL

Example:

export DBMCP_PROD_PG="postgres://mcp_readonly:password@prod-db.example.com:5432/mydb"
export DBMCP_ANALYTICS_MYSQL="mysql://mcp_readonly:password@analytics-db.example.com:3306/warehouse"

DBMCP_PROD_PG and DBMCP_ANALYTICS_MYSQL above would appear as connections named prod_pg and analytics_mysql respectively. Any DBMCP_* variable whose value isn't a recognized connection URL (and isn't one of the settings below) is skipped silently.

Settings

Env var Default Meaning
DBMCP_DEFAULT_LIMIT 100 Rows returned by run_query when no limit is given.
DBMCP_MAX_LIMIT 1000 Hard cap on limit, regardless of what's requested.
DBMCP_TIMEOUT_MS 15000 Statement timeout for every query, in milliseconds.
DBMCP_SCHEMA_CACHE_TTL_MS 300000 How long get_schema results are cached per connection, in milliseconds.
DBMCP_PREVIEW_ROWS 5 Rows returned by run_query when preview: true is set.

Safety model

1. Parse-based statement allowlist. Every incoming SQL string is parsed with node-sql-parser (dialect-aware for Postgres/MySQL) into an AST, and only SELECT statements (including CTEs), EXPLAIN, SHOW, and DESCRIBE are allowed through. The AST is walked recursively so writes hidden inside subqueries, UNION members, or CTEs are also caught — not just the top-level statement type. SELECT ... INTO and INTO OUTFILE/DUMPFILE are specifically rejected because they write despite parsing as a SELECT, and EXPLAIN ANALYZE (including the parenthesized EXPLAIN (ANALYZE) form) is rejected because it actually executes the underlying query.

2. Single-statement enforcement. Only one SQL statement is permitted per call. Stacked/batched statements (SELECT 1; DELETE FROM users;) are rejected before execution, closing off the classic SQL-injection-via-semicolon path even if the first statement alone would have passed the allowlist.

3. Engine-level read-only transactions. Independent of the parser, every query is executed inside a database-enforced read-only transaction (BEGIN/START TRANSACTION READ ONLY on Postgres, the MySQL equivalent on MySQL). This means that even if the parser has a bug or blind spot, the database itself will refuse to commit a write.

4. Timeouts and row caps. Every query runs under a statement timeout (DBMCP_TIMEOUT_MS) so a runaway query can't hang the connection, and result sets are capped by limit/DBMCP_MAX_LIMIT. Truncation is detected by fetching one row past the limit, so run_query can tell you honestly whether truncated: true and more rows exist.

Strongly recommended: use a dedicated read-only database user

Even with all four layers above, defense in depth means the database credentials you give this server should themselves only be able to read. Create a role that can SELECT but nothing else:

-- PostgreSQL
CREATE ROLE mcp_readonly LOGIN PASSWORD '...';
GRANT CONNECT ON DATABASE mydb TO mcp_readonly;
GRANT USAGE ON SCHEMA public TO mcp_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO mcp_readonly;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO mcp_readonly;
-- MySQL
CREATE USER 'mcp_readonly'@'%' IDENTIFIED BY '...';
GRANT SELECT, SHOW VIEW ON mydb.* TO 'mcp_readonly'@'%';

Example session

User: What were our top 5 customers by total order value last month?

Assistant: [calls list_connections]
           → [{ "name": "prod_pg", "engine": "postgres", ... }]

Assistant: [calls get_schema { connection: "prod_pg" }]
           → tables: customers(id, name, ...), orders(id, customer_id, total, created_at, ...)

Assistant: [calls run_query {
             connection: "prod_pg",
             sql: "SELECT c.name, SUM(o.total) AS total_value
                   FROM orders o JOIN customers c ON c.id = o.customer_id
                   WHERE o.created_at >= date_trunc('month', now()) - interval '1 month'
                     AND o.created_at < date_trunc('month', now())
                   GROUP BY c.name
                   ORDER BY total_value DESC
                   LIMIT 5",
             preview: true
           }]
           → { columns: ["name","total_value"], rows: [...], row_count: 5, truncated: false, markdown_table: "..." }

Assistant: Your top 5 customers by order value last month were:
           1. Acme Corp — $42,150
           2. Globex Inc — $38,920
           ...

Development

bun install
bun run test
bun run test:integration   # needs Docker (or podman) for testcontainers

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选