Add Dockerfile and build system for Jupyter image

Created docker/ directory with:
- Dockerfile.jupyter: Extends mcp-forge/python:3.12 with ipykernel 6.29.0
- build-images.sh: Build script with testing
- README.md: Complete documentation and usage instructions

Image Details:
- Base: mcp-forge/python:3.12 (python:3.12-slim)
- Size: 196 MB (~73 MB added for ipykernel + dependencies)
- Includes: ipykernel, jupyter-client, pyzmq, ipython, debugpy, etc.
- Version: 6.29.0 (matches host jupyter-client compatibility)

Build tested and verified:
✓ ipykernel imports correctly (6.29.0)
✓ kernel launcher works
✓ Image ready for use

Next: Update config and run integration tests
This commit is contained in:
Hans Aschauer 2026-02-07 08:32:06 +01:00
parent 8e49c23d36
commit 35e2c3e983
3 changed files with 223 additions and 0 deletions

30
docker/Dockerfile.jupyter Normal file
View file

@ -0,0 +1,30 @@
# Jupyter Kernel Image for MCP-Forge
#
# This image provides a Python environment with ipykernel for stateful
# execution via Jupyter protocol. It runs inside containers managed by
# MCP-Forge and communicates with the host via ZMQ.
#
# Base: mcp-forge/python:3.12 (which is python:3.12-slim)
# Adds: ipykernel for Jupyter kernel functionality
FROM mcp-forge/python:3.12
LABEL maintainer="MCP-Forge"
LABEL description="Python 3.12 with ipykernel for stateful execution"
LABEL version="1.0"
# Install ipykernel
# Using --no-cache-dir to keep image size small
RUN pip install --no-cache-dir \
ipykernel==6.29.0 \
&& python -m ipykernel install --user
# Set working directory
WORKDIR /workspace
# The container will be run with:
# - Host networking (for ZMQ communication)
# - Mounted connection file
# - Command: python -m ipykernel_launcher -f /tmp/kernel-{id}.json
#
# No ENTRYPOINT or CMD needed - command specified at runtime

107
docker/README.md Normal file
View file

@ -0,0 +1,107 @@
# Docker Images for MCP-Forge
This directory contains Dockerfiles for building MCP-Forge container images.
## Images
### Jupyter Image (`mcp-forge/jupyter:latest`)
**Purpose**: Provides Python environment with ipykernel for stateful Jupyter-based execution.
**Base**: `mcp-forge/python:3.12` (which is `python:3.12-slim`)
**Added Components**:
- `ipykernel` 6.29.0 - Jupyter kernel for Python
**Build Command**:
```bash
# From the mcp-forge project root:
podman build -f docker/Dockerfile.jupyter -t mcp-forge/jupyter:latest .
```
**Usage**: This image is used automatically by the Jupyter backend when creating stateful sessions. The kernel communicates with the host via ZMQ over host networking.
**Size**: ~130-140 MB (adds ~10-15 MB to base Python image)
## Base Python Images
The base Python images should already exist:
```bash
podman tag python:3.11-slim mcp-forge/python:3.11
podman tag python:3.12-slim mcp-forge/python:3.12
```
If not, pull and tag them:
```bash
podman pull python:3.11-slim
podman tag python:3.11-slim mcp-forge/python:3.11
podman pull python:3.12-slim
podman tag python:3.12-slim mcp-forge/python:3.12
```
## Building All Images
To build/verify all images at once:
```bash
#!/bin/bash
# From project root
# Ensure base images exist
podman pull python:3.11-slim
podman pull python:3.12-slim
podman tag python:3.11-slim mcp-forge/python:3.11
podman tag python:3.12-slim mcp-forge/python:3.12
# Build Jupyter image
podman build -f docker/Dockerfile.jupyter -t mcp-forge/jupyter:latest .
# Verify all images
podman images | grep mcp-forge
```
## Testing the Jupyter Image
Quick test to verify ipykernel is working:
```bash
# Test that ipykernel is installed
podman run --rm mcp-forge/jupyter:latest python -c "import ipykernel; print(ipykernel.__version__)"
# Test kernel launcher exists
podman run --rm mcp-forge/jupyter:latest python -m ipykernel_launcher --help
```
Expected output:
```
6.29.0
usage: ipykernel_launcher [-h] [-f FILE] ...
```
## Image Sizes
Expected sizes:
- `mcp-forge/python:3.11` - ~129 MB
- `mcp-forge/python:3.12` - ~123 MB
- `mcp-forge/jupyter:latest` - ~135-140 MB
## Updating Images
To update the Jupyter image with a newer ipykernel version:
1. Edit `docker/Dockerfile.jupyter`
2. Update the ipykernel version number
3. Rebuild: `podman build -f docker/Dockerfile.jupyter -t mcp-forge/jupyter:latest .`
4. Test with integration tests
5. Tag with version: `podman tag mcp-forge/jupyter:latest mcp-forge/jupyter:v1.0.1`
## Security Notes
All images run with:
- `network_mode: host` (for Jupyter kernels only, to enable ZMQ communication)
- `read_only: True` (filesystem is read-only except mounted volumes)
- `security_opt: ["no-new-privileges"]`
- Non-root user (UID:GID 1000:1000)
The host networking is required for ZMQ port communication between the host-side `jupyter-client` and the container-side `ipykernel`.

86
docker/build-images.sh Executable file
View file

@ -0,0 +1,86 @@
#!/bin/bash
# Build MCP-Forge container images
#
# Usage: ./build-images.sh [--all|--jupyter]
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
cd "$PROJECT_ROOT"
build_jupyter() {
echo "Building mcp-forge/jupyter:latest..."
podman build -f docker/Dockerfile.jupyter -t mcp-forge/jupyter:latest .
echo "✓ Built mcp-forge/jupyter:latest"
}
ensure_base_images() {
echo "Ensuring base Python images exist..."
# Check if images exist
if ! podman image exists mcp-forge/python:3.11; then
echo " Pulling and tagging python:3.11-slim..."
podman pull python:3.11-slim
podman tag python:3.11-slim mcp-forge/python:3.11
echo " ✓ Created mcp-forge/python:3.11"
else
echo " ✓ mcp-forge/python:3.11 exists"
fi
if ! podman image exists mcp-forge/python:3.12; then
echo " Pulling and tagging python:3.12-slim..."
podman pull python:3.12-slim
podman tag python:3.12-slim mcp-forge/python:3.12
echo " ✓ Created mcp-forge/python:3.12"
else
echo " ✓ mcp-forge/python:3.12 exists"
fi
}
verify_images() {
echo ""
echo "Verifying images..."
echo ""
podman images | grep -E "REPOSITORY|mcp-forge"
}
test_jupyter_image() {
echo ""
echo "Testing Jupyter image..."
echo -n " Testing ipykernel import... "
VERSION=$(podman run --rm mcp-forge/jupyter:latest python -c "import ipykernel; print(ipykernel.__version__)")
echo "✓ ipykernel $VERSION"
echo -n " Testing kernel launcher... "
podman run --rm mcp-forge/jupyter:latest python -m ipykernel_launcher --help > /dev/null 2>&1
echo "✓ launcher works"
}
# Parse arguments
ACTION="${1:-jupyter}"
case "$ACTION" in
--all)
ensure_base_images
build_jupyter
verify_images
test_jupyter_image
;;
--jupyter)
build_jupyter
test_jupyter_image
;;
*)
echo "Usage: $0 [--all|--jupyter]"
echo ""
echo " --all Ensure base images and build Jupyter image"
echo " --jupyter Build Jupyter image only (default)"
exit 1
;;
esac
echo ""
echo "✓ Done!"