A fast, read-only MySQL Server for the Model Context Protocol (MCP) written in Go.
This project exposes safe MySQL introspection tools to Claude Desktop via MCP. Claude can explore databases, describe schemas, and execute controlled read-only SQL queries — ideal for secure development assistance, debugging, analytics, and schema documentation.
- Fully read-only (blocks all non-SELECT/SHOW/DESCRIBE/EXPLAIN)
- Multi-DSN Support: Connect to multiple MySQL instances, switch via tool
- Vector Search (MySQL 9.0+): Similarity search on vector columns
- MCP tools:
- list_databases, list_tables, describe_table
- run_query (safe and row-limited)
- ping, server_info
- list_connections, use_connection (multi-DSN)
- vector_search, vector_info (MySQL 9.0+)
- Supports MySQL 8.0, 8.4, 9.0+
- Query timeouts, structured logging, audit logs
- Single Go binary
- Unit and integration tests (Testcontainers)
- Native integration with Claude Desktop MCP
brew install askdba/tap/mysql-mcp-serverdocker pull ghcr.io/askdba/mysql-mcp-server:latestDownload the latest release from GitHub Releases.
Available for:
- macOS (Intel & Apple Silicon)
- Linux (amd64 & arm64)
- Windows (amd64)
git clone https://github.com/askdba/mysql-mcp-server.git
cd mysql-mcp-server
make buildBinary output: bin/mysql-mcp-server
Run the interactive setup script:
./scripts/quickstart.shThis will:
- Test your MySQL connection
- Optionally create a read-only MCP user
- Generate your Claude Desktop configuration
- Optionally load a test dataset
Environment variables:
| Variable | Required | Default | Description |
|---|---|---|---|
| MYSQL_DSN | Yes | – | MySQL DSN |
| MYSQL_MAX_ROWS | No | 200 | Max rows returned |
| MYSQL_QUERY_TIMEOUT_SECONDS | No | 30 | Query timeout |
| MYSQL_MCP_EXTENDED | No | 0 | Enable extended tools (set to 1) |
| MYSQL_MCP_JSON_LOGS | No | 0 | Enable JSON structured logging (set to 1) |
| MYSQL_MCP_AUDIT_LOG | No | – | Path to audit log file |
| MYSQL_MCP_VECTOR | No | 0 | Enable vector tools for MySQL 9.0+ (set to 1) |
| MYSQL_MCP_HTTP | No | 0 | Enable REST API mode (set to 1) |
| MYSQL_HTTP_PORT | No | 9306 | HTTP port for REST API mode |
| MYSQL_HTTP_RATE_LIMIT | No | 0 | Enable rate limiting for HTTP mode (set to 1) |
| MYSQL_HTTP_RATE_LIMIT_RPS | No | 100 | Rate limit: requests per second |
| MYSQL_HTTP_RATE_LIMIT_BURST | No | 200 | Rate limit: burst size |
| MYSQL_MAX_OPEN_CONNS | No | 10 | Max open database connections |
| MYSQL_MAX_IDLE_CONNS | No | 5 | Max idle database connections |
| MYSQL_CONN_MAX_LIFETIME_MINUTES | No | 30 | Connection max lifetime in minutes |
Configure multiple MySQL connections using numbered environment variables:
# Default connection
export MYSQL_DSN="user:pass@tcp(localhost:3306)/db1?parseTime=true"
# Additional connections
export MYSQL_DSN_1="user:pass@tcp(prod-server:3306)/production?parseTime=true"
export MYSQL_DSN_1_NAME="production"
export MYSQL_DSN_1_DESC="Production database"
export MYSQL_DSN_2="user:pass@tcp(staging:3306)/staging?parseTime=true"
export MYSQL_DSN_2_NAME="staging"
export MYSQL_DSN_2_DESC="Staging database"Or use JSON configuration:
export MYSQL_CONNECTIONS='[
{"name": "production", "dsn": "user:pass@tcp(prod:3306)/db?parseTime=true", "description": "Production"},
{"name": "staging", "dsn": "user:pass@tcp(staging:3306)/db?parseTime=true", "description": "Staging"}
]'Example:
export MYSQL_DSN="root:password@tcp(127.0.0.1:3306)/mysql?parseTime=true"
export MYSQL_MAX_ROWS=200
export MYSQL_QUERY_TIMEOUT_SECONDS=30Run:
make runCheck the installed version:
mysql-mcp-server --versionOutput:
mysql-mcp-server v1.1.0
Build time: 2025-01-15T10:30:00Z
Git commit: abc1234
Edit:
~/Library/Application Support/Claude/claude_desktop_config.json
Add:
{
"mcpServers": {
"mysql": {
"command": "/absolute/path/to/bin/mysql-mcp-server",
"env": {
"MYSQL_DSN": "root:password@tcp(127.0.0.1:3306)/mysql?parseTime=true",
"MYSQL_MAX_ROWS": "200"
}
}
}
}Restart Claude Desktop.
Returns non-system databases.
Input:
{ "database": "employees" }Input:
{ "database": "employees", "table": "salaries" }Input:
{ "sql": "SELECT id, name FROM users LIMIT 5" }Optional database context:
{ "sql": "SELECT * FROM users LIMIT 5", "database": "myapp" }- Rejects non-read-only SQL
- Enforces row limit
- Enforces timeout
Tests database connectivity and returns latency.
Output:
{ "success": true, "latency_ms": 2, "message": "pong" }Returns MySQL server details.
Output:
{
"version": "8.0.36",
"version_comment": "MySQL Community Server - GPL",
"uptime_seconds": 86400,
"current_database": "myapp",
"current_user": "mcp@localhost",
"character_set": "utf8mb4",
"collation": "utf8mb4_0900_ai_ci",
"max_connections": 151,
"threads_connected": 5
}List all configured MySQL connections.
Output:
{
"connections": [
{"name": "production", "dsn": "user:****@tcp(prod:3306)/db", "active": true},
{"name": "staging", "dsn": "user:****@tcp(staging:3306)/db", "active": false}
],
"active": "production"
}Switch to a different MySQL connection.
Input:
{ "name": "staging" }Output:
{
"success": true,
"active": "staging",
"message": "Switched to connection 'staging'",
"database": "staging_db"
}Enable with:
export MYSQL_MCP_VECTOR=1Perform similarity search on vector columns.
Input:
{
"database": "myapp",
"table": "embeddings",
"column": "embedding",
"query": [0.1, 0.2, 0.3, ...],
"limit": 10,
"select": "id, title, content",
"distance_func": "cosine"
}Output:
{
"results": [
{"distance": 0.123, "data": {"id": 1, "title": "Doc 1", "content": "..."}},
{"distance": 0.456, "data": {"id": 2, "title": "Doc 2", "content": "..."}}
],
"count": 2
}Distance functions: cosine (default), euclidean, dot
List vector columns in a database.
Input:
{ "database": "myapp" }Output:
{
"columns": [
{"table": "embeddings", "column": "embedding", "dimensions": 768, "index_name": "vec_idx"}
],
"vector_support": true,
"mysql_version": "9.0.0"
}Enable with:
export MYSQL_MCP_EXTENDED=1List indexes on a table.
{ "database": "myapp", "table": "users" }Get the CREATE TABLE statement.
{ "database": "myapp", "table": "users" }Get execution plan for a SELECT query.
{ "sql": "SELECT * FROM users WHERE id = 1", "database": "myapp" }List views in a database.
{ "database": "myapp" }List triggers in a database.
{ "database": "myapp" }List stored procedures.
{ "database": "myapp" }List stored functions.
{ "database": "myapp" }List table partitions.
{ "database": "myapp", "table": "events" }Get database size information.
{ "database": "myapp" }Or get all databases:
{}Get table size information.
{ "database": "myapp" }List foreign key constraints.
{ "database": "myapp", "table": "orders" }List MySQL server status variables.
{ "pattern": "Threads%" }List MySQL server configuration variables.
{ "pattern": "%buffer%" }The server enforces strict SQL validation:
Allowed operations:
SELECT,SHOW,DESCRIBE,EXPLAIN
Blocked patterns:
- Multi-statement queries (semicolons)
- File operations:
LOAD_FILE(),INTO OUTFILE,INTO DUMPFILE - DDL:
CREATE,ALTER,DROP,TRUNCATE,RENAME - DML:
INSERT,UPDATE,DELETE,REPLACE - Admin:
GRANT,REVOKE,FLUSH,KILL,SHUTDOWN - Dangerous functions:
SLEEP(),BENCHMARK(),GET_LOCK() - Transaction control:
BEGIN,COMMIT,ROLLBACK
CREATE USER 'mcp'@'localhost' IDENTIFIED BY 'strongpass';
GRANT SELECT ON *.* TO 'mcp'@'localhost';Enable JSON logs for production:
export MYSQL_MCP_JSON_LOGS=1Output:
{"timestamp":"2025-01-15T10:30:00.123Z","level":"INFO","message":"query executed","fields":{"tool":"run_query","duration_ms":15,"row_count":42}}Enable query audit trail:
export MYSQL_MCP_AUDIT_LOG=/var/log/mysql-mcp-audit.jsonlEach query is logged with timing, success/failure, and row counts.
All queries are automatically timed and logged with:
- Execution duration (milliseconds)
- Row count returned
- Tool name
- Truncated query (for debugging)
Configure the connection pool for your workload:
export MYSQL_MAX_OPEN_CONNS=20 # Max open connections
export MYSQL_MAX_IDLE_CONNS=10 # Max idle connections
export MYSQL_CONN_MAX_LIFETIME_MINUTES=60 # Connection lifetimemake test
make integrationBasic usage:
docker run -e MYSQL_DSN="user:password@tcp(host.docker.internal:3306)/mydb" \
ghcr.io/askdba/mysql-mcp-server:latestNote: Use
host.docker.internalinstead oflocalhostto connect from inside the container to MySQL on your host machine (macOS/Windows).
With extended tools enabled:
docker run \
-e MYSQL_DSN="user:password@tcp(host.docker.internal:3306)/mydb" \
-e MYSQL_MCP_EXTENDED=1 \
ghcr.io/askdba/mysql-mcp-server:latestWith all options:
docker run \
-e MYSQL_DSN="user:password@tcp(host.docker.internal:3306)/mydb" \
-e MYSQL_MCP_EXTENDED=1 \
-e MYSQL_MCP_VECTOR=1 \
-e MYSQL_MAX_ROWS=500 \
-e MYSQL_QUERY_TIMEOUT_SECONDS=60 \
ghcr.io/askdba/mysql-mcp-server:latestEdit ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"mysql": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "MYSQL_DSN=user:password@tcp(host.docker.internal:3306)/mydb",
"ghcr.io/askdba/mysql-mcp-server:latest"
]
}
}
}With extended tools:
{
"mcpServers": {
"mysql": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "MYSQL_DSN=user:password@tcp(host.docker.internal:3306)/mydb",
"-e", "MYSQL_MCP_EXTENDED=1",
"ghcr.io/askdba/mysql-mcp-server:latest"
]
}
}
}version: "3.9"
services:
mysql:
image: mysql:8.0.36
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: testdb
ports:
- "3306:3306"
mcp:
image: ghcr.io/askdba/mysql-mcp-server:latest
depends_on:
- mysql
environment:
MYSQL_DSN: "root:rootpass@tcp(mysql:3306)/testdb?parseTime=true"
MYSQL_MCP_EXTENDED: "1"Run:
docker compose updocker build -t mysql-mcp-server .Enable HTTP REST API mode to use with ChatGPT, Gemini, or any HTTP client:
export MYSQL_DSN="user:password@tcp(localhost:3306)/mydb"
export MYSQL_MCP_HTTP=1
export MYSQL_HTTP_PORT=9306 # Optional, defaults to 9306
mysql-mcp-serverEnable per-IP rate limiting for production deployments:
export MYSQL_HTTP_RATE_LIMIT=1
export MYSQL_HTTP_RATE_LIMIT_RPS=100 # 100 requests/second
export MYSQL_HTTP_RATE_LIMIT_BURST=200 # Allow bursts up to 200When rate limited, clients receive HTTP 429 (Too Many Requests) with a Retry-After: 1 header.
| Method | Endpoint | Description |
|---|---|---|
| GET | /health |
Health check |
| GET | /api |
API index with all endpoints |
| GET | /api/databases |
List databases |
| GET | /api/tables?database= |
List tables |
| GET | /api/describe?database=&table= |
Describe table |
| POST | /api/query |
Run SQL query |
| GET | /api/ping |
Ping database |
| GET | /api/server-info |
Server info |
| GET | /api/connections |
List connections |
| POST | /api/connections/use |
Switch connection |
Extended endpoints (requires MYSQL_MCP_EXTENDED=1):
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/indexes?database=&table= |
List indexes |
| GET | /api/create-table?database=&table= |
Show CREATE TABLE |
| POST | /api/explain |
Explain query |
| GET | /api/views?database= |
List views |
| GET | /api/triggers?database= |
List triggers |
| GET | /api/procedures?database= |
List procedures |
| GET | /api/functions?database= |
List functions |
| GET | /api/size/database?database= |
Database size |
| GET | /api/size/tables?database= |
Table sizes |
| GET | /api/foreign-keys?database= |
Foreign keys |
| GET | /api/status?pattern= |
Server status |
| GET | /api/variables?pattern= |
Server variables |
Vector endpoints (requires MYSQL_MCP_VECTOR=1):
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/vector/search |
Vector similarity search |
| GET | /api/vector/info?database= |
Vector column info |
List databases:
curl http://localhost:9306/api/databasesRun a query:
curl -X POST http://localhost:9306/api/query \
-H "Content-Type: application/json" \
-d '{"sql": "SELECT * FROM users LIMIT 5", "database": "myapp"}'Get server info:
curl http://localhost:9306/api/server-infoAll responses follow this format:
{
"success": true,
"data": { ... }
}Error responses:
{
"success": false,
"error": "error message"
}- Start the REST API server on a publicly accessible host
- Create a Custom GPT with Actions
- Import the OpenAPI schema from
/api - Configure authentication if needed
docker run -p 9306:9306 \
-e MYSQL_DSN="user:password@tcp(host.docker.internal:3306)/mydb" \
-e MYSQL_MCP_HTTP=1 \
-e MYSQL_MCP_EXTENDED=1 \
ghcr.io/askdba/mysql-mcp-server:latestcmd/mysql-mcp-server/
├── main.go -> Server entrypoint and tool registration
├── types.go -> Input/output struct types for tools
├── tools.go -> Core MCP tool handlers
├── tools_extended.go -> Extended MCP tool handlers
├── http.go -> HTTP REST API handlers and server
├── connection.go -> Multi-DSN connection manager
└── logging.go -> Structured and audit logging
internal/
├── api/ -> HTTP middleware and response utilities
├── config/ -> Configuration loader from environment
├── mysql/ -> MySQL client wrapper + tests
└── util/ -> Shared utilities (SQL validation, identifiers)
examples/ -> Example configs and test data
scripts/ -> Quickstart and utility scripts
bin/ -> Built binaries
The examples/ folder contains:
claude_desktop_config.json- Example Claude Desktop configurationtest-dataset.sql- Demo database with tables, views, and sample data
Load the test dataset:
mysql -u root -p < examples/test-dataset.sqlThis creates a mcp_demo database with:
- 5 categories, 13 products, 8 customers
- 9 orders with 16 order items
- Views:
order_summary,product_inventory - Stored procedure:
GetCustomerOrders - Stored function:
GetProductStock
make fmt # Format code
make lint # Run linter
make test # Run unit tests
make build # Build binary
make release # Build release binariesReleases are automated via GitHub Actions and GoReleaser.
To create a new release:
git tag v1.0.0
git push origin v1.0.0This will automatically:
- Build binaries for macOS, Linux, and Windows
- Create a GitHub Release with changelog
- Push Docker image to
ghcr.io/askdba/mysql-mcp-server - Update Homebrew formula (if configured)
Apache License 2.0
© 2025 Alkin Tezuysal