diff --git a/.github/workflows/build-deploy.yaml b/.github/workflows/build-deploy.yaml new file mode 100644 index 0000000..ce87145 --- /dev/null +++ b/.github/workflows/build-deploy.yaml @@ -0,0 +1,76 @@ +name: build-deploy flux-mcp + +on: + pull_request: {} + release: + types: [published] + push: + branches: + - main + +jobs: + build-arm: + if: (github.event_name != 'pull_request') + runs-on: ubuntu-latest + name: make and build arm + env: + container: ghcr.io/converged-computing/flux-mcp + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + - name: GHCR Login + if: (github.event_name != 'pull_request') + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Add custom buildx ARM builder + run: | + docker buildx create --name armbuilder + docker buildx use armbuilder + docker buildx inspect --bootstrap + + - name: Build Container + run: make arm + + - name: Tag Release Image + if: (github.event_name == 'release') + run: | + tag=${GITHUB_REF#refs/tags/} + echo "Tagging and releasing ${{ env.container }}:${tag}" + docker tag ${{ env.container }}:latest ${{ env.container }}:${tag} + + - name: Deploy Container + run: make push + + build: + permissions: + packages: write + runs-on: ubuntu-latest + name: build + env: + container: ghcr.io/converged-computing/flux-mcp + steps: + - uses: actions/checkout@v4 + - name: Build Container + run: make + - name: Tag Release Image + if: (github.event_name == 'release') + run: | + tag=${GITHUB_REF#refs/tags/} + echo "Tagging and releasing ${{ env.container }}:${tag}" + docker tag ${{ env.container }}:latest ${{ env.container }}:${tag} + + - name: GHCR Login + if: (github.event_name != 'pull_request') + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Deploy Container + if: (github.event_name != 'pull_request') + run: make push diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..b8f994e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +FROM fluxrm/flux-sched:noble + +# docker build -t ghcr.io/converged-computing/flux-mcp:latest . +# Install system-level dependencies for HPC introspection +USER root +RUN apt-get update && apt-get install -y --no-install-recommends python3-pip python3-venv && \ + apt remove -y python3-zipp && apt remove -y python3-typing-extensions \ + && rm -rf /var/lib/apt/lists/* + +# Set environment variables +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 \ + PYTHONPATH=/code + +WORKDIR /code +COPY . /code +RUN python3 -m pip install mcp-serve --no-cache-dir --break-system-packages && python3 -m pip install . --no-cache-dir --break-system-packages +EXPOSE 8089 +ENTRYPOINT ["mcpserver", "start"] + +# Default command if no arguments are provided. +# We bind to 0.0.0.0 so the server is reachable outside the container. +CMD ["-t", "http", "--host", "0.0.0.0", "--port", "8089", "--config", "/code/examples/servers/flux-mcp.yaml"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..03b7eb6 --- /dev/null +++ b/Makefile @@ -0,0 +1,40 @@ +IMAGE_NAME ?= ghcr.io/converged-computing/flux-mcp +IMAGE_TAG ?= latest +FULL_IMAGE_NAME = $(IMAGE_NAME):$(IMAGE_TAG) +FULL_ARM_IMAGE = $(IMAGE_NAME):arm +DOCKERFILE_PATH = Dockerfile +BUILD_CONTEXT = . + +# Default target: builds the Docker image +all: build + +# Build the Docker image +build: + @echo "Building Docker image $(FULL_IMAGE_NAME)..." + docker build \ + -f $(DOCKERFILE_PATH) \ + -t $(FULL_IMAGE_NAME) \ + . + @echo "Docker image $(FULL_IMAGE_NAME) built successfully." + +# Push the docker image +push: + @echo "Pushing image $(FULL_IMAGE_NAME)..." + docker push $(IMAGE_NAME) --all-tags + +# Remove the image (clean with rmi) +clean: + @echo "Removing Docker image $(FULL_IMAGE_NAME)..." + docker rmi $(FULL_IMAGE_NAME) || true + @echo "Docker image $(FULL_IMAGE_NAME) removed (if it existed)." + +arm: + @echo "Building arm Docker image $(FULL_IMAGE_NAME)..." + docker buildx build \ + --platform linux/arm64 \ + -f $(DOCKERFILE_PATH) \ + -t $(FULL_ARM_IMAGE) \ + --load . + @echo "Docker image $(FULL_ARM_IMAGE) built successfully." + +.PHONY: all build push clean arm diff --git a/README.md b/README.md index 5850c01..a6514b7 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,15 @@ echo '{"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {"protocolVe python3 -m flux_mcp.server.fastmcp ``` +### Docker + +We have a provided [Dockerfile](Dockerfile) that builds and includes [mcp-server](https://github.com/converged-computing/mcp-server) to provide the basic set of Flux endpoints (submit, info, cancel, etc) via the configuration file [flux-mcp.yaml](examples/servers/flux-mcp.yaml). You can tweak that file and build, or just use from our GitHub packages registry. + +```bash +docker build -t ghcr.io/converged-computing/flux-mcp:latest . +docker run -it -p 8089:8089 ghcr.io/converged-computing/flux-mcp:latest +``` + ### Testing I will add tools to git as I write tests for them. To test, start the fastmcp server in one terminal: diff --git a/flux_mcp/validate/tools.py b/flux_mcp/validate/tools.py index 881f8fc..2ac9627 100644 --- a/flux_mcp/validate/tools.py +++ b/flux_mcp/validate/tools.py @@ -31,7 +31,8 @@ def flux_validate_batch_jobspec(content: Annotated[str, "Loaded jobspec"]): except Exception as e: display_error(content, str(e)) errors.append(str(e)) - return {"jobspec": None, "errors": errors, "valid": not errors} + # Return the original jobspec so there is a record + return {"jobspec": content, "errors": errors, "valid": not errors} def flux_validate_jobspec(content: Annotated[str, "Loaded jobspec"]): diff --git a/flux_mcp/version.py b/flux_mcp/version.py index 8ce1866..5291112 100644 --- a/flux_mcp/version.py +++ b/flux_mcp/version.py @@ -1,4 +1,4 @@ -__version__ = "0.0.16" +__version__ = "0.0.17" AUTHOR = "Vanessa Sochat" AUTHOR_EMAIL = "vsoch@users.noreply.github.com" NAME = "flux-mcp"