Skip to main content
Glama
za2keep

MySQL MCP Server

by za2keep
README.md
# MySQL MCP Server

[English](README.md) | [简体中文](README.zh-CN.md)

A Model Context Protocol (MCP) server implementation for MySQL databases, enabling AI assistants like Claude to interact with MySQL databases in a safe, standardized, and controlled way.

## Features

- **MCP Protocol Compliant**: Fully implements the Model Context Protocol specification
- **Safe Query Execution**: Built-in query validation and safety controls
- **Schema Inspection**: Tools to explore database structure
- **Transaction Support**: Full transaction management (BEGIN, COMMIT, ROLLBACK)
- **Resource Exposure**: Database schemas exposed as MCP resources
- **Comprehensive Error Handling**: Detailed error messages with SQL state codes
- **Property-Based Testing**: Extensively tested with property-based testing for correctness

## Installation

### From Source

```bash
# Clone the repository
git clone <repository-url>
cd mysql-mcp-server

# Install dependencies
npm install

# Build the project
npm run build

# Link globally (makes the command available system-wide)
npm link
```

After installation, the `mysql-mcp-server` command will be available system-wide.

## Configuration

The server is configured entirely through environment variables:

### Required Environment Variables

- `MYSQL_HOST` - MySQL server hostname (default: `localhost`)
- `MYSQL_PORT` - MySQL server port (default: `3306`)
- `MYSQL_USER` - MySQL username (required)
- `MYSQL_PASSWORD` - MySQL password (required)
- `MYSQL_DATABASE` - Database name (required)

### Optional Environment Variables

- `MYSQL_CONNECTION_LIMIT` - Maximum number of connections in pool (default: `10`)
- `MAX_SELECT_ROWS` - Maximum rows returned by SELECT queries (default: `1000`)
- `ALLOW_DDL` - Allow DDL operations (CREATE, DROP, ALTER) (default: `false`)
- `ALLOW_MULTIPLE_STATEMENTS` - Allow multiple SQL statements (default: `false`)
- `REQUIRE_WHERE_CLAUSE` - Require WHERE clause for UPDATE/DELETE (default: `true`)
- `MCP_LOG_LEVEL` - Logging level: `debug`, `info`, `warn`, `error` (default: `info`)

## Usage

### With Claude Desktop

Add to your Claude Desktop configuration file:

**macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
**Windows**: `%APPDATA%\Claude\claude_desktop_config.json`

```json
{
  "mcpServers": {
    "mysql": {
      "command": "mysql-mcp-server",
      "env": {
        "MYSQL_HOST": "localhost",
        "MYSQL_PORT": "3306",
        "MYSQL_USER": "your_username",
        "MYSQL_PASSWORD": "your_password",
        "MYSQL_DATABASE": "your_database"
      }
    }
  }
}
```

### With Cursor

Add to your Cursor MCP settings (`.cursor/mcp.json` in your project):

```json
{
  "mcpServers": {
    "mysql": {
      "command": "mysql-mcp-server",
      "env": {
        "MYSQL_HOST": "localhost",
        "MYSQL_USER": "your_username",
        "MYSQL_PASSWORD": "your_password",
        "MYSQL_DATABASE": "your_database"
      }
    }
  }
}
```

### With npx (Direct Execution)

You can also run the server directly using npx without installation. First, build the project locally:

```bash
# In the project directory
npm run build
```

Then configure your MCP client to use npx with the local path:

```json
{
  "mcpServers": {
    "mysql": {
      "command": "node",
      "args": ["/absolute/path/to/mysql-mcp-server/dist/index.js"],
      "env": {
        "MYSQL_HOST": "localhost",
        "MYSQL_USER": "your_username",
        "MYSQL_PASSWORD": "your_password",
        "MYSQL_DATABASE": "your_database"
      }
    }
  }
}
```

## Available Tools

The server provides the following MCP tools:

### 1. `query`
Execute SQL queries (SELECT, INSERT, UPDATE, DELETE)

```typescript
// Example usage in Claude
"Execute a query to find all users: SELECT * FROM users WHERE active = 1"
```

**Safety Features**:
- Automatically adds LIMIT to SELECT queries without one
- Rejects DELETE/UPDATE without WHERE clause (configurable)
- Rejects multiple statements
- Rejects DDL operations by default

### 2. `list_tables`
List all tables in the current database

```typescript
// Example usage
"Show me all tables in the database"
```

### 3. `describe_table`
Get detailed schema information for a specific table

```typescript
// Example usage
"Describe the structure of the users table"
```

### 4. `show_indexes`
Show all indexes for a specific table

```typescript
// Example usage
"Show me the indexes on the orders table"
```

### 5. `begin_transaction`
Start a new database transaction

### 6. `commit_transaction`
Commit the current transaction

### 7. `rollback_transaction`
Rollback the current transaction

```typescript
// Example transaction usage
"Start a transaction, update the user's email, then commit"
```

## Available Resources

The server exposes database schemas as MCP resources:

- **URI Format**: `mysql://{database}/{table}`
- **Content**: Structured JSON with table schema information

```typescript
// Example usage
"Read the schema resource for the users table"
```

## Security Considerations

### Default Safety Controls

1. **Query Validation**: All queries are validated before execution
2. **Row Limits**: SELECT queries are automatically limited to prevent memory exhaustion
3. **WHERE Clause Enforcement**: DELETE/UPDATE require WHERE clause by default
4. **DDL Restrictions**: CREATE, DROP, ALTER are blocked by default
5. **Single Statement**: Multiple statements are rejected by default

### Recommended Practices

1. **Use Read-Only Users**: Create a MySQL user with SELECT-only permissions for read-only use cases
2. **Limit Permissions**: Grant only necessary permissions to the MySQL user
3. **Network Security**: Use localhost or secure network connections
4. **Environment Variables**: Never commit credentials to version control
5. **Enable Logging**: Use `MCP_LOG_LEVEL=info` to monitor query execution

### Example: Creating a Read-Only User

```sql
-- Create a read-only user
CREATE USER 'mcp_readonly'@'localhost' IDENTIFIED BY 'secure_password';
GRANT SELECT ON your_database.* TO 'mcp_readonly'@'localhost';
FLUSH PRIVILEGES;
```

### Example: Creating a Limited Write User

```sql
-- Create a user with limited write permissions
CREATE USER 'mcp_user'@'localhost' IDENTIFIED BY 'secure_password';
GRANT SELECT, INSERT, UPDATE ON your_database.* TO 'mcp_user'@'localhost';
FLUSH PRIVILEGES;
```

## Development

### Prerequisites

- Node.js >= 18.0.0
- npm or yarn
- MySQL database (for testing and development)
- Docker (optional, for integration tests)

### Setup

```bash
# Install dependencies
npm install

# Build the project
npm run build

# Run tests
npm test
```

### Testing

```bash
# Run all tests
npm test

# Run specific test suites
npm run test:unit          # Unit tests only
npm run test:property      # Property-based tests only
npm run test:integration   # Integration tests only

# Run tests in watch mode
npm run test:watch

# Run tests with coverage
npm run test:coverage
```

### Integration Tests

Integration tests require a MySQL database. You can use the provided Docker setup:

```bash
# Start test database
npm run db:start

# Run integration tests
npm run test:integration

# Stop test database
npm run db:stop

# View database logs
npm run db:logs

# Connect to test database
npm run db:connect
```

See [tests/integration/README.md](tests/integration/README.md) for more details.

## Project Structure

```
mysql-mcp-server/
├── src/                    # TypeScript source files
│   ├── index.ts           # Main entry point
│   ├── config.ts          # Configuration management
│   ├── database.ts        # Database connection handling
│   ├── validator.ts       # Query validation
│   ├── transaction.ts     # Transaction management
│   ├── resources.ts       # MCP resource handlers
│   ├── errors.ts          # Error handling
│   └── logger.ts          # Logging system
├── tests/
│   ├── unit/              # Unit tests
│   ├── property/          # Property-based tests (fast-check)
│   └── integration/       # Integration tests
├── dist/                  # Compiled JavaScript output
├── .kiro/specs/           # Project specifications
│   └── mysql-mcp-server/
│       ├── requirements.md # Formal requirements (EARS format)
│       ├── design.md      # Design document with correctness properties
│       └── tasks.md       # Implementation task list
├── package.json
├── tsconfig.json
└── vitest.config.ts
```

## Technology Stack

- **Runtime**: Node.js with TypeScript
- **MCP SDK**: @modelcontextprotocol/sdk
- **Database**: mysql2
- **Validation**: Zod
- **Testing**: Vitest + fast-check (property-based testing)

## Troubleshooting

### Network Access in Different MCP Clients

**Important**: Different MCP clients have different network access policies:

- **AI IDEs (Cursor, Windsurf, etc.)**: Usually restrict private network access (192.168.x.x, 10.x.x.x) for security
- **Desktop Apps (Claude Desktop)**: Full network access, no restrictions
- **CLI Tools**: Full network access, inherit terminal permissions

**This affects ALL database MCP servers** (MySQL, PostgreSQL, Redis, MongoDB, etc.) when connecting to local network databases.

See [MCP_CLIENT_NETWORK_COMPARISON.md](MCP_CLIENT_NETWORK_COMPARISON.md) for detailed comparison and solutions.

### Connection Issues

**Problem**: Server fails to connect to MySQL

**Solutions**:
- Verify MySQL is running: `mysql -h localhost -u your_user -p`
- Check credentials in environment variables
- Verify network connectivity and firewall rules
- Check MySQL user permissions

**Problem**: `EHOSTUNREACH` error when connecting to remote MySQL server

This error can occur when running the MCP server in Cursor, specifically when connecting to **private network (LAN) MySQL servers**:

**Affected scenarios:**
- ❌ Local network MySQL: `192.168.x.x`, `10.x.x.x`, `172.16.x.x - 172.31.x.x`
- ✅ Cloud/Public MySQL: AWS RDS, Alibaba Cloud RDS, public IPs - **should work directly**

**Root cause**: Cursor may run MCP servers in a sandboxed environment that restricts access to private networks for security reasons.

**Solutions**:

**If your MySQL is on a cloud server (public IP/domain):**

Simply use the public endpoint directly - no special configuration needed:

```json
{
  "mcpServers": {
    "mysql": {
      "command": "mysql-mcp-server",
      "env": {
        "MYSQL_HOST": "your-rds.amazonaws.com",
        "MYSQL_PORT": "3306",
        "MYSQL_USER": "your_username",
        "MYSQL_PASSWORD": "your_password",
        "MYSQL_DATABASE": "your_database"
      }
    }
  }
}
```

**If your MySQL is on a local network (192.168.x.x, etc.):**

**Option 1: Use SSH Tunnel (Recommended)**

SSH tunnel forwards the remote MySQL port to your local machine, allowing Cursor to access it via localhost.

**Step-by-step guide:**

1. **Open a terminal** and run this command (keep the terminal open):

```bash
ssh -L 3307:192.168.1.200:3306 user@192.168.1.200
```

Replace:
- `3307` - Local port (can be any unused port)
- `192.168.1.200:3306` - Your MySQL server IP and port
- `user@192.168.1.200` - Your SSH username and server IP

2. **Enter your SSH password** when prompted

3. **Keep the terminal window open** (minimize it, don't close it)

4. **Update your Cursor config** to use localhost:

```json
{
  "mcpServers": {
    "mysql": {
      "command": "mysql-mcp-server",
      "env": {
        "MYSQL_HOST": "127.0.0.1",
        "MYSQL_PORT": "3307",
        "MYSQL_USER": "your_username",
        "MYSQL_PASSWORD": "your_password",
        "MYSQL_DATABASE": "your_database"
      }
    }
  }
}
```

Key changes:
- ✅ `MYSQL_HOST`: Change from `192.168.1.200` to `127.0.0.1`
- ✅ `MYSQL_PORT`: Change from `3306` to `3307` (match the local port in SSH command)
- ⚠️ Keep username, password, and database unchanged

5. **Restart Cursor** completely and test the connection

See [CURSOR_NETWORK_WORKAROUND.md](CURSOR_NETWORK_WORKAROUND.md) for detailed tutorial with screenshots and troubleshooting.

**Option 2: Test if it's a Cursor limitation**

Run the test script to verify the connection works outside of Cursor:

```bash
export MYSQL_HOST=192.168.1.200
export MYSQL_PORT=3306
export MYSQL_USER=your_username
export MYSQL_PASSWORD=your_password
export MYSQL_DATABASE=your_database

node test-connection.js
```

If the test succeeds but Cursor fails, it confirms Cursor's sandbox is blocking the connection.

**Option 3: Use Docker**

Run the MCP server in Docker, which typically has fewer network restrictions.

See [CURSOR_NETWORK_WORKAROUND.md](CURSOR_NETWORK_WORKAROUND.md) for detailed solutions and alternatives.

### Query Rejected

**Problem**: Query is rejected with validation error

**Solutions**:
- Add WHERE clause to DELETE/UPDATE queries
- Add LIMIT to SELECT queries (or let server add it automatically)
- Check if DDL operations are needed (set `ALLOW_DDL=true`)
- Verify query syntax

### Transaction Errors

**Problem**: Transaction commit/rollback fails

**Solutions**:
- Ensure transaction was started with `begin_transaction`
- Check for connection issues
- Verify no nested transactions (not supported)
- Check MySQL logs for database-level errors

### Logging

Enable debug logging to troubleshoot issues:

```json
{
  "env": {
    "MCP_LOG_LEVEL": "debug"
  }
}
```

Logs are written to stderr and won't interfere with MCP protocol communication.

## Contributing

Contributions are welcome! Please:

1. Fork the repository
2. Create a feature branch
3. Write tests for new functionality
4. Ensure all tests pass
5. Submit a pull request

## License

MIT

## Acknowledgments

- Built with the [Model Context Protocol SDK](https://github.com/modelcontextprotocol/sdk)
- Uses [mysql2](https://github.com/sidorares/node-mysql2) for MySQL connectivity
- Property-based testing with [fast-check](https://github.com/dubzzz/fast-check)

## Support

For issues, questions, or contributions, please visit the project repository.