feat: add Docker support for cross-platform easy setup

Implements comprehensive Docker support to eliminate Python version and dependency concerns.
Users can now run the MCP server in a container with automatic path translation between
host and container filesystems.

Key features:
- Dockerfile with multi-architecture support (amd64/arm64)
- Automatic path translation using WORKSPACE_ROOT environment variable
- Setup scripts for all platforms (Bash, CMD, PowerShell)
- GitHub Actions workflow for automated Docker Hub publishing
- Secure non-root container execution
- Read-only volume mounts by default

The setup process is now simplified to:
1. Run setup-docker-env script to generate .env and Claude config
2. Build the Docker image
3. Copy generated config to Claude Desktop

No Python installation or virtual environment management required.

Fixes #3

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Fahad
2025-06-09 22:25:06 +04:00
parent ed587f27d5
commit 6b03088eee
9 changed files with 651 additions and 18 deletions

View File

@@ -24,18 +24,29 @@ from typing import List, Optional, Tuple, Set
from .token_utils import estimate_tokens, MAX_CONTEXT_TOKENS
# Get workspace root for Docker path translation
# When running in Docker with a mounted workspace, WORKSPACE_ROOT contains
# the host path that corresponds to /workspace in the container
WORKSPACE_ROOT = os.environ.get("WORKSPACE_ROOT")
CONTAINER_WORKSPACE = Path("/workspace")
# Get project root from environment or use current directory
# This defines the sandbox directory where file access is allowed
#
# Security model:
# 1. If MCP_PROJECT_ROOT is explicitly set, use it as a sandbox
# 2. If not set, allow access to user's home directory and below
# 3. Never allow access to system directories outside home
# 2. If not set and in Docker (WORKSPACE_ROOT exists), use /workspace
# 3. Otherwise, allow access to user's home directory and below
# 4. Never allow access to system directories outside home
env_root = os.environ.get("MCP_PROJECT_ROOT")
if env_root:
# If explicitly set, use it as sandbox
PROJECT_ROOT = Path(env_root).resolve()
SANDBOX_MODE = True
elif WORKSPACE_ROOT and CONTAINER_WORKSPACE.exists():
# Running in Docker with workspace mounted
PROJECT_ROOT = CONTAINER_WORKSPACE
SANDBOX_MODE = True
else:
# If not set, default to home directory for safety
# This allows access to any file under the user's home directory
@@ -130,6 +141,45 @@ CODE_EXTENSIONS = {
}
def translate_docker_path(path_str: str) -> str:
"""
Translate host paths to container paths when running in Docker.
When running in Docker with WORKSPACE_ROOT set, this function translates
absolute paths from the host filesystem to their equivalent paths inside
the container. This enables seamless operation where Claude sends host
paths but the server runs in a container.
Args:
path_str: Original path string from the client
Returns:
Translated path string (unchanged if not in Docker mode)
"""
if not WORKSPACE_ROOT or not CONTAINER_WORKSPACE.exists():
# Not running in Docker mode, return path unchanged
return path_str
try:
# Resolve both paths to handle different path formats (forward/backslashes)
workspace_root_path = Path(WORKSPACE_ROOT).resolve()
host_path = Path(path_str).resolve()
# Get the relative path from workspace root
relative_path = host_path.relative_to(workspace_root_path)
# Construct container path using forward slashes (Linux format in container)
container_path = CONTAINER_WORKSPACE / relative_path
return container_path.as_posix()
except ValueError:
# Path is not within the workspace root, return unchanged
return path_str
except Exception:
# Any other error (invalid path, etc.), return unchanged
return path_str
def resolve_and_validate_path(path_str: str) -> Path:
"""
Validates that a path is absolute and resolves it.
@@ -149,6 +199,9 @@ def resolve_and_validate_path(path_str: str) -> Path:
ValueError: If path is not absolute
PermissionError: If path is outside allowed directory
"""
# Translate Docker paths if necessary
path_str = translate_docker_path(path_str)
# Create a Path object from the user-provided path
user_path = Path(path_str)