MySQL MCP 服务

mcp-go-mysql 是一个用 Go 编写的 MySQL/MariaDB MCP 服务器,通过明确定义的工具集让 Claude Desktop 等 MCP 客户端安全地探索和修改数据库,并内置分层安全机制与危险操作拦截。

  • by scopweb
  • Go
  • 本地部署
  • 2 星标
  • 更新于
在 GitHub 查看 scopweb/mcp-go-mysql

使用说明

mcp-go-mysql

A Model Context Protocol (MCP) server for MySQL and MariaDB, written in Go. Current release: 3.1.0. Lets Claude Desktop (or any MCP client) explore and modify a database through a small, well-defined set of tools.

Security model

Two layers — and only two. Any extra "regex of dangerous patterns" was removed because it traded clarity and false positives for protection that the database already provides better.

Layer 1 — MySQL grants (primary). This is the real boundary. The MCP connects with a dedicated user; that user's privileges decide what is actually possible. A user without FILE cannot read /etc/passwd no matter how the SQL is phrased. A user without CREATE USER cannot create users. Get this layer right and most of the rest is paranoia.

Layer 2 — Verb classifier (this code). A defence-in-depth layer for the case where layer 1 is misconfigured (root user, too-broad grants, …). Every statement is classified by its leading SQL verb after comments are stripped. The classifier:

  • Always rejects privilege management and filesystem access: GRANT, REVOKE, SET, FLUSH, RESET, KILL, SHUTDOWN, LOAD, HANDLER, INSTALL, UNINSTALL, LOCK, UNLOCK. Also rejects INTO OUTFILE / INTO DUMPFILE clauses inside otherwise-legal SELECTs.
  • Rejects DDL (CREATE, DROP, ALTER, TRUNCATE, RENAME) unless ALLOW_DDL=true.
  • Rejects stacked statements (SELECT 1; DROP DATABASE foo).
  • Rejects unknown verbs — it's a whitelist, not a blacklist.
  • Allows SELECT/WITH/SHOW/DESCRIBE/EXPLAIN/USE and INSERT/UPDATE/DELETE/REPLACE/CALL.

Plus: an UPDATE or DELETE that ends up affecting more than MAX_SAFE_ROWS rows is executed inside an explicit transaction and requires confirm_key to commit. Without the key the transaction is rolled back, so the changes never persist. This catches the "oops, I forgot the WHERE" case without trying to parse the SQL.

What the classifier deliberately does not do:

  • It does not pattern-match for SLEEP, BENCHMARK, EXTRACTVALUE, etc. These are legitimate SQL functions; the classic "time-based blind injection" threat model assumes user input is being concatenated into SQL by an application — that's not what's happening here. The MCP client is the LLM, and it writes whole statements directly.
  • It does not try to detect missing WHERE clauses with regex (those checks had bugs and false positives). The row-count gate covers the same use case more reliably.

Recommended MySQL user

-- Create a dedicated user
CREATE USER 'mcp_user'@'%' IDENTIFIED BY 'secure_password';

-- Minimum privileges for read-only usage
GRANT SELECT, SHOW VIEW ON your_database.* TO 'mcp_user'@'%';

-- Read + write (most common)
GRANT SELECT, INSERT, UPDATE, DELETE, SHOW VIEW
  ON your_database.* TO 'mcp_user'@'%';

-- DDL (only if you actually want Claude to alter the schema)
GRANT CREATE, ALTER, DROP, INDEX ON your_database.* TO 'mcp_user'@'%';

FLUSH PRIVILEGES;

Do not use root or any user with GRANT OPTION / FILE / CREATE USER / SUPER. The classifier blocks the obvious abuses but the grants are what stops them at the source.

Tools

Tool What it does
query Run a SELECT/WITH/SHOW. Read-only.
execute Run INSERT/UPDATE/DELETE. Asks for confirm_key past MAX_SAFE_ROWS.
tables List tables with metadata.
describe Show columns, types, keys for one table.
views List views.
indexes Show indexes for a table.
explain EXPLAIN a SELECT.
count SELECT COUNT(*) on a table. For filtered counts, use query.
sample First N rows of a table (default 10, max 100).
database_info Server version, current user, host, port, database.

Install

git clone https://github.com/scopweb/mcp-go-mysql.git
cd mcp-go-mysql
go mod tidy
go build -o mysql-mcp ./cmd
go test ./...

Configuration

.env in the project directory, or environment variables in the Claude Desktop config.

Variable Required Default Notes
MYSQL_HOST yes localhost
MYSQL_PORT no 3306
MYSQL_USER yes — The dedicated user, not root.
MYSQL_PASSWORD yes —
MYSQL_DATABASE yes — Default schema.
LOG_PATH no mysql-mcp.log Confined to cwd, temp, or /var/log.
ALLOWED_TABLES no empty (= all tables allowed) Whitelist for the identifier-based tools (describe, count, sample, indexes). See note below.
ALLOW_DDL no false true lets DDL through the classifier.
SAFETY_KEY no random per start Required for >MAX_SAFE_ROWS writes. See note below.
MAX_SAFE_ROWS no 100

ALLOWED_TABLES is enforced only by the tools that take a table name directly (describe, count, sample, indexes). It is not an access boundary for the raw-SQL tools (query, execute, explain), which accept arbitrary statements — restricting which data a session can read or write is the job of the MySQL user's own GRANTs, not this list.

SAFETY_KEY has no hardcoded default. If you don't set it, the server generates a random key at startup and logs a warning; large writes (>MAX_SAFE_ROWS rows) then cannot be confirmed at all until you set SAFETY_KEY to a value of your choosing.

Claude Desktop

Configuration file:

OS Path
Windows %APPDATA%\Claude\claude_desktop_config.json
macOS ~/Library/Application Support/Claude/claude_desktop_config.json
Linux ~/.config/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "mysql": {
      "command": "C:\\path\\to\\mysql-mcp.exe",
      "args": [],
      "env": {
        "MYSQL_HOST": "localhost",
        "MYSQL_PORT": "3306",
        "MYSQL_USER": "mcp_user",
        "MYSQL_PASSWORD": "secure_password",
        "MYSQL_DATABASE": "your_database",
        "SAFETY_KEY": "your-own-key",
        "MAX_SAFE_ROWS": "100"
      }
    }
  }
}

Restart Claude Desktop. In a new chat: "What MySQL tools are available?" should list ten tools.

OpenCode

Local stdio MCP. Config file (opencode.json or opencode.jsonc):

Scope Path
Global ~/.config/opencode/opencode.json
Project opencode.json in the project root
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "mysql": {
      "type": "local",
      "command": ["C:\\path\\to\\mysql-mcp.exe"],
      "enabled": true,
      "environment": {
        "MYSQL_HOST": "localhost",
        "MYSQL_PORT": "3306",
        "MYSQL_USER": "mcp_user",
        "MYSQL_PASSWORD": "secure_password",
        "MYSQL_DATABASE": "your_database",
        "SAFETY_KEY": "your-own-key",
        "MAX_SAFE_ROWS": "100"
      }
    }
  }
}

On macOS/Linux use "command": ["/path/to/mysql-mcp"]. Restart OpenCode (or start a new session) so the server is picked up. See OpenCode MCP docs.

Grok Builder / Grok TUI

Grok supports MCP servers via stdio. Configure it through Grok's settings or the TUI (usually under ~/.grok/ or the interface).

Use the same parameters as other clients:

{
  "mcpServers": {
    "mysql": {
      "command": "/path/to/mysql-mcp",
      "args": [],
      "env": {
        "MYSQL_HOST": "localhost",
        "MYSQL_PORT": "3306",
        "MYSQL_USER": "mcp_user",
        "MYSQL_PASSWORD": "secure_password",
        "MYSQL_DATABASE": "your_database",
        "SAFETY_KEY": "your-own-key",
        "MAX_SAFE_ROWS": "100"
      }
    }
  }
}

For the current way to register MCP servers in Grok, refer to the Grok TUI documentation.

Examples

-- Allowed
SELECT * FROM products WHERE category = 'electronics' LIMIT 10
UPDATE orders SET status = 'shipped' WHERE order_id = 12345
WITH t AS (SELECT 1) SELECT * FROM t

-- Allowed, but >MAX_SAFE_ROWS rows → asks for confirm_key
UPDATE products SET discount = 0.1 WHERE category = 'clearance'

-- Rejected: privilege management
GRANT ALL ON *.* TO 'evil'@'%'
CREATE USER 'foo'@'%' IDENTIFIED BY 'bar'
SET PASSWORD FOR 'root'@'localhost' = PASSWORD('x')
FLUSH PRIVILEGES

-- Rejected: filesystem
LOAD DATA INFILE '/etc/passwd' INTO TABLE x
SELECT * FROM users INTO OUTFILE '/tmp/data'

-- Rejected: stacked
SELECT 1; DROP DATABASE foo

-- Rejected unless ALLOW_DDL=true
DROP TABLE users
ALTER TABLE users ADD COLUMN x INT

Tests

go test ./...                       # everything
go test -v ./cmd/security/...       # security and dependency tests

cmd/security/dependencies_test.go checks that go.mod stays limited to the MySQL driver and that go.sum is well formed.

Project layout

cmd/                     MCP protocol layer (stdin/stdout JSON-RPC)
  main.go                Entry point, .env loader, logging
  handlers.go            initialize / tools/list / tools/call
  tools.go               Tool definitions and dispatch
  format.go              Result formatting
  sqlcheck.go            Leading-verb gates for query and explain
internal/                Database client and policy
  client.go              Connection, classifier, ValidateQuery
  timeout.go             Per-operation timeouts
  db_compat.go           MySQL vs MariaDB detection and DSN
cmd/security/            Classifier and dependency-integrity tests
docs/                    Architecture, security, safety-gate verification
website/                 Documentation site (Astro Starlight)

Troubleshooting

Symptom Likely cause
connection refused MySQL is not running on the configured host/port.
access denied Wrong credentials or grants don't cover the schema.
statement "X" is not allowed A forbidden verb (GRANT, SET, LOAD, …). Use grants.
DDL operations are blocked Set ALLOW_DDL=true if you really want this.
multiple statements are not allowed Split your call into separate query/execute runs.
operation affects N rows (>M) Pass confirm_key matching SAFETY_KEY. Without it the transaction is rolled back and no changes are committed.

Logs go to LOG_PATH (default mysql-mcp.log in cwd). tail -f it while debugging.

License

MIT — see LICENSE.