From 8694f3fcf67ac66b633602633a4c8ee21b0588b8 Mon Sep 17 00:00:00 2001 From: Jasim Muhammed Date: Sun, 27 Apr 2025 22:27:47 +0400 Subject: [PATCH 1/3] adding docker build --- Dockerfile | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 3 +- server.py | 5 ++++ 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 Dockerfile create mode 100644 server.py diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..b1d5a88 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,77 @@ +# Build stage +FROM debian:bookworm-slim AS builder + +# Install build dependencies +RUN apt-get update && apt-get install -y \ + wget \ + gcc \ + g++ \ + make \ + zlib1g-dev \ + libffi-dev \ + libssl-dev \ + libbz2-dev \ + liblzma-dev \ + clang \ + && rm -rf /var/lib/apt/lists/* + +# Download and install Python from source +ENV PYTHON_VERSION=3.13.2 +RUN wget https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tgz \ + && tar -xf Python-${PYTHON_VERSION}.tgz \ + && cd Python-${PYTHON_VERSION} \ + && ./configure --enable-optimizations \ + && make -j$(nproc) \ + && make install \ + && cd .. \ + && rm -rf Python-${PYTHON_VERSION} \ + && rm Python-${PYTHON_VERSION}.tgz + +# Create and activate virtual environment +RUN python3 -m venv /opt/venv +ENV PATH="/opt/venv/bin:$PATH" + +# Install UV package manager +RUN pip install uv + +# Set working directory +WORKDIR /app + +# Copy application files +COPY . . + +# Install dependencies +RUN uv sync + +# Runtime stage +FROM debian:bookworm-slim + +# Install runtime dependencies +RUN apt-get update && apt-get install -y \ + zlib1g \ + libffi8 \ + libssl3 \ + libbz2-1.0 \ + liblzma5 \ + && rm -rf /var/lib/apt/lists/* + +# Install Python from builder +COPY --from=builder /usr/local/bin /usr/local/bin +COPY --from=builder /usr/local/lib /usr/local/lib +COPY --from=builder /usr/local/include /usr/local/include + +# Copy virtual environment from builder +COPY --from=builder /opt/venv /opt/venv +ENV PATH="/opt/venv/bin:$PATH" + +# Set working directory +WORKDIR /app + +# Copy application files +COPY . . + +# Expose port +EXPOSE 18123 + +# Run the application +CMD ["python3", "server.py"] \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 40155f8..03d9381 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "mcp-clickhouse" -version = "0.1.7" +version = "0.1.8" description = "An MCP server for ClickHouse." readme = "README.md" license = "Apache-2.0" @@ -10,6 +10,7 @@ dependencies = [ "mcp[cli]>=1.3.0", "python-dotenv>=1.0.1", "uvicorn>=0.34.0", + "uvicorn[standard]>=0.34.0", "clickhouse-connect>=0.8.16", "pip-system-certs>=4.0", ] diff --git a/server.py b/server.py new file mode 100644 index 0000000..cbe0e8c --- /dev/null +++ b/server.py @@ -0,0 +1,5 @@ +import uvicorn +from mcp_clickhouse.mcp_server import app + +if __name__ == "__main__": + uvicorn.run(app, host="0.0.0.0", port=8080) \ No newline at end of file From 1abc991bb33bad3765d7bae60f6be708d2625edb Mon Sep 17 00:00:00 2001 From: Jasim Muhammed Date: Sun, 27 Apr 2025 22:30:48 +0400 Subject: [PATCH 2/3] fix: port --- server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.py b/server.py index cbe0e8c..9cbddaf 100644 --- a/server.py +++ b/server.py @@ -2,4 +2,4 @@ from mcp_clickhouse.mcp_server import app if __name__ == "__main__": - uvicorn.run(app, host="0.0.0.0", port=8080) \ No newline at end of file + uvicorn.run(app, host="0.0.0.0", port=18213) \ No newline at end of file From 71893e130aef96a34d401b0d07bdbc7500e9f1d0 Mon Sep 17 00:00:00 2001 From: Jasim Muhammed Date: Thu, 1 May 2025 17:11:01 +0400 Subject: [PATCH 3/3] Adjust MCP server to be run in SSE mode --- Dockerfile | 112 ++++++++++++++--------------------- README.md | 44 ++++++++++++++ docker-compose.yml | 18 ++++++ mcp_clickhouse/main.py | 7 +++ mcp_clickhouse/mcp_server.py | 3 +- pyproject.toml | 2 +- 6 files changed, 116 insertions(+), 70 deletions(-) create mode 100644 docker-compose.yml diff --git a/Dockerfile b/Dockerfile index b1d5a88..6e097e8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,77 +1,53 @@ -# Build stage -FROM debian:bookworm-slim AS builder +# Use a Python image with uv pre-installed +FROM ghcr.io/astral-sh/uv:python3.13-bookworm-slim AS uv -# Install build dependencies -RUN apt-get update && apt-get install -y \ - wget \ - gcc \ - g++ \ - make \ - zlib1g-dev \ - libffi-dev \ - libssl-dev \ - libbz2-dev \ - liblzma-dev \ - clang \ - && rm -rf /var/lib/apt/lists/* - -# Download and install Python from source -ENV PYTHON_VERSION=3.13.2 -RUN wget https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tgz \ - && tar -xf Python-${PYTHON_VERSION}.tgz \ - && cd Python-${PYTHON_VERSION} \ - && ./configure --enable-optimizations \ - && make -j$(nproc) \ - && make install \ - && cd .. \ - && rm -rf Python-${PYTHON_VERSION} \ - && rm Python-${PYTHON_VERSION}.tgz - -# Create and activate virtual environment -RUN python3 -m venv /opt/venv -ENV PATH="/opt/venv/bin:$PATH" - -# Install UV package manager -RUN pip install uv - -# Set working directory +# Install the project into `/app` WORKDIR /app -# Copy application files -COPY . . +# Enable bytecode compilation +ENV UV_COMPILE_BYTECODE=1 -# Install dependencies -RUN uv sync +# Copy from the cache instead of linking since it's a mounted volume +ENV UV_LINK_MODE=copy -# Runtime stage -FROM debian:bookworm-slim +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential liblz4-dev libzstd-dev +# Install the project's dependencies using the lockfile and settings +RUN --mount=type=cache,target=/root/.cache/uv \ + --mount=type=bind,source=uv.lock,target=uv.lock \ + --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ + uv sync --frozen --no-install-project --no-dev --no-editable -# Install runtime dependencies -RUN apt-get update && apt-get install -y \ - zlib1g \ - libffi8 \ - libssl3 \ - libbz2-1.0 \ - liblzma5 \ - && rm -rf /var/lib/apt/lists/* +# Then, add the rest of the project source code and install it +# Installing separately from its dependencies allows optimal layer caching +ADD . /app +RUN --mount=type=cache,target=/root/.cache/uv \ + uv sync --frozen --no-dev --no-editable -# Install Python from builder -COPY --from=builder /usr/local/bin /usr/local/bin -COPY --from=builder /usr/local/lib /usr/local/lib -COPY --from=builder /usr/local/include /usr/local/include +FROM python:3.13-slim-bookworm -# Copy virtual environment from builder -COPY --from=builder /opt/venv /opt/venv -ENV PATH="/opt/venv/bin:$PATH" - -# Set working directory WORKDIR /app - -# Copy application files -COPY . . - -# Expose port -EXPOSE 18123 - -# Run the application -CMD ["python3", "server.py"] \ No newline at end of file +# Copy the virtual environment +COPY --from=uv --chown=app:app /app/.venv /app/.venv +# Copy the uv executable +COPY --from=uv /usr/local/bin/uv /usr/local/bin/ +# Copy the project files +COPY --from=uv /app /app + +# Place executables in the environment at the front of the path +ENV PATH="/app/.venv/bin:/usr/local/bin:$PATH" + +# Add ClickHouse environment variables +ENV CLICKHOUSE_HOST="" +ENV CLICKHOUSE_PORT="8443" +ENV CLICKHOUSE_USER="" +ENV CLICKHOUSE_PASSWORD="" +ENV CLICKHOUSE_SECURE="true" +ENV CLICKHOUSE_VERIFY="true" +ENV CLICKHOUSE_CONNECT_TIMEOUT="30" +ENV CLICKHOUSE_SEND_RECEIVE_TIMEOUT="300" +ENV CLICKHOUSE_DATABASE="" + +EXPOSE 28123 +# when running the container, add --db-path and a bind mount to the host's db file +ENTRYPOINT ["uv", "run", "mcp-clickhouse-sse", "--directory", "/app"] \ No newline at end of file diff --git a/README.md b/README.md index bbe4ba3..f1e00cf 100644 --- a/README.md +++ b/README.md @@ -210,6 +210,50 @@ You can set these variables in your environment, in a `.env` file, or in the Cla } ``` +## Running with Docker Compose + +You can run the MCP server using Docker Compose with SSE mode. Create a `docker-compose.yml` file with the following configuration: + +```yaml +version: '3.8' +services: + mcp-clickhouse: + build: . + environment: + - CLICKHOUSE_HOST=your-clickhouse-host + - CLICKHOUSE_PORT=8443 + - CLICKHOUSE_USER=your-user + - CLICKHOUSE_PASSWORD=your-password + - CLICKHOUSE_DATABASE=your-database + - CLICKHOUSE_SECURE=true + - CLICKHOUSE_VERIFY=true + ports: + - "28123:28123" +``` + +Then run: +```bash +docker compose up -d +``` + +### MCP Configuration for Docker + +When running the MCP server in Docker in SSE, you'll need to configure your MCP client to use `host.docker.internal` when running on the same machine. Update your MCP configuration to use the following URI: + +```json +{ + "mcpServers": { + "mcp-clickhouse": { + "type": "sse", + "uri": "http://host.docker.internal:28123/sse", + "timeout": 60000 + } + } +} +``` + +If you're using Claude Desktop, add this configuration to your `claude_desktop_config.json` file along with the existing configuration. + ### Running tests ```bash diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..671a907 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,18 @@ +version: '3.8' + +services: + mcp-clickhouse: + build: + context: . + dockerfile: Dockerfile + ports: + - "28123:28123" + environment: + - CLICKHOUSE_HOST=sql-clickhouse.clickhouse.com + - CLICKHOUSE_PORT=8443 + - CLICKHOUSE_USER=demo + - CLICKHOUSE_PASSWORD= + - CLICKHOUSE_SECURE=true + - CLICKHOUSE_VERIFY=true + - CLICKHOUSE_CONNECT_TIMEOUT=30 + - CLICKHOUSE_SEND_RECEIVE_TIMEOUT=30 diff --git a/mcp_clickhouse/main.py b/mcp_clickhouse/main.py index 3653ca1..d7712cd 100644 --- a/mcp_clickhouse/main.py +++ b/mcp_clickhouse/main.py @@ -1,3 +1,6 @@ +import asyncio +import argparse + from .mcp_server import mcp @@ -5,5 +8,9 @@ def main(): mcp.run() +def main_sse(): + mcp.run(transport="sse") + + if __name__ == "__main__": main() diff --git a/mcp_clickhouse/mcp_server.py b/mcp_clickhouse/mcp_server.py index 244a6cd..2c6d4bd 100644 --- a/mcp_clickhouse/mcp_server.py +++ b/mcp_clickhouse/mcp_server.py @@ -46,6 +46,7 @@ class Table: MCP_SERVER_NAME = "mcp-clickhouse" +SERVER_PORT = 28123 # Configure logging logging.basicConfig( @@ -66,7 +67,7 @@ class Table: "pip-system-certs", ] -mcp = FastMCP(MCP_SERVER_NAME, dependencies=deps) +mcp = FastMCP(MCP_SERVER_NAME, dependencies=deps, port=SERVER_PORT) def result_to_table(query_columns, result) -> List[Table]: diff --git a/pyproject.toml b/pyproject.toml index 03d9381..4eeb7af 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,7 +17,7 @@ dependencies = [ [project.scripts] mcp-clickhouse = "mcp_clickhouse.main:main" - +mcp-clickhouse-sse = "mcp_clickhouse.main:main_sse" [project.urls] Home = "https://github.com/ClickHouse/mcp-clickhouse"