feat: add Docker support for HTTP-based MCP server#136
Conversation
Add multi-stage Dockerfile optimized for production: - Uses uv for fast dependency installation - Runs as non-root user for security - Includes health check for container orchestration - Exposes port 8000 for HTTP transport Add GitHub workflow for automated image builds: - Triggers on release, main branch push, and manual dispatch - Builds multi-platform images (amd64, arm64) - Pushes to GitHub Container Registry (ghcr.io) - Uses build cache for faster CI Update .dockerignore to exclude tests, examples, and docs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #136 +/- ##
==========================================
- Coverage 84.74% 84.74% -0.01%
==========================================
Files 27 27
Lines 3075 3080 +5
Branches 458 458
==========================================
+ Hits 2606 2610 +4
- Misses 355 356 +1
Partials 114 114
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report in Codecov by Sentry.
🚀 New features to boost your workflow:
|
Code Review: Docker Support for HTTP-based MCP ServerOverviewThis PR adds production-ready Docker support for deploying DataBeak as an HTTP-based MCP server. The implementation follows modern container best practices with multi-stage builds, security hardening, and automated CI/CD. Critical Issues🔴 Health Check Endpoint MismatchPriority: High - Blocks deployment The Dockerfile HEALTHCHECK references http://localhost:8000/health, but FastMCP servers don't automatically expose this endpoint. Problem:
Impact: Health checks will fail, causing container orchestration platforms to continuously restart the container. Recommendations:
🟡 GitHub Workflow: Attestation Step IssuePriority: Medium The attestation step references steps.build-push.outputs.digest but the build step has no explicit ID. Fix: Add "id: build-push" to line 68 (Build and push Docker image step) Security Review ✅Strong Points:
Minor Recommendations:
Performance ✅Excellent Optimizations:
Size Estimate: ~400-500 MB (reasonable for data processing server) Code Quality ✅Alignment with CLAUDE.md:
Test Coverage
|
There was a problem hiding this comment.
Pull request overview
This PR reintroduces Docker support for the DataBeak MCP server, specifically optimized for HTTP-based deployment. Previously, Docker support was removed (per CHANGELOG.md), but this PR adds it back with a focus on production HTTP server deployments to container registries.
Key changes:
- Multi-stage Dockerfile with security hardening (non-root user, minimal runtime image)
- Automated GitHub Actions workflow for building and publishing multi-platform images to GHCR
- Updated .dockerignore to exclude development artifacts while preserving necessary runtime files
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| Dockerfile | Multi-stage build configuration with uv package manager, non-root user security, and HTTP server defaults |
| .github/workflows/docker-publish.yml | Automated CI/CD pipeline for building and publishing container images on releases, main branch pushes, and manual triggers |
| .dockerignore | Excludes tests, examples, and documentation from container images to reduce image size |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| packages: write |
There was a problem hiding this comment.
The actions/attest-build-provenance@v2 action requires additional permissions that are not currently granted. The attestation step will fail without id-token: write and attestations: write permissions.
Update the permissions section:
permissions:
contents: read
packages: write
id-token: write
attestations: write| packages: write | |
| packages: write | |
| id-token: write | |
| attestations: write |
Dockerfile
Outdated
| FROM python:3.12-slim AS builder | ||
|
|
||
| # Install uv for fast dependency management | ||
| COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ |
There was a problem hiding this comment.
Using the latest tag for the uv image can lead to non-reproducible builds and potential breakages if the uv image is updated with breaking changes. This violates container best practices for reproducibility.
Pin to a specific version or SHA:
COPY --from=ghcr.io/astral-sh/uv:0.5.18 /uv /uvx /bin/Or use a specific version tag to ensure reproducible builds across time.
| COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ | |
| COPY --from=ghcr.io/astral-sh/uv:0.5.18 /uv /uvx /bin/ |
| # Manual tag override | ||
| type=raw,value=${{ github.event.inputs.tag }},enable=${{ github.event.inputs.tag != '' }} | ||
|
|
||
| - name: Build and push Docker image |
There was a problem hiding this comment.
The step "Build and push Docker image" is missing an id attribute. Line 83 references steps.build-push.outputs.digest, but this step doesn't have id: build-push, causing the attestation step to fail.
Add id: build-push to this step:
- name: Build and push Docker image
id: build-push
uses: docker/build-push-action@v6| - name: Build and push Docker image | |
| - name: Build and push Docker image | |
| id: build-push |
|
|
||
| # Documentation files | ||
| *.md | ||
| !README.md |
There was a problem hiding this comment.
The *.md exclusion pattern on line 68 will exclude src/databeak/instructions.md, which is required by the server at runtime (loaded in server.py:41-46). While the code has fallback handling, this will cause a warning log and the server will run with "Instructions file not available" instead of the proper instructions.
Add an exception for the instructions file:
# Documentation files
*.md
!README.md
!src/databeak/instructions.md
| !README.md | |
| !README.md | |
| !src/databeak/instructions.md |
Dockerfile
Outdated
|
|
||
| # Health check for container orchestration | ||
| HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ | ||
| CMD python -c "import httpx; httpx.get('http://localhost:8000/health', timeout=5)" || exit 1 |
There was a problem hiding this comment.
The HEALTHCHECK command will succeed even if the /health endpoint returns an error status (4xx/5xx). In httpx 0.27.0+, httpx.get() doesn't raise exceptions for non-2xx responses by default - it only raises on network errors.
Fix by explicitly checking the status code:
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD python -c "import sys, httpx; sys.exit(0 if httpx.get('http://localhost:8000/health', timeout=5).status_code == 200 else 1)"Alternatively, use raise_for_status():
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD python -c "import httpx; httpx.get('http://localhost:8000/health', timeout=5).raise_for_status()"| CMD python -c "import httpx; httpx.get('http://localhost:8000/health', timeout=5)" || exit 1 | |
| CMD python -c "import httpx; httpx.get('http://localhost:8000/health', timeout=5).raise_for_status()" |
- Add /health endpoint to FastMCP server for container orchestration - Pin uv version to 0.5.18 for reproducible builds - Fix health check to use raise_for_status() for proper error detection - Add id: build-push to workflow for attestation step - Add id-token and attestations permissions for provenance - Preserve src/databeak/instructions.md in Docker builds 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
PR Review: Docker Support for HTTP-based MCP ServerSummaryThis PR adds Docker support for running DataBeak as an HTTP-based MCP server with multi-architecture container builds. The implementation is generally solid with good security practices, though there are several issues that need addressing. Critical Issues1. Health Check Dependency MissingLocation: The health check uses HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD python -c "import httpx; httpx.get('http://localhost:8000/health', timeout=5).raise_for_status()"Issue: While Recommendation: Use a simpler health check that doesn't require external dependencies: HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health', timeout=5).read()"Or alternatively, use RUN apt-get update && apt-get install -y --no-install-recommends curl && rm -rf /var/lib/apt/lists/*
HEALTHCHECK CMD curl -f http://localhost:8000/health || exit 12. Health Check Endpoint Not TestedLocation: The new Recommendation: Add integration tests: # In tests/integration/test_server_integration.py or new file
async def test_health_endpoint():
"""Test health check endpoint responds correctly."""
# Test that /health returns 200 OK with "OK" body
pass3. Workflow May Trigger on Draft PRsLocation: The workflow triggers on pushes to Recommendation: Consider adding a condition to only run on successful PR merges or releases: on:
pull_request:
types: [closed]
branches: [main]
# ... rest of triggersCode Quality Issues4. Inconsistent ARG001 HandlingLocation: async def health_check(request: Request) -> PlainTextResponse: # noqa: ARG001Issue: The Recommendation: Use underscore prefix per Python convention: async def health_check(_request: Request) -> PlainTextResponse:
"""Health check endpoint for container orchestration."""
return PlainTextResponse("OK")This eliminates the need for 5. Missing Type Annotations in WorkflowNot applicable (YAML file), but documentation could be improved. Security Considerations6. Non-Root User Implementation ✅Location: The use of a dedicated non-root user ( 7. Binding to 0.0.0.0
|
- Switch health check from httpx to urllib.request (stdlib) to avoid dependency coupling in container health checks - Use underscore prefix (_request) instead of noqa: ARG001 for unused parameter per Python conventions - Add unit tests for health check endpoint registration and response 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Code Review: Docker Support for HTTP-based MCP ServerThis PR adds Docker support with multi-stage builds and GitHub Actions workflow. The implementation is solid but has critical test coverage issues. Critical Issues1. Weak Test Coverage (tests/unit/test_server.py:485-509) Recommendation: Use httpx AsyncClient to test actual HTTP responses: async def test_health_check_endpoint(self) -> None:
from httpx import AsyncClient
mcp = create_server()
async with AsyncClient(app=mcp._app, base_url="http://test") as client:
response = await client.get("/health")
assert response.status_code == 200
assert response.text == "OK"2. Dockerfile Missing Metadata Files (Dockerfile:14) 3. Overly Broad Workflow Triggers (.github/workflows/docker-publish.yml:8-12) Security Considerations4. Hardcoded UID/GID (Dockerfile:30-31) 5. Silent Health Check Failures (Dockerfile:54) Best Practices✅ Good:
Required Before Merge
VerdictReview based on CLAUDE.md: Zero tolerance for quality issues |
Summary
Details
Dockerfile features:
uvfor fast dependency installationdatabeakuser for security--transport http --host 0.0.0.0 --port 8000GitHub workflow triggers:
latestmain(relevant files) →maintagPlatforms: linux/amd64, linux/arm64
Test plan
docker build -t databeak .docker run -p 8000:8000 databeak🤖 Generated with Claude Code