Skip to content

Commit e98d836

Browse files
authored
Add Docker support and Anthropic MCP registry preparation (#13)
2 parents 6ae10d5 + 241ce5e commit e98d836

File tree

8 files changed

+275
-1
lines changed

8 files changed

+275
-1
lines changed

.dockerignore

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# VCS/IDE
2+
.git
3+
.gitignore
4+
.cursor
5+
.vscode
6+
.DS_Store
7+
8+
# CI and docs
9+
.github
10+
assets
11+
design
12+
13+
# Python artifacts
14+
__pycache__
15+
*.pyc
16+
*.pyo
17+
*.pyd
18+
*.log
19+
*.egg-info
20+
dist
21+
build
22+
.cache
23+
.venv
24+
venv
25+
env
26+
27+
# Desktop extension bundle
28+
*.dxt
29+
30+
# MCP registry metadata (if added later)
31+
.mcp
32+
33+
# Vendored libs (we install via pip inside the image)
34+
server/lib/
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
name: Docker Publish
2+
3+
on:
4+
push:
5+
branches: [main]
6+
workflow_dispatch:
7+
8+
env:
9+
IMAGE_NAME: docker.io/zenmldocker/mcp-zenml
10+
11+
permissions:
12+
contents: read
13+
14+
jobs:
15+
build-and-push:
16+
runs-on: ubuntu-latest
17+
concurrency:
18+
group: docker-publish-${{ github.ref }}
19+
cancel-in-progress: true
20+
steps:
21+
- name: Checkout
22+
uses: actions/checkout@v4
23+
24+
- name: Set up QEMU
25+
uses: docker/setup-qemu-action@v3
26+
27+
- name: Set up Docker Buildx
28+
uses: docker/setup-buildx-action@v3
29+
30+
- name: Log in to Docker Hub
31+
uses: docker/login-action@v3
32+
with:
33+
username: ${{ secrets.DOCKERHUB_USERNAME }}
34+
password: ${{ secrets.DOCKERHUB_TOKEN }}
35+
36+
- name: Extract metadata (tags, labels)
37+
id: meta
38+
uses: docker/metadata-action@v5
39+
with:
40+
images: ${{ env.IMAGE_NAME }}
41+
tags: |
42+
type=raw,value=latest,enable={{is_default_branch}}
43+
type=sha,format=short,prefix=sha-
44+
labels: |
45+
org.opencontainers.image.title=ZenML MCP Server
46+
org.opencontainers.image.description=Model Context Protocol server for ZenML
47+
org.opencontainers.image.source=https://github.com/${{ github.repository }}
48+
org.opencontainers.image.licenses=MIT
49+
50+
- name: Build and push
51+
uses: docker/build-push-action@v6
52+
with:
53+
context: .
54+
file: ./Dockerfile
55+
push: true
56+
platforms: linux/amd64,linux/arm64
57+
tags: ${{ steps.meta.outputs.tags }}
58+
labels: ${{ steps.meta.outputs.labels }}
59+
cache-from: type=gha
60+
cache-to: type=gha,mode=max
61+
62+
- name: Image digest
63+
run: echo "Digest -> ${{ steps.meta.outputs.digest }}"
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
name: Release Docker
2+
3+
on:
4+
push:
5+
tags: ["v*.*.*"]
6+
7+
env:
8+
IMAGE_NAME: docker.io/zenmldocker/mcp-zenml
9+
10+
permissions:
11+
contents: read
12+
13+
jobs:
14+
docker:
15+
runs-on: ubuntu-latest
16+
steps:
17+
- name: Checkout
18+
uses: actions/checkout@v4
19+
20+
- name: Set up QEMU
21+
uses: docker/setup-qemu-action@v3
22+
23+
- name: Set up Docker Buildx
24+
uses: docker/setup-buildx-action@v3
25+
26+
- name: Log in to Docker Hub
27+
uses: docker/login-action@v3
28+
with:
29+
username: ${{ secrets.DOCKERHUB_USERNAME }}
30+
password: ${{ secrets.DOCKERHUB_TOKEN }}
31+
32+
- name: Extract metadata (tags, labels)
33+
id: meta
34+
uses: docker/metadata-action@v5
35+
with:
36+
images: ${{ env.IMAGE_NAME }}
37+
tags: |
38+
type=semver,pattern={{version}}
39+
type=raw,value=latest
40+
labels: |
41+
org.opencontainers.image.title=ZenML MCP Server
42+
org.opencontainers.image.description=Model Context Protocol server for ZenML
43+
org.opencontainers.image.source=https://github.com/${{ github.repository }}
44+
org.opencontainers.image.licenses=MIT
45+
46+
- name: Build and push
47+
uses: docker/build-push-action@v6
48+
with:
49+
context: .
50+
file: ./Dockerfile
51+
push: true
52+
platforms: linux/amd64,linux/arm64
53+
tags: ${{ steps.meta.outputs.tags }}
54+
labels: ${{ steps.meta.outputs.labels }}
55+
cache-from: type=gha
56+
cache-to: type=gha,mode=max

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,3 +176,4 @@ cython_debug/
176176
# project-specific ignores
177177
design/
178178
mcp.json
179+
.claude/

AGENTS.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Repository Guidelines
2+
3+
## Project Structure & Module Organization
4+
- `server/` – MCP server implementation. Main entry: `server/zenml_server.py`; treat `server/lib/` as vendored support code (avoid edits unless necessary).
5+
- `scripts/` – Developer utilities: `format.sh` (ruff) and `test_mcp_server.py` (smoke test).
6+
- `assets/` – Images and static assets.
7+
- Root files – `README.md`, `manifest.json`, `mcp-zenml.dxt` (DXT package), CI in `.github/workflows/`.
8+
9+
## Build, Test, and Development Commands
10+
- Run server locally: `uv run server/zenml_server.py`
11+
- Smoke test (local): `uv run scripts/test_mcp_server.py server/zenml_server.py`
12+
- Format & lint: `bash scripts/format.sh` (ruff check + import sort + format)
13+
- CI mirrors the smoke test via GitHub Actions and requires Python 3.12.
14+
15+
## Coding Style & Naming Conventions
16+
- Language: Python 3.12+. Indentation: 4 spaces.
17+
- Use snake_case for functions/variables, PascalCase for classes, UPPER_SNAKE_CASE for constants.
18+
- Keep imports tidy; `scripts/format.sh` enforces ruff rules and import sorting.
19+
- Logging: prefer `logging` to stderr; avoid printing from MCP tool functions except returning strings/JSON. Keep logs minimal to avoid MCP JSON protocol interference.
20+
21+
## Testing Guidelines
22+
- Primary test: `scripts/test_mcp_server.py` exercises MCP connection, initialization, and basic tools.
23+
- Run locally with `uv run ...`; CI runs on PRs and a scheduled workflow.
24+
- If adding tests, follow descriptive names (e.g., `test_<area>_behavior.py`) and place alongside existing script tests under `scripts/` or add a `tests/` folder. Keep tests fast and network-light; mock ZenML calls when feasible.
25+
26+
## Commit & Pull Request Guidelines
27+
- Commits: concise, imperative subject (e.g., "Update README", "Add smoke test"), group related changes.
28+
- PRs: include a clear description, link related issues, and add logs/screenshots for failures or tool output when relevant. Ensure CI passes (smoke test and formatting).
29+
30+
## Security & Configuration Tips
31+
- Required env vars to run tools: `ZENML_STORE_URL`, `ZENML_STORE_API_KEY`.
32+
- Prefer `uv` for isolated runs. Do not log secrets; scrub values in examples and CI output.
33+
- Avoid modifying `server/lib/` unless you understand downstream effects.
34+

Dockerfile

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# syntax=docker/dockerfile:1
2+
FROM python:3.12-slim AS base
3+
4+
ENV PYTHONDONTWRITEBYTECODE=1 \
5+
PYTHONUNBUFFERED=1 \
6+
PYTHONIOENCODING=UTF-8 \
7+
PIP_NO_CACHE_DIR=1 \
8+
LOGLEVEL=WARNING \
9+
NO_COLOR=1 \
10+
ZENML_LOGGING_COLORS_DISABLED=true \
11+
ZENML_ENABLE_RICH_TRACEBACK=false
12+
13+
# Optional but helpful: fresh CA certs for TLS reliability
14+
RUN apt-get update \
15+
&& apt-get install -y --no-install-recommends ca-certificates \
16+
&& rm -rf /var/lib/apt/lists/*
17+
18+
WORKDIR /app
19+
20+
# Install Python dependencies
21+
COPY requirements.txt /app/
22+
RUN pip install --upgrade pip setuptools wheel \
23+
&& pip install -r requirements.txt
24+
25+
# Security: non-root user
26+
RUN useradd -m -u 10001 appuser
27+
USER appuser
28+
29+
# Copy only what we need to run the server in stdio mode
30+
COPY --chown=appuser:appuser server/zenml_server.py /app/server/zenml_server.py
31+
32+
# OCI labels (will be enriched/overridden by CI metadata)
33+
LABEL org.opencontainers.image.title="ZenML MCP Server" \
34+
org.opencontainers.image.description="Model Context Protocol server for ZenML" \
35+
org.opencontainers.image.source="https://github.com/zenml-io/mcp-zenml" \
36+
org.opencontainers.image.licenses="MIT"
37+
38+
ENTRYPOINT ["python", "-u", "server/zenml_server.py"]

README.md

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,11 @@ You will need to specify your ZenML MCP server in the following format:
104104
"command": "/usr/local/bin/uv",
105105
"args": ["run", "path/to/server/zenml_server.py"],
106106
"env": {
107-
"LOGLEVEL": "INFO",
107+
"LOGLEVEL": "WARNING",
108108
"NO_COLOR": "1",
109+
"ZENML_LOGGING_COLORS_DISABLED": "true",
110+
"ZENML_LOGGING_VERBOSITY": "WARN",
111+
"ZENML_ENABLE_RICH_TRACEBACK": "false",
109112
"PYTHONUNBUFFERED": "1",
110113
"PYTHONIOENCODING": "UTF-8",
111114
"ZENML_STORE_URL": "https://your-zenml-server-goes-here.com",
@@ -178,6 +181,50 @@ In our experience, sometimes it shows a red error indicator even though it is
178181
working. You can try it out by chatting in the Cursor chat window. It will let
179182
you know if is able to access the ZenML tools or not.
180183

184+
## Docker Image
185+
186+
You can run the server as a Docker container. The process communicates over stdio, so it will wait for an MCP client connection. Pass your ZenML credentials via environment variables.
187+
188+
### Prebuilt Images (Docker Hub)
189+
190+
Pull the latest multi-arch image:
191+
192+
```bash
193+
docker pull zenmldocker/mcp-zenml:latest
194+
```
195+
196+
Versioned releases are tagged as `vX.Y.Z`:
197+
198+
```bash
199+
docker pull zenmldocker/mcp-zenml:v1.0.0
200+
```
201+
202+
Run with your ZenML credentials (stdio mode):
203+
204+
```bash
205+
docker run -i --rm \
206+
-e ZENML_STORE_URL="https://your-zenml-server.example.com" \
207+
-e ZENML_STORE_API_KEY="your-api-key" \
208+
zenmldocker/mcp-zenml:latest
209+
```
210+
211+
### Build Locally
212+
213+
From the repository root:
214+
215+
```bash
216+
docker build -t zenmldocker/mcp-zenml:local .
217+
```
218+
219+
Run the locally built image:
220+
221+
```bash
222+
docker run -i --rm \
223+
-e ZENML_STORE_URL="https://your-zenml-server.example.com" \
224+
-e ZENML_STORE_API_KEY="your-api-key" \
225+
zenmldocker/mcp-zenml:local
226+
```
227+
181228
## Desktop Extensions (DXT) Support
182229

183230
This project supports [Anthropic's Desktop Extensions (DXT) standard](https://www.anthropic.com/engineering/desktop-extensions), which makes installing MCP servers as simple as clicking a button. DXT is a new packaging format that bundles entire MCP servers into a single `.dxt` file, including all dependencies and providing user-friendly configuration.

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ httpx
22
mcp[cli]
33
zenml
44
setuptools
5+
requests>=2.32.0

0 commit comments

Comments
 (0)