npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@braincode-tech/database-mcp

v2.0.0

Published

MCP Server for multiple databases (ClickHouse, PostgreSQL, MySQL) with SSH Tunnel and Multi-Cluster Support

Downloads

16

Readme

Database MCP Server

A Model Context Protocol (MCP) server that enables AI assistants to securely interact with multiple database types (ClickHouse, PostgreSQL, MySQL) through optional SSH tunneling.

Overview

This MCP server provides a bridge between AI assistants (like Claude, Ollama, Microsoft Copilot) and various databases, allowing natural language queries and database exploration with enterprise-grade security features.

Supported Databases

| Database | Default Port | EXPLAIN Types | Features | |----------|--------------|---------------|----------| | ClickHouse | 8123 | AST, SYNTAX, PLAN, PIPELINE, ESTIMATE | Columnar analytics, high performance | | PostgreSQL | 5432 | ANALYZE, VERBOSE, COSTS, BUFFERS, FORMAT | Full-featured RDBMS, JSON support | | MySQL | 3306 | EXTENDED, PARTITIONS, FORMAT | Wide compatibility, mature ecosystem |

Architecture

flowchart LR
    subgraph Clients["AI Clients"]
        CC[Claude Code<br/>stdio transport]
        OL[Ollama / LLama<br/>HTTP transport]
        CP[Microsoft Copilot<br/>HTTP + Auth]
    end

    subgraph MCP["MCP Server"]
        TR[Transport Layer]
        TL[8 Tools]
        CR[Cluster Registry]
        DB[Database Adapters]
    end

    subgraph Databases["Databases"]
        SSH[SSH Tunnel<br/>optional]
        CH[(ClickHouse)]
        PG[(PostgreSQL)]
        MY[(MySQL)]
    end

    CC -->|JSON-RPC| TR
    OL -->|REST API| TR
    CP -->|REST API| TR
    TR --> TL
    TL --> CR
    CR --> DB
    DB --> SSH
    SSH --> CH
    SSH --> PG
    SSH --> MY
    DB -.->|Direct Connection| CH
    DB -.->|Direct Connection| PG
    DB -.->|Direct Connection| MY

Deployment Scenarios

Choose the deployment scenario that matches your environment:

Scenario 1: Development (Local with Internet)

Best for: Local development with Claude Code

flowchart LR
    CC[Claude Code] -->|stdio| MCP[MCP Server]
    MCP --> CH[(ClickHouse)]
    MCP --> PG[(PostgreSQL)]
    MCP --> MY[(MySQL)]
# Quick start
npm install && npm run build
# Claude Code auto-detects via .mcp.json

Scenario 2: Cloud/Remote with SSH Tunnel

Best for: Accessing databases behind bastion/jump host

flowchart LR
    AI[AI Client] --> MCP[MCP Server<br/>localhost]
    MCP --> SSH[SSH Bastion]
    SSH --> DB[(Databases<br/>Private Network)]

    style SSH fill:#f9f,stroke:#333
# Uses SSH tunnel configuration in clusters.json
docker compose up -d

Scenario 3: Internal Network (No SSH)

Best for: MCP Server on same network as databases

flowchart LR
    AI[AI Client] --> MCP[MCP Server]
    MCP --> DB[(Databases<br/>Same Network)]

    style MCP fill:#9f9,stroke:#333
# Direct connection - no SSH needed
# Edit config/clusters.internal.json
docker compose -f docker-compose.prod.yml up -d

Scenario 4: Air-Gapped / Offline Environment

Best for: Internal servers without internet access (e.g., enterprise data centers)

flowchart TB
    subgraph Internal["Internal Server (No Internet)"]
        OL[Ollama + LLama<br/>:11434]
        MCP[MCP Server<br/>:3100]
        DB[(Databases<br/>Existing)]

        OL -->|HTTP API| MCP
        MCP -->|Direct| DB
    end

    USER[User / Application] --> OL
# On internet-connected machine:
./scripts/prepare-offline.sh

# Transfer offline-package/ to internal server

# On internal server:
./install.sh
docker compose -f docker-compose.prod.yml up -d

Air-Gapped Deployment Guide - Complete instructions for offline deployment

Scenario 5: Microsoft 365 Copilot Integration

Best for: Enterprise environments using Microsoft Copilot as official AI assistant

flowchart LR
    subgraph Microsoft["Microsoft Cloud"]
        CS[Copilot Studio<br/>M365 Copilot]
    end

    subgraph Azure["Azure"]
        AR[Azure Relay<br/>Hybrid Connection]
    end

    subgraph OnPrem["On-Premises"]
        MCP[MCP Server<br/>:3100]
        DB[(Databases)]
    end

    CS -->|"MCP Protocol"| AR
    AR -->|"Outbound Only"| MCP
    MCP --> DB

Requirements:

  • Azure Relay for secure tunnel (outbound-only)
  • API Key authentication enabled
  • Security team approval for outbound connection

Copilot Integration Guide - Complete setup guide + security proposal template

Features

  • 8 MCP Tools for comprehensive database interaction
  • Multi-Database Support - ClickHouse, PostgreSQL, MySQL
  • Multi-Cluster Support - Manage multiple database instances
  • SSH Tunneling - Secure connections through bastion hosts
  • Dual Transport - Support for both stdio (Claude Code) and HTTP (Ollama/Copilot)
  • Security First - Read-only queries, SQL injection prevention
  • Environment Variable Substitution - Secure credential management

Quick Start

Quick Start by Scenario

Choose your deployment scenario and run the corresponding commands:

Option 1: Development with Claude Code (stdio)

# Use development environment template
cp config/env/.env.development .env

# Install and build
npm install && npm run build

# Claude Code auto-detects via .mcp.json

Option 2: Air-Gapped with Ollama (HTTP)

# Use Ollama environment template
cp config/env/.env.ollama .env

# Edit .env with your database passwords
# CH_PASSWORD=your_clickhouse_password
# PG_PASSWORD=your_postgres_password
# MYSQL_PASSWORD=your_mysql_password

# Start with Docker
docker compose -f docker-compose.prod.yml up -d

Option 3: Microsoft Copilot Integration (HTTP + Auth)

# Use Copilot environment template
cp config/env/.env.copilot .env

# Edit .env with your credentials
# API_KEY=your_secure_api_key (generate: openssl rand -base64 32)
# CH_PASSWORD=your_clickhouse_password

# Start with Docker
docker compose -f docker-compose.prod.yml up -d

# Register MCP URL in Copilot Studio

Configuration Templates: See config/env/ for all available environment templates.


Installation via npm (Recommended for Developers)

Install globally or use via npx for easy integration with Claude Code.

Option A: Using npx (No Installation)

npx @braincode-tech/database-mcp

Option B: Global Installation

npm install -g @braincode-tech/database-mcp
database-mcp

Configuration for npm Usage

When using via npm/npx, you need to provide your own configuration:

Step 1: Create Configuration Directory

# Linux/macOS
mkdir -p ~/.database-mcp

# Windows (PowerShell)
New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.database-mcp"

Step 2: Create clusters.json

Create ~/.database-mcp/clusters.json:

Multi-Database Configuration:

{
  "clusters": {
    "clickhouse-analytics": {
      "name": "ClickHouse Analytics",
      "ssh": {
        "host": "bastion.yourcompany.com",
        "port": 22,
        "user": "your-username",
        "keyPath": "~/.ssh/id_rsa"
      },
      "connection": {
        "type": "clickhouse",
        "host": "clickhouse.internal",
        "port": 8123,
        "database": "analytics",
        "user": "readonly",
        "password": "${CH_PASSWORD}"
      }
    },
    "postgres-app": {
      "name": "PostgreSQL App",
      "connection": {
        "type": "postgres",
        "host": "localhost",
        "port": 5432,
        "database": "myapp",
        "user": "reader",
        "password": "${PG_PASSWORD}",
        "schema": "public"
      }
    },
    "mysql-legacy": {
      "name": "MySQL Legacy",
      "connection": {
        "type": "mysql",
        "host": "localhost",
        "port": 3306,
        "database": "legacy_db",
        "user": "readonly",
        "password": "${MYSQL_PASSWORD}"
      }
    }
  },
  "defaultCluster": "clickhouse-analytics"
}

Direct Connection (Same Network):

{
  "clusters": {
    "local": {
      "name": "Local ClickHouse",
      "connection": {
        "type": "clickhouse",
        "host": "localhost",
        "port": 8123,
        "database": "default",
        "user": "default",
        "password": ""
      }
    }
  },
  "defaultCluster": "local"
}

Step 3: Configure Claude Code (.mcp.json)

Create or edit ~/.mcp.json (global) or .mcp.json (project):

{
  "mcpServers": {
    "database": {
      "command": "npx",
      "args": ["@braincode-tech/database-mcp"],
      "env": {
        "CLUSTERS_CONFIG_PATH": "~/.database-mcp/clusters.json",
        "CH_PASSWORD": "your_clickhouse_password",
        "PG_PASSWORD": "your_postgres_password",
        "MYSQL_PASSWORD": "your_mysql_password",
        "MCP_TRANSPORT": "stdio"
      }
    }
  }
}

Windows Users

Use full paths in .mcp.json:

{
  "mcpServers": {
    "database": {
      "command": "npx",
      "args": ["@braincode-tech/database-mcp"],
      "env": {
        "CLUSTERS_CONFIG_PATH": "C:\\Users\\YourName\\.database-mcp\\clusters.json",
        "CH_PASSWORD": "your_clickhouse_password",
        "PG_PASSWORD": "your_postgres_password",
        "MYSQL_PASSWORD": "your_mysql_password",
        "MCP_TRANSPORT": "stdio"
      }
    }
  }
}

Step 4: Test Connection

In Claude Code, try:

"List all database clusters"
"Show tables in the database"
"Run query: SELECT version()"

Security Best Practices

| Practice | Description | |----------|-------------| | SSH Key Auth | Use keyPath instead of password for SSH | | Read-only User | Create database users with read-only permissions | | Environment Variables | Use ${VAR} syntax for sensitive data | | Local Config | Keep clusters.json outside version control |


Prerequisites

  • Node.js >= 20.0.0
  • npm or yarn
  • SSH access to your database server (if using SSH tunnel)
  • Database servers with their respective interfaces enabled

Installation

# Clone the repository
git clone https://github.com/braincode-tech/database-mcp.git
cd database-mcp

# Install dependencies
npm install

# Build the project
npm run build

Configuration

1. Environment Variables

Create a .env file in the project root:

# Transport mode: stdio (for Claude) or http (for Ollama)
MCP_TRANSPORT=stdio

# HTTP settings (only for http transport)
HTTP_PORT=3100
HTTP_HOST=0.0.0.0

# Logging level: debug, info, warn, error
LOG_LEVEL=info

# Path to cluster configuration
CLUSTERS_CONFIG_PATH=./config/clusters.json

# Database credentials (referenced in clusters.json)
CH_PASSWORD=your_clickhouse_password
PG_PASSWORD=your_postgres_password
MYSQL_PASSWORD=your_mysql_password

2. Cluster Configuration

Create config/clusters.json:

Option A: Multi-Database with SSH Tunnel

{
  "clusters": {
    "clickhouse-analytics": {
      "name": "ClickHouse Analytics",
      "ssh": {
        "host": "bastion.example.com",
        "port": 22,
        "user": "ssh-user",
        "keyPath": "/path/to/key.pem"
      },
      "connection": {
        "type": "clickhouse",
        "host": "clickhouse.internal",
        "port": 8123,
        "database": "analytics",
        "user": "readonly",
        "password": "${CH_PASSWORD}"
      }
    },
    "postgres-app": {
      "name": "PostgreSQL App",
      "connection": {
        "type": "postgres",
        "host": "postgres.internal",
        "port": 5432,
        "database": "myapp",
        "user": "reader",
        "password": "${PG_PASSWORD}",
        "schema": "public"
      }
    },
    "mysql-legacy": {
      "name": "MySQL Legacy",
      "connection": {
        "type": "mysql",
        "host": "mysql.internal",
        "port": 3306,
        "database": "legacy_db",
        "user": "readonly",
        "password": "${MYSQL_PASSWORD}"
      }
    }
  },
  "defaultCluster": "clickhouse-analytics"
}

Option B: Direct Connection (No SSH)

{
  "clusters": {
    "clickhouse-local": {
      "name": "Local ClickHouse",
      "connection": {
        "type": "clickhouse",
        "host": "localhost",
        "port": 8123,
        "database": "default",
        "user": "default",
        "password": ""
      }
    },
    "postgres-local": {
      "name": "Local PostgreSQL",
      "connection": {
        "type": "postgres",
        "host": "localhost",
        "port": 5432,
        "database": "postgres",
        "user": "postgres",
        "password": "${PG_PASSWORD}",
        "schema": "public"
      }
    }
  },
  "defaultCluster": "clickhouse-local"
}

Option C: Legacy ClickHouse Format (Still Supported)

{
  "clusters": {
    "legacy": {
      "name": "Legacy Format",
      "clickhouse": {
        "host": "localhost",
        "port": 8123,
        "database": "default",
        "user": "default",
        "password": ""
      }
    }
  },
  "defaultCluster": "legacy"
}

Database-Specific Configuration

ClickHouse Options

{
  "connection": {
    "type": "clickhouse",
    "host": "localhost",
    "port": 8123,
    "database": "default",
    "user": "default",
    "password": "${CH_PASSWORD}"
  }
}

PostgreSQL Options

{
  "connection": {
    "type": "postgres",
    "host": "localhost",
    "port": 5432,
    "database": "mydb",
    "user": "myuser",
    "password": "${PG_PASSWORD}",
    "schema": "public",
    "ssl": false
  }
}

MySQL Options

{
  "connection": {
    "type": "mysql",
    "host": "localhost",
    "port": 3306,
    "database": "mydb",
    "user": "myuser",
    "password": "${MYSQL_PASSWORD}"
  }
}

Usage

With Claude Code

Option A: Project-level configuration (Recommended)

The project includes a .mcp.json file. When you open this project in Claude Code, it will automatically detect and offer to enable the MCP server.

  1. Open the project directory in Claude Code
  2. Claude Code will prompt to enable the MCP server
  3. Approve the MCP server when prompted

Option B: Global configuration

Create or edit ~/.mcp.json to add the server globally:

{
  "mcpServers": {
    "database": {
      "command": "node",
      "args": ["D:\\path\\to\\database-mcp\\dist\\index.js"],
      "cwd": "D:\\path\\to\\database-mcp"
    }
  }
}

After configuration:

  1. Restart Claude Code

  2. Test the connection:

    • "List all database clusters"
    • "Show tables in the database"
    • "Run query: SELECT version()"

With Ollama (HTTP Transport)

  1. Set transport mode in .env:
MCP_TRANSPORT=http
HTTP_PORT=3100
  1. Start the server:
npm start
  1. The server exposes these endpoints:
    • GET /health - Health check
    • GET /tools - List available tools
    • POST /tools/:name - Call a tool by name
    • POST /v1/functions/:name - OpenAI-compatible format

With Docker

Docker deployment is ideal for production environments and easy integration with AI models via HTTP.

Docker Compose Files

| File | Use Case | |------|----------| | docker-compose.yml | Default - SSH tunnel to remote databases | | docker-compose.prod.yml | Production - Ollama + MCP for internal/air-gapped servers | | docker-compose.dev.yml | Development - Includes database servers |

Quick Start

# Development (with local databases)
docker compose -f docker-compose.dev.yml up -d

# Production (connect to existing databases)
docker compose -f docker-compose.prod.yml up -d

# Default (SSH tunnel)
docker compose up -d

# Check logs
docker compose logs -f

# Stop
docker compose down

Configuration

  1. Create .env file (environment variables):
MCP_TRANSPORT=http
HTTP_PORT=3100
LOG_LEVEL=info
CLUSTERS_CONFIG_PATH=./config/clusters.json
CH_PASSWORD=your_clickhouse_password
PG_PASSWORD=your_postgres_password
MYSQL_PASSWORD=your_mysql_password
  1. Create config/clusters.json (cluster configuration):
{
  "clusters": {
    "main": {
      "name": "Main Database",
      "connection": {
        "type": "clickhouse",
        "host": "clickhouse.internal",
        "port": 8123,
        "database": "analytics",
        "user": "readonly",
        "password": "${CH_PASSWORD}"
      }
    }
  },
  "defaultCluster": "main"
}
  1. Run Docker Compose:
docker compose up -d

Verify Container

# Check health
curl http://localhost:3100/health

# List available tools
curl http://localhost:3100/tools

# List clusters
curl -X POST http://localhost:3100/tools/list_clusters \
  -H "Content-Type: application/json" \
  -d '{}'

HTTP API Endpoints

| Endpoint | Method | Description | |----------|--------|-------------| | /health | GET | Health check with cluster status | | /tools | GET | List all available tools | | /tools/:name | POST | Call a tool by name | | /call | POST | Call a tool (name in request body) | | /v1/functions/:name | POST | OpenAI-compatible function call |

Example API Calls

Execute a Query:

curl -X POST http://localhost:3100/tools/query \
  -H "Content-Type: application/json" \
  -d '{"sql": "SELECT version()", "limit": 10}'

List Tables:

curl -X POST http://localhost:3100/tools/list_tables \
  -H "Content-Type: application/json" \
  -d '{"database": "default"}'

Get Sample Data:

curl -X POST http://localhost:3100/tools/get_sample \
  -H "Content-Type: application/json" \
  -d '{"table": "events", "rows": 5}'

Describe Table Schema:

curl -X POST http://localhost:3100/tools/describe_table \
  -H "Content-Type: application/json" \
  -d '{"table": "events"}'

Integration with AI Models

With Ollama/Open WebUI:

Configure the MCP endpoint as a function/tool provider:

Base URL: http://localhost:3100

With LangChain:

import requests

def call_database_tool(tool_name: str, params: dict):
    response = requests.post(
        f"http://localhost:3100/tools/{tool_name}",
        json=params
    )
    return response.json()

# Example: Execute query
result = call_database_tool("query", {
    "sql": "SELECT count() FROM events"
})

With Custom Applications:

async function queryDatabase(sql, limit = 100) {
  const response = await fetch('http://localhost:3100/tools/query', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ sql, limit })
  });
  return response.json();
}

// Example usage
const result = await queryDatabase('SELECT version()');
console.log(result);

Available Tools

Cluster Management

| Tool | Description | |------|-------------| | list_clusters | List all configured clusters with connection status | | switch_cluster | Switch to a different cluster | | get_active_cluster | Get information about the active cluster |

Query Execution

| Tool | Description | |------|-------------| | query | Execute SELECT queries (read-only) with automatic LIMIT |

Table Operations

| Tool | Description | |------|-------------| | list_tables | List tables in a database with row counts and sizes | | get_sample | Get sample rows from a table | | describe_table | Get detailed table schema (columns, types, keys) |

Query Analysis

| Tool | Description | |------|-------------| | explain_query | Get query execution plans (database-specific) |

Tool Reference

query

Execute a SELECT query on the database.

Parameters:

  • sql (required): The SQL SELECT query
  • limit (optional): Max rows to return (default: 100, max: 10000)
  • cluster (optional): Target cluster ID

Example:

Run query: SELECT count() FROM events WHERE date = today()

list_tables

List all tables in a database.

Parameters:

  • database (optional): Database name (uses default if not specified)
  • cluster (optional): Target cluster ID

Example:

List tables in the analytics database

describe_table

Get detailed table schema.

Parameters:

  • table (required): Table name (can include database prefix)
  • cluster (optional): Target cluster ID

Example:

Describe the events table

get_sample

Get sample data from a table.

Parameters:

  • table (required): Table name
  • rows (optional): Number of rows (default: 5, max: 100)
  • cluster (optional): Target cluster ID

Example:

Show 10 sample rows from users table

explain_query

Analyze query execution plan.

Parameters:

  • sql (required): The SQL query to analyze
  • type (optional): Explain type (database-specific, see table below)
  • cluster (optional): Target cluster ID

EXPLAIN Types by Database:

| Database | Available Types | |----------|-----------------| | ClickHouse | AST, SYNTAX, PLAN, PIPELINE, ESTIMATE | | PostgreSQL | ANALYZE, VERBOSE, COSTS, BUFFERS, FORMAT | | MySQL | EXTENDED, PARTITIONS, FORMAT |

Example:

Explain the execution plan for: SELECT * FROM events WHERE user_id = 123

Architecture

┌─────────────────┐     ┌──────────────────┐     ┌─────────────────┐
│  Claude Code    │────▶│  MCP Server      │────▶│  SSH Tunnel     │
│  (stdio)        │     │  (Node.js)       │     │  (ssh2)         │
└─────────────────┘     └──────────────────┘     └────────┬────────┘
                                                          │
┌─────────────────┐     ┌──────────────────┐              │
│  Ollama         │────▶│  HTTP Transport  │              │
│  (HTTP)         │     │  (Fastify)       │              ▼
└─────────────────┘     └──────────────────┘     ┌─────────────────┐
                                                 │  Database       │
┌─────────────────┐     ┌──────────────────┐     │  Adapters       │
│  Copilot        │────▶│  API Key Auth    │     ├─────────────────┤
│  (HTTP + Auth)  │     │                  │     │  ClickHouse     │
└─────────────────┘     └──────────────────┘     │  PostgreSQL     │
                                                 │  MySQL          │
                                                 └─────────────────┘

Project Structure

database-mcp/
├── src/
│   ├── index.ts              # Entry point
│   ├── server.ts             # MCP Server implementation
│   ├── tools/                # Tool implementations
│   │   ├── clusters.ts       # Cluster management tools
│   │   ├── query.ts          # Query execution
│   │   ├── tables.ts         # Table operations
│   │   ├── schema.ts         # Schema inspection
│   │   └── explain.ts        # Query analysis
│   ├── databases/            # Database adapters
│   │   ├── database-client.ts    # Abstract interface
│   │   ├── database-factory.ts   # Adapter factory
│   │   ├── clickhouse-adapter.ts # ClickHouse implementation
│   │   ├── postgres-adapter.ts   # PostgreSQL implementation
│   │   └── mysql-adapter.ts      # MySQL implementation
│   ├── clusters/
│   │   ├── cluster-config.ts # Type definitions
│   │   └── cluster-registry.ts # Connection management
│   ├── transports/
│   │   ├── http.ts           # HTTP transport
│   │   └── stdio.ts          # Stdio transport
│   ├── middleware/
│   │   └── auth.ts           # API Key authentication
│   ├── tunnel/
│   │   ├── ssh-manager.ts    # SSH tunnel manager
│   │   └── connection-pool.ts # Connection pooling
│   └── utils/
│       ├── config.ts         # Configuration loader
│       └── logger.ts         # Logging
├── config/
│   ├── env/                  # Environment templates
│   │   ├── .env.development  # Claude Code (stdio)
│   │   ├── .env.ollama       # Air-gapped Ollama
│   │   └── .env.copilot      # Microsoft Copilot
│   ├── clusters.json         # Default cluster config
│   ├── clusters.example.json # Example multi-database config
│   ├── clusters.dev.json     # Development config
│   └── clusters.internal.json # Internal network config
├── docs/
│   ├── AIR_GAPPED_DEPLOYMENT.md  # Offline deployment guide
│   └── COPILOT_INTEGRATION.md    # Copilot Studio guide
├── scripts/
│   └── prepare-offline.sh    # Build offline package
├── dist/                     # Compiled JavaScript
├── .env                      # Environment variables (from template)
├── .mcp.json                 # MCP server configuration
├── docker-compose.yml        # Default Docker config
├── docker-compose.dev.yml    # Development stack
├── docker-compose.prod.yml   # Production stack
└── package.json

Security

Query Safety (Configurable)

The MCP server enforces read-only access by default with multiple layers of protection:

| Protection | Default | Description | |------------|---------|-------------| | Allowed Prefixes | SELECT, WITH, SHOW, DESCRIBE, EXPLAIN | Only these query types are allowed | | Blocked Keywords | INSERT, UPDATE, DELETE, DROP, ... | These keywords are blocked anywhere in query | | Multiple Statements | Blocked | Queries with ; are rejected | | SQL Comments | Blocked | -- and /* */ comments are rejected | | Row Limit | 10,000 max | Prevents large result sets |

Security Configuration

All security settings are configurable in config/clusters.json:

{
  "clusters": { ... },
  "defaultCluster": "main",
  "security": {
    "allowedQueryPrefixes": ["select", "with", "show", "describe", "desc", "explain"],
    "blockedKeywords": ["insert", "update", "delete", "drop", "truncate", "alter", "create", "grant", "revoke"],
    "enableKeywordBlocking": true,
    "allowedDatabases": [],
    "allowedTables": [],
    "maxRowLimit": 10000,
    "defaultRowLimit": 100,
    "blockMultipleStatements": true,
    "blockComments": true
  }
}

Security Options Reference

| Option | Type | Default | Description | |--------|------|---------|-------------| | allowedQueryPrefixes | string[] | ["select", "with", ...] | Query must start with one of these | | blockedKeywords | string[] | ["insert", "update", ...] | Keywords blocked anywhere in query | | enableKeywordBlocking | boolean | true | Enable/disable keyword blocking | | allowedDatabases | string[] | [] (all) | Whitelist of allowed databases | | allowedTables | string[] | [] (all) | Whitelist of allowed tables | | maxRowLimit | number | 10000 | Maximum rows returned | | defaultRowLimit | number | 100 | Default LIMIT if not specified | | blockMultipleStatements | boolean | true | Block queries with semicolons | | blockComments | boolean | true | Block SQL comments |

Example: Restrict to Specific Tables

{
  "security": {
    "allowedDatabases": ["analytics", "reporting"],
    "allowedTables": ["analytics.events", "analytics.users", "reporting.summary"]
  }
}

Example: Minimal Security (Not Recommended)

{
  "security": {
    "enableKeywordBlocking": false,
    "blockMultipleStatements": false,
    "blockComments": false
  }
}

Credential Management

  • Environment variable substitution for sensitive data
  • SSH key or password authentication support
  • Separate credentials per cluster and database type

Network Security

  • SSH tunneling for all remote connections
  • Local port forwarding for isolation
  • Support for bastion/jump hosts

Defense in Depth

For maximum security, combine MCP security settings with:

  1. Database readonly user - Create users with read-only permissions
  2. Network isolation - Use SSH tunneling through bastion hosts
  3. Database permissions - Grant SELECT only on specific tables

Development

Scripts

# Development mode with hot reload
npm run dev

# Build for production
npm run build

# Run production build
npm start

# Lint code
npm run lint

# Clean build directory
npm run clean

Adding New Tools

  1. Create a new file in src/tools/
  2. Define the tool definition and input schema
  3. Implement the handler function
  4. Register the tool in src/server.ts

Adding New Database Support

  1. Create a new adapter in src/databases/
  2. Implement the DatabaseClient interface
  3. Register the adapter in database-factory.ts
  4. Update the configuration schema in cluster-config.ts

Rebuilding After Configuration Changes

When you update clusters.json, .env, or any source code, you need to rebuild the project.

Local Development (Claude Code / stdio)

# 1. Rebuild the project
npm run build

# 2. Restart Claude Code to reconnect MCP
#    In Claude Code, use: /mcp or restart the application

Quick rebuild command:

npm run clean && npm run build

Docker Deployment

# 1. Rebuild Docker image (required after source code changes)
docker-compose build

# 2. Restart container (picks up volume-mounted config changes)
docker-compose down && docker-compose up -d

# 3. Verify the container is running
docker-compose logs -f database-mcp

When to rebuild Docker image vs restart container:

| Change Type | Action Required | |-------------|-----------------| | clusters.json changes | Restart container only (docker-compose restart) | | .env changes | Restart container only | | Source code (src/) changes | Rebuild image (docker-compose build) | | package.json changes | Rebuild image | | Dockerfile changes | Rebuild image |

Full rebuild command:

docker-compose down && docker-compose build --no-cache && docker-compose up -d

Verify Connection After Rebuild

For Claude Code (stdio):

# Check MCP status in Claude Code
/mcp

For Docker (HTTP):

# Health check
curl http://localhost:3100/health

# Test query
curl -X POST http://localhost:3100/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"query","arguments":{"sql":"SELECT 1"}}}'

Troubleshooting

SSH Connection Failed

Error: SSH connection error

Solutions:

  • Verify bastion host is reachable: ssh user@bastion-host
  • Check SSH credentials (password or key path)
  • Ensure port 22 is open in firewall
  • Verify SSH user has required permissions

Database Connection Failed

Error: Query error: Connection refused

Solutions:

  • Verify database port is accessible from bastion (or directly)
  • Check database user credentials
  • Ensure database exists
  • Verify database service is running

MCP Server Not Responding

Error: MCP server not found

Solutions:

  • Ensure project is built: npm run build
  • Verify path in Claude settings points to dist/index.js
  • Check if cwd is set correctly in settings
  • Restart Claude Code after config changes

Debug Mode

Enable debug logging:

LOG_LEVEL=debug

Check logs for detailed error information.

Environment Variables Reference

| Variable | Description | Default | |----------|-------------|---------| | MCP_TRANSPORT | Transport type (stdio/http) | stdio | | HTTP_PORT | HTTP server port | 3100 | | HTTP_HOST | HTTP server host | 0.0.0.0 | | LOG_LEVEL | Logging level | info | | CLUSTERS_CONFIG_PATH | Path to clusters config | ./config/clusters.json | | API_KEY | API key for authentication (empty = disabled) | | | `API_KEY_HEADER` | HTTP header name for API key | `X-API-Key` | | `CH_PASSWORD` | ClickHouse password (referenced in clusters.json) | | | PG_PASSWORD | PostgreSQL password (referenced in clusters.json) | | | `MYSQL_PASSWORD` | MySQL password (referenced in clusters.json) | |

Tip: Use environment templates from config/env/ for quick scenario switching.

License

MIT License - see LICENSE file for details.

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Run tests and linting
  5. Submit a pull request

Support

For issues and feature requests, please open an issue on GitHub.