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
50 changes: 36 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,31 +130,45 @@ A vector store table has the following columns:

All configuration is via environment variables (typically set in a `.env` file):

| Variable | Description | Required | Default |
|------------------------|--------------------------------------------------------|----------|--------------|
| `DB_HOST` | MariaDB host address | Yes | `localhost` |
| `DB_PORT` | MariaDB port | No | `3306` |
| `DB_USER` | MariaDB username | Yes | |
| `DB_PASSWORD` | MariaDB password | Yes | |
| `DB_NAME` | Default database (optional; can be set per query) | No | |
| Variable | Description | Required | Default |
| ------------------------ | ---------------------------------------------------- | --------------------------------------- | ---------------- |
| `DB_HOST` | MariaDB host address | Yes | `localhost` |
| `DB_PORT` | MariaDB port | No | `3306` |
| `DB_USER` | MariaDB username | Yes | |
| `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 |
| `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)|
| `OPENAI_API_KEY` | API key for OpenAI embeddings | Yes (if EMBEDDING_PROVIDER=openai) | |
| `GEMINI_API_KEY` | API key for Gemini embeddings | Yes (if EMBEDDING_PROVIDER=gemini) | |
| `HF_MODEL` | Open models from Huggingface | Yes (if EMBEDDING_PROVIDER=huggingface) | |
| `DB_SSL` | Enable SSL/TLS connections (`true`/`false`) | No | `false` |
| `DB_SSL_CA` | Path to SSL certificate authority file | No | |
| `DB_SSL_CERT` | Path to SSL client certificate file | No | |
| `DB_SSL_KEY` | Path to SSL client private key file | No | |
| `DB_SSL_VERIFY_CERT` | Verify SSL certificate (`true`/`false`) | No | `true` |
| `DB_SSL_VERIFY_IDENTITY` | Verify SSL server 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) |
| `OPENAI_API_KEY` | API key for OpenAI embeddings | Yes (if EMBEDDING_PROVIDER=openai) | |
| `GEMINI_API_KEY` | API key for Gemini embeddings | Yes (if EMBEDDING_PROVIDER=gemini) | |
| `HF_MODEL` | Open models from Huggingface | Yes (if EMBEDDING_PROVIDER=huggingface) | |

#### Example `.env` file

**With Embedding Support (OpenAI):**
**With Embedding Support (OpenAI) and SSL:**
```dotenv
DB_HOST=localhost
DB_USER=your_db_user
DB_PASSWORD=your_db_password
DB_PORT=3306
DB_NAME=your_default_database

# SSL Configuration
DB_SSL=true
DB_SSL_CA=/path/to/ca-cert.pem
DB_SSL_CERT=/path/to/client-cert.pem
DB_SSL_KEY=/path/to/client-key.pem
DB_SSL_VERIFY_CERT=true
DB_SSL_VERIFY_IDENTITY=false

MCP_READ_ONLY=true
MCP_MAX_POOL_SIZE=10

Expand All @@ -175,6 +189,14 @@ MCP_READ_ONLY=true
MCP_MAX_POOL_SIZE=10
```

**SSL Configuration Notes:**

- Set `DB_SSL=true` to enable SSL/TLS connections
- Certificate paths should be absolute paths to the certificate files
- Three levels of SSL verification:
1. **Basic SSL** (`DB_SSL=true`, no certificates): Encrypts connection without certificate verification
2. **Certificate verification** (`DB_SSL_VERIFY_CERT=true`): Verifies server certificate against CA
3. **Full verification** (`DB_SSL_VERIFY_CERT=true`, `DB_SSL_VERIFY_IDENTITY=true`): Verifies certificate and server identity
---

## Installation & Setup
Expand Down
8 changes: 8 additions & 0 deletions src/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,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") # Path to CA certificate
DB_SSL_CERT = os.getenv("DB_SSL_CERT") # Path to client certificate
DB_SSL_KEY = os.getenv("DB_SSL_KEY") # Path to client private 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
49 changes: 42 additions & 7 deletions src/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import re
from typing import List, Dict, Any, Optional
from functools import partial
import os
import ssl

import asyncmy
import anyio
Expand All @@ -13,6 +15,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,
logger
)
Expand Down Expand Up @@ -72,6 +75,43 @@ async def initialize_pool(self):
return

try:
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})")

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

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

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 @@ -81,15 +121,10 @@ async def initialize_pool(self):
"minsize": 1,
"maxsize": MCP_MAX_POOL_SIZE,
"autocommit": self.autocommit,
"pool_recycle": 3600
"pool_recycle": 3600,
"ssl_context"=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