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:
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user