Skip to main content

Overview

The servers in this repository are reference implementations intended for educational purposes. They demonstrate MCP features and SDK usage but are not production-ready solutions. Evaluate your security requirements and implement appropriate safeguards based on your threat model.
This guide covers security best practices for deploying MCP servers safely and securely.

Read-Only Access

Oracle Database

All queries executed through the Oracle MCP server are run within READ ONLY transactions:
-- Automatic transaction mode
SET TRANSACTION READ ONLY;
Tools with read-only enforcement:
  • orcl-query - Executes SELECT queries only
  • orcl-explain - Analyzes query execution plans
  • orcl-stats - Retrieves table statistics
  • orcl-awr - Generates Automatic Workload Repository reports
The orcl-awr tool requires SELECT_CATALOG_ROLE and EXECUTE privilege on DBMS_WORKLOAD_REPOSITORY package.

MySQL Database

The MySQL MCP server enforces read-only transactions for all query operations:
-- All queries run in read-only mode
START TRANSACTION READ ONLY;
Read-only tools:
  • mysql-query - Executes SELECT statements only
  • mysql-explain - Returns execution plans in JSON format
  • mysql-stats - Provides table, index, and column statistics
  • mysql-awr - Generates performance reports (requires performance_schema)

MikroTik RouterOS

The MikroTik MCP server is designed as a read-only interface:
  • All API calls use /print commands
  • No write operations (/add, /set, /remove) are supported
  • Resources provide inspection capabilities only
Even though the server is read-only, you should still create a dedicated user with minimal privileges for MCP access.

Credential Management

Environment Variables

Use environment variables instead of hardcoding credentials:
// Good - Uses environment variables
{
  "mcpServers": {
    "mysql": {
      "command": "docker",
      "args": ["run", "-i", "--rm"],
      "env": {
        "MYSQL_USER": "readonly_user",
        "MYSQL_PASSWORD": "secure_password"
      }
    }
  }
}
// Bad - Credentials in args
{
  "mcpServers": {
    "mysql": {
      "command": "npx",
      "args": ["-y", "@marcelo-ochoa/server-mysql", 
               "--user=admin", "--password=secret123"]
    }
  }
}

Connection Strings Without Credentials

Always separate credentials from connection strings:
# Correct - Credentials via environment
docker run -i --rm \
  -e ORACLE_USER=readonly \
  -e ORACLE_PASSWORD=secure123 \
  mochoa/mcp-oracle \
  host.docker.internal:1521/freepdb1

# Incorrect - Credentials in connection string
# oracle://readonly:secure123@host:1521/freepdb1
# Correct
docker run -i --rm \
  -e MYSQL_USER=readonly \
  -e MYSQL_PASSWORD=secure123 \
  mochoa/mcp-mysql \
  host.docker.internal:3306/mydb

# Incorrect
# mysql://readonly:secure123@host:3306/mydb

Runtime Connection Tools

For enhanced security, use runtime connection tools instead of startup credentials:
# Start server without credentials
docker run -i --rm mochoa/mcp-oracle

# Connect at runtime using tool
orcl-connect host.docker.internal:1521/freepdb1 readonly_user secure_password
Benefits:
  • Credentials not stored in configuration files
  • Easier credential rotation
  • Better audit trail
  • Reduced exposure in logs

Database User Permissions

Create a dedicated read-only user for MCP access:
-- Create read-only user
CREATE USER mcp_readonly IDENTIFIED BY secure_password;

-- Grant minimal permissions
GRANT CONNECT TO mcp_readonly;
GRANT SELECT ON schema_name.* TO mcp_readonly;

-- Optional: For explain plans
GRANT SELECT_CATALOG_ROLE TO mcp_readonly;

-- Optional: For AWR reports
GRANT SELECT_CATALOG_ROLE TO mcp_readonly;
GRANT EXECUTE ON DBMS_WORKLOAD_REPOSITORY TO mcp_readonly;
Create a restricted user with read-only access:
-- Create read-only user
CREATE USER 'mcp_readonly'@'%' IDENTIFIED BY 'secure_password';

-- Grant SELECT only
GRANT SELECT ON mydb.* TO 'mcp_readonly'@'%';

-- Optional: For performance schema access (AWR reports)
GRANT SELECT ON performance_schema.* TO 'mcp_readonly'@'%';

-- Apply changes
FLUSH PRIVILEGES;
Create a dedicated user with read-only API access:
# Create read-only user
/user add name=mcp_readonly password=secure_password group=read

# Configure API access
/ip service set api address=192.168.88.0/24
/ip service set api-ssl address=192.168.88.0/24
Never use administrative or root accounts for MCP server connections. Always create dedicated accounts with minimal necessary privileges.

Network Security

Firewall Configuration

1

Restrict Database Access

Configure firewalls to allow connections only from trusted MCP client IPs:
# MySQL example
sudo ufw allow from 192.168.1.100 to any port 3306

# Oracle example
sudo ufw allow from 192.168.1.100 to any port 1521
2

Use VPN or SSH Tunnels

For remote access, use VPN or SSH tunnels instead of exposing databases directly:
# SSH tunnel example
ssh -L 3306:localhost:3306 user@remote-server
3

Enable SSL/TLS

Use encrypted connections when supported:
# MikroTik with SSL
docker run -i --rm \
  -e MK_USER=admin \
  -e MK_PASSWORD=pass \
  mochoa/mcp-mikrotik \
  192.168.88.1 \
  true

MikroTik Protocol Security

  • Plain TCP (Port 8728): Unencrypted - use only on trusted networks
  • SSL/TLS (Port 8729): Encrypted - recommended for remote access
# Enable API-SSL on MikroTik
/ip service enable api-ssl
/ip service set api-ssl port=8729
/ip service set api-ssl certificate=your-certificate

Docker Security

Container Isolation

# Run with read-only root filesystem
docker run -i --rm --read-only mochoa/mcp-mysql

# Drop unnecessary capabilities
docker run -i --rm --cap-drop=ALL mochoa/mcp-mysql

# Use non-root user (if image supports)
docker run -i --rm --user 1000:1000 mochoa/mcp-mysql

Secrets Management

For production deployments, use Docker secrets or external secret managers:
# Using Docker secrets
echo "mypassword" | docker secret create mysql_password -

docker service create \
  --name mcp-mysql \
  --secret mysql_password \
  -e MYSQL_PASSWORD_FILE=/run/secrets/mysql_password \
  mochoa/mcp-mysql

Network Segmentation

# Create isolated network
docker network create --internal mcp-network

# Run containers on isolated network
docker run -i --rm --network mcp-network mochoa/mcp-mysql

Audit and Monitoring

Enable Database Audit Logs

-- Enable unified auditing
AUDIT SELECT ON schema_name.sensitive_table BY mcp_readonly;

-- Review audit logs
SELECT * FROM UNIFIED_AUDIT_TRAIL 
WHERE DBUSERNAME = 'MCP_READONLY';
-- Enable general query log (development only)
SET GLOBAL general_log = 'ON';
SET GLOBAL log_output = 'TABLE';

-- Review query log
SELECT * FROM mysql.general_log 
WHERE user_host LIKE '%mcp_readonly%';
# Enable API logging
/system logging add topics=api action=memory

# View logs
/log print where topics~"api"

Monitor MCP Server Activity

Review MCP server logs for suspicious activity:
# Docker logs
docker logs container-name

# Filter for authentication attempts
docker logs container-name | grep -i "auth\|connect\|error"

Configuration File Security

File Permissions

# Restrict access to configuration files
chmod 600 ~/.config/claude/claude_desktop_config.json
chmod 600 ~/.vscode/mcp.json
chmod 600 ~/.gemini/antigravity/mcp_config.json

Avoid Committing Secrets

Add configuration files to .gitignore:
# MCP configurations with secrets
clause_desktop_config.json
mcp_config.json
.vscode/mcp.json
.env

SSL/TLS Certificates

MikroTik Certificate Setup

# Generate self-signed certificate
/certificate add name=mcp-cert common-name=mcp.local \
  key-size=2048 days-valid=365
/certificate sign mcp-cert

# Apply to API-SSL service
/ip service set api-ssl certificate=mcp-cert

MySQL SSL Configuration

[mysqld]
ssl-ca=/path/to/ca.pem
ssl-cert=/path/to/server-cert.pem
ssl-key=/path/to/server-key.pem
require_secure_transport=ON

Incident Response

1

Detect Unauthorized Access

Monitor database audit logs and MCP server logs for suspicious queries or connection attempts.
2

Rotate Credentials

Immediately change passwords for MCP users if compromise is suspected:
-- MySQL
ALTER USER 'mcp_readonly'@'%' IDENTIFIED BY 'new_secure_password';

-- Oracle
ALTER USER mcp_readonly IDENTIFIED BY new_secure_password;
3

Review Access Logs

Identify which resources were accessed and determine data exposure.
4

Update Configuration

Update all MCP client configurations with new credentials and restart services.

Security Checklist

  • Use dedicated read-only database users
  • Store credentials in environment variables, not configuration files
  • Enable SSL/TLS for network connections
  • Restrict network access with firewalls
  • Set appropriate file permissions on configuration files
  • Enable database audit logging
  • Monitor MCP server and database logs
  • Use Docker security features (read-only, cap-drop, etc.)
  • Implement secrets management for production
  • Regular credential rotation
  • Keep server images updated with security patches

Build docs developers (and LLMs) love