diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..f3e0b46 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,18 @@ +.git +.github +.ruff_cache +.env +*.egg-info +__pycache__ +tests +docs +*.md +!README.md +LICENSE +SECURITY.md +CODE_OF_CONDUCT.md +CONTRIBUTING.md +docker-compose.yml +policy +.pre-commit-config.yaml +.gitignore diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index acaebb9..e3698f1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -92,6 +92,7 @@ jobs: images: ghcr.io/jrhuerta/secure-sql-mcp tags: | type=raw,value=${{ github.event.release.tag_name }} + type=raw,value=latest - name: Build and push image uses: docker/build-push-action@v7 diff --git a/AGENTS.md b/AGENTS.md index a8d6882..e8da51f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -73,8 +73,9 @@ Run core security suites: ## Container Publishing - GitHub Actions publishes container images to `ghcr.io/jrhuerta/secure-sql-mcp`. -- Images are published only when a GitHub Release is published (e.g. tag `v0.1.0`). -- Use the release tag to pull: `docker pull ghcr.io/jrhuerta/secure-sql-mcp:v0.1.0`. +- Images are published only when a GitHub Release is published. +- Each release pushes both the version tag (e.g. `v0.1.0`) and `latest`. +- Use `docker pull ghcr.io/jrhuerta/secure-sql-mcp:latest` or a specific version tag. ## Policy File Contract diff --git a/Dockerfile b/Dockerfile index 7c9f341..8f0ed61 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,17 +1,22 @@ -FROM python:3.12-slim +FROM python:3.12-slim AS builder -ENV PYTHONDONTWRITEBYTECODE=1 \ - PYTHONUNBUFFERED=1 +WORKDIR /build +COPY pyproject.toml README.md ./ +COPY src ./src -WORKDIR /app +RUN python -m venv /opt/venv \ + && /opt/venv/bin/pip install --no-cache-dir . \ + && find /opt/venv -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null; true -COPY pyproject.toml README.md /app/ -COPY src /app/src +FROM python:3.12-slim + +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 \ + PATH="/opt/venv/bin:$PATH" -RUN pip install --no-cache-dir . \ - && useradd -r -s /usr/sbin/nologin appuser \ - && chown -R appuser:appuser /app +COPY --from=builder /opt/venv /opt/venv +RUN useradd -r -s /usr/sbin/nologin appuser USER appuser ENTRYPOINT ["python", "-m", "secure_sql_mcp.server"] diff --git a/README.md b/README.md index 118d1d3..0a65b3f 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ To use this server with Cursor, Claude Desktop, or other MCP clients, add it to "--rm", "--env-file", "/path/to/your/secrets", "-v", "/path/to/your/policy:/run/policy:ro", - "ghcr.io/jrhuerta/secure-sql-mcp:v0.1.0" + "ghcr.io/jrhuerta/secure-sql-mcp:latest" ] } } @@ -31,7 +31,7 @@ To use this server with Cursor, Claude Desktop, or other MCP clients, add it to **Claude Desktop** (`claude_desktop_config.json`): same structure under `mcpServers`. -The `--env-file` should point to a file containing `DATABASE_URL` and `ALLOWED_POLICY_FILE=/run/policy/allowed_policy.txt` (see Environment Variables below). The volume mounts the policy directory read-only. Pull the image first: `docker pull ghcr.io/jrhuerta/secure-sql-mcp:v0.1.0` +The `--env-file` should point to a file containing `DATABASE_URL` and `ALLOWED_POLICY_FILE=/run/policy/allowed_policy.txt` (see Environment Variables below). The volume mounts the policy directory read-only. Pull the image first: `docker pull ghcr.io/jrhuerta/secure-sql-mcp:latest` ## Security Model @@ -164,10 +164,10 @@ docker run -i --rm \ ## Quick Start (GHCR Image) -Images are published when a GitHub Release is published (e.g. tag `v0.1.0`). Pull by release tag: +Images are published when a GitHub Release is created. Each release pushes both the version tag (e.g. `v0.1.0`) and `latest`: ```bash -docker pull ghcr.io/jrhuerta/secure-sql-mcp:v0.1.0 +docker pull ghcr.io/jrhuerta/secure-sql-mcp:latest ``` Run with env file and read-only mounted policy: @@ -176,7 +176,7 @@ Run with env file and read-only mounted policy: docker run -i --rm \ --env-file .env \ -v "$(pwd)/policy:/run/policy:ro" \ - ghcr.io/jrhuerta/secure-sql-mcp:v0.1.0 + ghcr.io/jrhuerta/secure-sql-mcp:latest ``` Or with Docker Compose (builds from local Dockerfile): @@ -280,8 +280,8 @@ After merging workflow/docs changes, verify: - linear history, no force-push, no deletion - CI workflow runs on PRs and on pushes to `main` - GHCR image publish succeeds when a GitHub Release is published -- GHCR pull works (use release tag, e.g. `v0.1.0`): - - `docker pull ghcr.io/jrhuerta/secure-sql-mcp:v0.1.0` +- GHCR pull works: + - `docker pull ghcr.io/jrhuerta/secure-sql-mcp:latest` - community docs are present: - `CONTRIBUTING.md` - `CODE_OF_CONDUCT.md` diff --git a/pyproject.toml b/pyproject.toml index cae0da8..466da88 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ license = "MIT" requires-python = ">=3.11" authors = [{ name = "jrhuerta" }] dependencies = [ - "mcp[cli]", + "mcp", "sqlglot", "sqlalchemy[asyncio]", "asyncpg", @@ -36,6 +36,7 @@ Issues = "https://github.com/jrhuerta/secure-sql-mcp/issues" [project.optional-dependencies] dev = [ + "mcp[cli]", "pre-commit", "pytest", "ruff",