Skip to content

Commit 6c9a015

Browse files
committed
Поддержка Docker: контейнеризация и управление roots
- Dockerfile с multi-stage сборкой (eclipse-temurin:25-jdk/jre-alpine) - GitHub Actions workflow для автоматической публикации в ghcr.io - Переменная NTS_DOCKER_ROOTS для явного указания корневых директорий - Клиентские roots переопределяются в Docker-режиме (пути хоста недоступны внутри контейнера) - docker-compose.yml для локальной разработки - Обновлённая документация README (EN/RU) Граничные случаи NTS_DOCKER_ROOTS: - Пустое значение → fallback к PROJECT_ROOT или CWD - Множественные пути через двоеточие - Пути с пробелами - Пустые элементы (trailing separators) игнорируются
1 parent 1bec3f9 commit 6c9a015

6 files changed

Lines changed: 539 additions & 10 deletions

File tree

.dockerignore

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# =============================================================================
2+
# Docker ignore file for NTS MCP FileSystem Server
3+
# =============================================================================
4+
5+
# Git
6+
.git/
7+
.gitignore
8+
9+
# IDE
10+
.idea/
11+
.vscode/
12+
*.iml
13+
14+
# Build artifacts (we rebuild in Docker)
15+
build/
16+
app/build/
17+
.gradle/
18+
**/bin/
19+
**/out/
20+
21+
# Documentation (not needed in image)
22+
docs/
23+
*.md
24+
!README.md
25+
26+
# OS files
27+
.DS_Store
28+
Thumbs.db
29+
30+
# Logs and temp files
31+
*.log
32+
*.tmp
33+
.nts/
34+
35+
# Docker files (no recursion needed)
36+
Dockerfile
37+
docker-compose*.yml
38+
.dockerignore
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# =============================================================================
2+
# GitHub Actions: Build and publish Docker image to GitHub Container Registry
3+
# =============================================================================
4+
# Triggers:
5+
# - On release: builds and tags with version (v1.2.3 -> 1.2.3, latest)
6+
# - On push to main: builds and tags as 'edge' (development version)
7+
#
8+
# Images are published to: ghcr.io/nefrols/nts-mcp-fs
9+
# =============================================================================
10+
11+
name: Docker Build & Publish
12+
13+
on:
14+
release:
15+
types: [published]
16+
push:
17+
branches: [main]
18+
paths:
19+
- 'app/**'
20+
- 'gradle/**'
21+
- 'Dockerfile'
22+
- '.github/workflows/docker-publish.yml'
23+
workflow_dispatch: # Manual trigger
24+
25+
env:
26+
REGISTRY: ghcr.io
27+
IMAGE_NAME: nefrols/nts-mcp-fs
28+
29+
jobs:
30+
build-and-push:
31+
runs-on: ubuntu-latest
32+
33+
permissions:
34+
contents: read
35+
packages: write
36+
attestations: write
37+
id-token: write
38+
39+
steps:
40+
# ----------------------------------------------------------------------
41+
# Setup
42+
# ----------------------------------------------------------------------
43+
- name: Checkout repository
44+
uses: actions/checkout@v4
45+
46+
- name: Set up QEMU (for multi-platform builds)
47+
uses: docker/setup-qemu-action@v3
48+
49+
- name: Set up Docker Buildx
50+
uses: docker/setup-buildx-action@v3
51+
52+
# ----------------------------------------------------------------------
53+
# Authentication
54+
# ----------------------------------------------------------------------
55+
- name: Log in to GitHub Container Registry
56+
uses: docker/login-action@v3
57+
with:
58+
registry: ${{ env.REGISTRY }}
59+
username: ${{ github.actor }}
60+
password: ${{ secrets.GITHUB_TOKEN }}
61+
62+
# ----------------------------------------------------------------------
63+
# Metadata (tags and labels)
64+
# ----------------------------------------------------------------------
65+
- name: Extract metadata for Docker
66+
id: meta
67+
uses: docker/metadata-action@v5
68+
with:
69+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
70+
tags: |
71+
# For releases: v1.2.3 -> 1.2.3, 1.2, 1, latest
72+
type=semver,pattern={{version}}
73+
type=semver,pattern={{major}}.{{minor}}
74+
type=semver,pattern={{major}}
75+
# For main branch pushes: edge
76+
type=edge,branch=main
77+
# Always include commit SHA for traceability
78+
type=sha,prefix=
79+
labels: |
80+
org.opencontainers.image.title=NTS MCP FileSystem Server
81+
org.opencontainers.image.description=Transactional File System server for Model Context Protocol
82+
org.opencontainers.image.vendor=Nefrols
83+
84+
# ----------------------------------------------------------------------
85+
# Build and Push
86+
# ----------------------------------------------------------------------
87+
- name: Build and push Docker image
88+
id: push
89+
uses: docker/build-push-action@v6
90+
with:
91+
context: .
92+
platforms: linux/amd64,linux/arm64
93+
push: true
94+
tags: ${{ steps.meta.outputs.tags }}
95+
labels: ${{ steps.meta.outputs.labels }}
96+
cache-from: type=gha
97+
cache-to: type=gha,mode=max
98+
99+
# ----------------------------------------------------------------------
100+
# Attestation (provenance)
101+
# ----------------------------------------------------------------------
102+
- name: Generate artifact attestation
103+
uses: actions/attest-build-provenance@v2
104+
with:
105+
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
106+
subject-digest: ${{ steps.push.outputs.digest }}
107+
push-to-registry: true

Dockerfile

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# =============================================================================
2+
# NTS MCP FileSystem Server - Docker Image
3+
# =============================================================================
4+
# Multi-stage build for optimized image size
5+
#
6+
# IMPORTANT: Docker Mode and Roots
7+
# --------------------------------
8+
# In Docker, the server cannot access host filesystem paths directly.
9+
# You MUST explicitly mount directories and specify them via NTS_DOCKER_ROOTS.
10+
# These roots will OVERRIDE any roots sent by the MCP client.
11+
#
12+
# Usage Examples:
13+
#
14+
# Single project:
15+
# docker run -i --rm \
16+
# -v /home/user/myproject:/mnt/project \
17+
# -e NTS_DOCKER_ROOTS=/mnt/project \
18+
# nts-mcp-fs
19+
#
20+
# Multiple projects:
21+
# docker run -i --rm \
22+
# -v /home/user/project1:/mnt/p1 \
23+
# -v /home/user/project2:/mnt/p2 \
24+
# -e NTS_DOCKER_ROOTS=/mnt/p1:/mnt/p2 \
25+
# nts-mcp-fs
26+
#
27+
# For MCP clients (Claude Desktop, Cursor), configure:
28+
# {
29+
# "command": "docker",
30+
# "args": [
31+
# "run", "-i", "--rm",
32+
# "-v", "/home/user/project:/mnt/project",
33+
# "-e", "NTS_DOCKER_ROOTS=/mnt/project",
34+
# "nts-mcp-fs"
35+
# ]
36+
# }
37+
#
38+
# =============================================================================
39+
40+
# -----------------------------------------------------------------------------
41+
# Stage 1: Build
42+
# -----------------------------------------------------------------------------
43+
FROM eclipse-temurin:25-jdk AS builder
44+
45+
WORKDIR /build
46+
47+
# Copy Gradle wrapper and build files first (for layer caching)
48+
COPY gradlew gradlew.bat ./
49+
COPY gradle/ gradle/
50+
COPY settings.gradle.kts ./
51+
COPY app/build.gradle.kts app/
52+
53+
# Download dependencies (cached layer)
54+
RUN chmod +x gradlew && ./gradlew dependencies --no-daemon || true
55+
56+
# Copy source code
57+
COPY app/src/ app/src/
58+
59+
# Build fat JAR
60+
RUN ./gradlew shadowJar --no-daemon
61+
62+
# -----------------------------------------------------------------------------
63+
# Stage 2: Runtime
64+
# -----------------------------------------------------------------------------
65+
FROM eclipse-temurin:25-jre-alpine
66+
67+
LABEL org.opencontainers.image.title="NTS MCP FileSystem Server"
68+
LABEL org.opencontainers.image.description="Transactional File System server for Model Context Protocol"
69+
LABEL org.opencontainers.image.source="https://github.com/Nefrols/nts-mcp-fs"
70+
LABEL org.opencontainers.image.licenses="Apache-2.0"
71+
72+
# Create non-root user for security
73+
RUN addgroup -g 1000 nts && \
74+
adduser -u 1000 -G nts -s /bin/sh -D nts
75+
76+
WORKDIR /app
77+
78+
# Copy JAR from builder
79+
COPY --from=builder /build/app/build/libs/app-all.jar /app/nts-mcp-fs.jar
80+
81+
# Create default mount point
82+
RUN mkdir -p /mnt && chown nts:nts /mnt
83+
84+
# Switch to non-root user
85+
USER nts
86+
87+
# Environment variables:
88+
# NTS_DOCKER_ROOTS - Colon-separated list of root paths inside container.
89+
# These OVERRIDE client-provided roots (required for Docker).
90+
# Must match your -v volume mount points.
91+
# Example: /mnt/project1:/mnt/project2
92+
#
93+
# JAVA_OPTS - JVM options (default: ZGC with 512MB heap)
94+
# MCP_DEBUG - Set to "true" for debug logging to stderr
95+
# MCP_LOG_FILE - Path to log file for debugging
96+
97+
# ZGC is generational by default in Java 25+
98+
ENV JAVA_OPTS="-XX:+UseZGC -Xmx512m"
99+
100+
# Health check (optional, for orchestrators)
101+
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
102+
CMD pgrep -f "nts-mcp-fs.jar" || exit 1
103+
104+
# MCP servers communicate via stdio, so we need interactive mode
105+
# The entrypoint uses exec to properly handle signals
106+
ENTRYPOINT ["sh", "-c", "exec java $JAVA_OPTS -jar /app/nts-mcp-fs.jar"]

0 commit comments

Comments
 (0)