Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,12 @@ All configuration is via environment variables (typically set in a `.env` file):
| `DB_PASSWORD` | MariaDB password | Yes | |
| `DB_NAME` | Default database (optional; can be set per query) | No | |
| `DB_CHARSET` | Character set for database connection (e.g., `cp1251`) | No | MariaDB default |
| `DB_SSL` | Enable SSL/TLS for database connection (`true`/`false`) | No | `false` |
| `DB_SSL_CA` | Path to CA certificate file for SSL verification | No | |
| `DB_SSL_CERT` | Path to client certificate file for SSL authentication | No | |
| `DB_SSL_KEY` | Path to client private key file for SSL authentication | No | |
| `DB_SSL_VERIFY_CERT` | Verify server certificate (`true`/`false`) | No | `true` |
| `DB_SSL_VERIFY_IDENTITY` | Verify server hostname identity (`true`/`false`) | No | `false` |
| `MCP_READ_ONLY` | Enforce read-only SQL mode (`true`/`false`) | No | `true` |
| `MCP_MAX_POOL_SIZE` | Max DB connection pool size | No | `10` |
| `EMBEDDING_PROVIDER` | Embedding provider (`openai`/`gemini`/`huggingface`) | No |`None`(Disabled)|
Expand Down Expand Up @@ -179,6 +185,33 @@ MCP_READ_ONLY=true
MCP_MAX_POOL_SIZE=10
```

**With SSL/TLS Enabled:**
```dotenv
DB_HOST=your-remote-host.com
DB_USER=your_db_user
DB_PASSWORD=your_db_password
DB_PORT=3306
DB_NAME=your_default_database

# Enable SSL
DB_SSL=true
DB_SSL_CA=~/.mysql/ca-cert.pem
DB_SSL_CERT=~/.mysql/client-cert.pem
DB_SSL_KEY=~/.mysql/client-key.pem
DB_SSL_VERIFY_CERT=true
DB_SSL_VERIFY_IDENTITY=false

MCP_READ_ONLY=true
MCP_MAX_POOL_SIZE=10
```

**Note on SSL Configuration:**
- All SSL certificate paths support `~` for home directory expansion
- `DB_SSL_CA` is used to verify the server's certificate
- `DB_SSL_CERT` and `DB_SSL_KEY` are used for client certificate authentication (mutual TLS)
- Set `DB_SSL_VERIFY_CERT=false` only for testing with self-signed certificates
- Set `DB_SSL_VERIFY_IDENTITY=true` to enable strict hostname verification

**Example Authentication Configuration:**
This configuration uses external web authentication via GitHub or Google. If you have internal JWT authentication (desired for organizations who manage their own services), you can use the JWT provider instead.

Expand Down
8 changes: 8 additions & 0 deletions src/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@
DB_NAME = os.getenv("DB_NAME")
DB_CHARSET = os.getenv("DB_CHARSET")

# --- SSL Configuration ---
DB_SSL = os.getenv("DB_SSL", "false").lower() == "true"
DB_SSL_CA = os.getenv("DB_SSL_CA")
DB_SSL_CERT = os.getenv("DB_SSL_CERT")
DB_SSL_KEY = os.getenv("DB_SSL_KEY")
DB_SSL_VERIFY_CERT = os.getenv("DB_SSL_VERIFY_CERT", "true").lower() == "true"
DB_SSL_VERIFY_IDENTITY = os.getenv("DB_SSL_VERIFY_IDENTITY", "false").lower() == "true"

# --- MCP Server Configuration ---
# Read-only mode
MCP_READ_ONLY = os.getenv("MCP_READ_ONLY", "true").lower() == "true"
Expand Down
46 changes: 43 additions & 3 deletions src/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# Import configuration settings
from config import (
DB_HOST, DB_PORT, DB_USER, DB_PASSWORD, DB_NAME, DB_CHARSET,
DB_SSL, DB_SSL_CA, DB_SSL_CERT, DB_SSL_KEY, DB_SSL_VERIFY_CERT, DB_SSL_VERIFY_IDENTITY,
MCP_READ_ONLY, MCP_MAX_POOL_SIZE, EMBEDDING_PROVIDER,
ALLOWED_ORIGINS, ALLOWED_HOSTS,
logger
Expand All @@ -12,7 +13,9 @@
import argparse
import re
from typing import List, Dict, Any, Optional
from functools import partial
from functools import partial
import os
import ssl

import asyncmy
import anyio
Expand Down Expand Up @@ -77,6 +80,41 @@ async def initialize_pool(self):
return

try:
ssl_context = None
if DB_SSL:
ssl_context = ssl.create_default_context()
if DB_SSL_CA:
ca_path = os.path.expanduser(DB_SSL_CA)
if os.path.exists(ca_path):
ssl_context.load_verify_locations(cafile=ca_path)
logger.info(f"Loaded SSL CA certificate: {ca_path}")
else:
logger.warning(f"SSL CA certificate file not found: {ca_path}")

if DB_SSL_CERT and DB_SSL_KEY:
cert_path = os.path.expanduser(DB_SSL_CERT)
key_path = os.path.expanduser(DB_SSL_KEY)
if os.path.exists(cert_path) and os.path.exists(key_path):
ssl_context.load_cert_chain(cert_path, key_path)
logger.info(f"Loaded SSL client certificate: {cert_path}")
else:
logger.warning(f"SSL client certificate files not found: cert={cert_path}, key={key_path}")

if not DB_SSL_VERIFY_CERT:
ssl_context.check_hostname = False
ssl_context.verify_mode = ssl.CERT_NONE
logger.info("SSL certificate verification disabled")
elif not DB_SSL_VERIFY_IDENTITY:
ssl_context.check_hostname = False
ssl_context.verify_mode = ssl.CERT_REQUIRED
logger.info("SSL hostname verification disabled, certificate verification enabled")
else:
logger.info("Full SSL verification enabled")

logger.info("SSL enabled for database connection")
else:
logger.info("SSL disabled for database connection")

pool_params = {
"host": DB_HOST,
"port": DB_PORT,
Expand All @@ -88,13 +126,15 @@ async def initialize_pool(self):
"autocommit": self.autocommit,
"pool_recycle": 3600
}

if DB_SSL and ssl_context is not None:
pool_params["ssl"] = ssl_context

if DB_CHARSET:
pool_params["charset"] = DB_CHARSET
logger.info(f"Creating connection pool for {DB_USER}@{DB_HOST}:{DB_PORT}/{DB_NAME} (max size: {MCP_MAX_POOL_SIZE}, charset: {DB_CHARSET})")
else:
logger.info(f"Creating connection pool for {DB_USER}@{DB_HOST}:{DB_PORT}/{DB_NAME} (max size: {MCP_MAX_POOL_SIZE})")

self.pool = await asyncmy.create_pool(**pool_params)
logger.info("Connection pool initialized successfully.")
except AsyncMyError as e:
Expand Down