MCP server for MySQL/MariaDB — pooled async queries with destructive-query safety
A high-quality Model Context Protocol (MCP) server implementation for MySQL databases. This server enables AI assistants like Claude to interact with MySQL databases through a standardized protocol.
Version: 0.2.0 | Protocol: MCP 2025-03-26 | Rust: 1.70+ | Status: Production Ready
git clone <repository-url>
cd mcp-server-mysql
cargo build --release
The compiled binary will be available at target/release/mcp-server-mysql.
# Extract the package
tar -xzf mcp-server-mysql-v0.2.0-linux-x86_64.tar.gz
# Move binary to system path (optional)
sudo cp mcp-server-mysql /usr/local/bin/
# Verify installation
mcp-server-mysql --version
cargo build --release
The binary will be at target/release/mcp-server-mysql
./target/release/mcp-server-mysql \
--host localhost \
--username root \
--password yourpassword \
--database testdb
You should see: "MCP MySQL Server started and ready to accept connections"
Edit your Claude Desktop configuration file:
~/Library/Application Support/Claude/claude_desktop_config.json%APPDATA%\Claude\claude_desktop_config.jsonAdd this configuration:
{
"mcpServers": {
"mysql": {
"command": "/absolute/path/to/mcp-server-mysql",
"args": [
"--host", "localhost",
"--port", "3306",
"--username", "your_username",
"--password", "your_password",
"--database", "your_database"
]
}
}
}
Security Note: For production use, consider using environment variables or a secure secrets management solution instead of hardcoding passwords in the configuration file.
Close and reopen Claude Desktop completely. You should see a small hammer icon indicating the MCP server is connected.
Ask Claude:
mcp-server-mysql \
--host localhost \
--port 3306 \
--username your_username \
--password your_password \
--database your_database \
--allow-dangerous-queries false
| Argument | Description | Default | Required |
|---|---|---|---|
--host | MySQL server hostname | localhost | No |
--port | MySQL server port | 3306 | No |
--username | MySQL username | - | Yes |
--password | MySQL password | (empty) | No |
--database | Database name to connect to | - | Yes |
--allow-dangerous-queries | Allow INSERT/UPDATE/DELETE queries | false | No |
Add this configuration to your Claude Desktop config file:
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"mysql": {
"command": "/path/to/mcp-server-mysql",
"args": [
"--host", "localhost",
"--port", "3306",
"--username", "your_username",
"--password", "your_password",
"--database", "your_database"
]
}
}
}
Retrieve database schema information for tables.
Parameters:
table_name (string): Name of the table to inspect, or "all-tables" to get all table schemasExample:
{
"table_name": "users"
}
Returns:
Execute SQL queries on the database.
Parameters:
query (string): SQL query to executedatabase (string, optional): Database name to use for this specific queryExample:
{
"query": "SELECT * FROM users WHERE active = 1 LIMIT 10",
"database": "my_database"
}
Safety:
--allow-dangerous-queries flag to enable INSERT/UPDATE/DELETEInsert data into a specified table.
Parameters:
table_name (string): Name of the tabledata (object): Key-value pairs of column names and valuesExample:
{
"table_name": "users",
"data": {
"username": "john_doe",
"email": "john@example.com",
"active": true
}
}
Returns: Last insert ID
Update data in a specified table based on conditions.
Parameters:
table_name (string): Name of the tabledata (object): Key-value pairs of columns to updateconditions (object): Key-value pairs for WHERE clauseExample:
{
"table_name": "users",
"data": {
"email": "newemail@example.com",
"updated_at": "2024-01-15 10:30:00"
},
"conditions": {
"id": 123
}
}
Returns: Number of affected rows
Delete data from a specified table based on conditions.
Parameters:
table_name (string): Name of the tableconditions (object): Key-value pairs for WHERE clauseExample:
{
"table_name": "users",
"conditions": {
"id": 123
}
}
Returns: Number of affected rows
Warning: Always specify conditions to avoid deleting all rows!
Previously, database context was not maintained between queries:
-- Query 1
USE dev_database; -- Succeeds
-- Query 2 (new connection from pool)
SELECT * FROM my_table; -- ❌ Fails: context was lost
Use the optional database parameter on each query:
{
"query": "SELECT * FROM my_table",
"database": "dev_database"
}
"database": "name" to query arguments{
"query": "SELECT * FROM crm_sites LIMIT 10",
"database": "dev_smartConnect_za"
}
{
"query": "SELECT * FROM users WHERE active = 1"
}
Uses the database specified in --database startup argument.
// Query database 1
{
"query": "SELECT COUNT(*) FROM customers",
"database": "production_db"
}
// Query database 2
{
"query": "SELECT COUNT(*) FROM test_data",
"database": "test_db"
}
Before (Required fully qualified names):
SELECT * FROM dev_smartConnect_za.crm_sites
JOIN dev_smartConnect_za.crm_orgs ON ...
WHERE dev_smartConnect_za.crm_sites.active = 1;
After (Clean and simple):
{
"query": "SELECT * FROM crm_sites JOIN crm_orgs ON ... WHERE active = 1",
"database": "dev_smartConnect_za"
}
Set default database and omit the parameter:
# Startup
--database my_project_db
# Query (no database parameter needed)
{
"query": "SELECT * FROM users"
}
Specify database for each query:
// Customer database
{ "query": "...", "database": "customers_db" }
// Orders database
{ "query": "...", "database": "orders_db" }
// Analytics database
{ "query": "...", "database": "analytics_db" }
Error Code -32005: Connection Acquisition Failed
Cause: Connection pool exhausted
Solution: Retry after a moment
Error Code -32006: Database Context Switch Failed
Cause: Database doesn't exist or user lacks permissions
Solution: Verify database exists and user has access
✅ DO
SELECT DATABASE() to verify context❌ DON'T
By default, the server operates in read-only mode, allowing only SELECT queries. This prevents accidental data modification or deletion.
Enable write operations with --allow-dangerous-queries:
mcp-server-mysql --username user --password pass --database mydb --allow-dangerous-queries true
Use with caution! This enables:
Use dedicated database user:
CREATE USER 'mcp_user'@'localhost' IDENTIFIED BY 'secure_password';
GRANT SELECT ON your_database.* TO 'mcp_user'@'localhost';
FLUSH PRIVILEGES;
Enable write access only when needed:
--allow-dangerous-queries true # Use with caution!
Use environment variables (future enhancement): Consider wrapping the binary in a shell script that reads from env vars.
┌─────────────────────────────────────────────────────┐
│ MCP Client (e.g., Claude) │
│ Sends: {query, database} │
└────────────────────────┬────────────────────────────┘
│ JSON-RPC 2.0 (stdio)
▼
┌─────────────────────────────────────────────────────┐
│ MCP MySQL Server (Rust) │
│ │
│ execute_query(query, database, pool) │
│ ├─ If database param: │
│ │ ├─ Acquire connection from pool │
│ │ ├─ Execute: USE `database` │
│ │ └─ Execute: [user's query] │
│ └─ Else: │
│ └─ Execute query on pool (default database) │
└────────────────────────┬────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ MySQL Connection Pool (5 connections) │
└────────────────────────┬────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ MySQL/MariaDB Server │
└─────────────────────────────────────────────────────┘
Client MCP Server Connection Pool MySQL Server
│ │ │ │
│ query + │ │ │
│ database │ │ │
├────────────────>│ │ │
│ │ │ │
│ │ acquire() │ │
│ ├───────────────────>│ │
│ │ <connection> │ │
│ │<───────────────────┤ │
│ │ │ │
│ │ USE database │ │
│ ├────────────────────┼─────────────────>│
│ │ OK │ │
│ │<────────────────────┼──────────────────┤
│ │ │ │
│ │ SELECT query │ │
│ ├────────────────────┼─────────────────>│
│ │ Results │ │
│ │<────────────────────┼──────────────────┤
│ │ │ │
│ │ release() │ │
│ ├───────────────────>│ │
│ Results │ │ │
│<────────────────┤ │ │
Pool (5 connections)
┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐
│ C1 │ │ C2 │ │ C3 │ │ C4 │ │ C5 │
└────┘ └────┘ └────┘ └────┘ └────┘
Key Properties:
• Each query gets its own connection instance
• Database context is set per connection, per query
• No state persists between queries
• Fully thread-safe and concurrent
If you encounter connection errors:
Check MySQL is running:
mysql -h localhost -u your_username -p
Verify credentials:
Check network access:
Review server logs:
--allow-dangerous-queries true if write access is neededSHOW DATABASES; to see available databasesdatabase parameter if using multiple databasesSELECT DATABASE() to check current contextmcp-server-mysql/
├── src/
│ ├── main.rs # Main server implementation
│ ├── config.rs # Configuration handling
│ ├── db.rs # Database operations
│ ├── rpc.rs # RPC protocol handling
│ └── server.rs # Server initialization
├── tests/ # Test files
├── Cargo.toml # Rust dependencies
├── Cargo.lock # Locked dependency versions
└── README.md # This file
cargo build
cargo run -- --help
cargo test
# Format code
cargo fmt
# Run linter
cargo clippy
# Check for issues
cargo check
The project follows standard Rust best practices:
cargo fmtcargo clippycargo test./mcp-server-mysql \
--username your_user \
--password your_pass \
--database your_db
Press Ctrl+C to exit after seeing "MCP MySQL Server started".
Edit your Claude config file and add the server configuration (see Quick Start section).
Close and reopen Claude Desktop completely.
--release flagServer logs go to stderr. Capture them with:
./mcp-server-mysql --username user --password pass --database db 2>> server.log
Log levels:
INFO: Connection events, tool callsDEBUG: Detailed query informationWARN: Non-fatal issuesERROR: Failures and errorsFor long-running deployments, create /etc/systemd/system/mcp-mysql.service:
[Unit]
Description=MySQL MCP Server
After=network.target mysql.service
[Service]
Type=simple
User=mcp-user
ExecStart=/usr/local/bin/mcp-server-mysql --username mcp_user --password secret --database production
Restart=on-failure
RestartSec=5s
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
Enable and start:
sudo systemctl enable mcp-mysql
sudo systemctl start mcp-mysql
sudo systemctl status mcp-mysql
# Backup current version
cp /usr/local/bin/mcp-server-mysql /usr/local/bin/mcp-server-mysql.backup
# Replace with new version
cp mcp-server-mysql /usr/local/bin/
# Restart services
sudo systemctl restart mcp-mysql # If using systemd
# Or restart Claude Desktop
# Restore previous version
cp /usr/local/bin/mcp-server-mysql.backup /usr/local/bin/mcp-server-mysql
# Or checkout previous git tag
git checkout v0.1.0
cargo build --release
Contributions are welcome! Please ensure:
Apache-2.0
For issues, questions, or contributions, please open an issue on the project repository.
Version: 0.2.0 | Release Date: 2025-01-XX | Protocol: MCP 2025-03-26 | Platform: Linux x86_64 | Status: Production Ready ✅
This listing does not have a supported local package template. Use the maintainer’s documentation for its hosted endpoint, authentication, and client-specific setup. No install command has been inferred.