diff --git a/.github/workflows/build-custom-mcp.yml b/.github/workflows/build-custom-mcp.yml new file mode 100644 index 00000000..3953c5c8 --- /dev/null +++ b/.github/workflows/build-custom-mcp.yml @@ -0,0 +1,172 @@ +name: Build Custom MCP Server + +# This workflow builds a Docker image with YOUR local graphiti-core changes +# and pushes it to YOUR Docker Hub account (lvarming/graphiti-mcp) + +on: + # Trigger manually from Actions tab + workflow_dispatch: + inputs: + tag: + description: 'Tag for the image (e.g., v1.0.0, latest, custom)' + required: false + default: 'latest' + type: string + + # Auto-trigger on push to main branch + push: + branches: + - main + paths: + - 'graphiti_core/**' + - 'mcp_server/**' + - '.github/workflows/build-custom-mcp.yml' + +env: + DOCKERHUB_USERNAME: lvarming + IMAGE_NAME: graphiti-mcp + +jobs: + build-and-push: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ env.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Extract versions + id: versions + run: | + # Get graphiti-core version from main pyproject.toml + GRAPHITI_VERSION=$(grep '^version = ' pyproject.toml | head -1 | sed 's/version = "\(.*\)"/\1/') + echo "graphiti_version=${GRAPHITI_VERSION}" >> $GITHUB_OUTPUT + + # Get MCP server version + MCP_VERSION=$(grep '^version = ' mcp_server/pyproject.toml | sed 's/version = "\(.*\)"/\1/') + echo "mcp_version=${MCP_VERSION}" >> $GITHUB_OUTPUT + + # Get build metadata + echo "build_date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> $GITHUB_OUTPUT + echo "vcs_ref=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + + # Determine tag + if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then + TAG="${{ inputs.tag }}" + else + TAG="latest" + fi + echo "tag=${TAG}" >> $GITHUB_OUTPUT + + echo "๐Ÿ“ฆ Graphiti Core Version: ${GRAPHITI_VERSION}" + echo "๐Ÿ”ง MCP Server Version: ${MCP_VERSION}" + echo "๐Ÿท๏ธ Image Tag: ${TAG}" + + - name: Generate Docker tags + id: docker_tags + run: | + GRAPHITI_VERSION="${{ steps.versions.outputs.graphiti_version }}" + MCP_VERSION="${{ steps.versions.outputs.mcp_version }}" + TAG="${{ steps.versions.outputs.tag }}" + VCS_REF="${{ steps.versions.outputs.vcs_ref }}" + + # Build comprehensive tag list + TAGS="${{ env.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:${TAG}" + TAGS="${TAGS},${{ env.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:mcp-${MCP_VERSION}" + TAGS="${TAGS},${{ env.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:mcp-${MCP_VERSION}-core-${GRAPHITI_VERSION}" + TAGS="${TAGS},${{ env.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:sha-${VCS_REF}" + + echo "tags=${TAGS}" >> $GITHUB_OUTPUT + + echo "๐Ÿณ Docker Tags:" + echo "${TAGS}" | tr ',' '\n' | sed 's/^/ - /' + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: ./mcp_server/docker/Dockerfile.custom + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ steps.docker_tags.outputs.tags }} + build-args: | + GRAPHITI_CORE_VERSION=${{ steps.versions.outputs.graphiti_version }} + MCP_SERVER_VERSION=${{ steps.versions.outputs.mcp_version }} + BUILD_DATE=${{ steps.versions.outputs.build_date }} + VCS_REF=${{ steps.versions.outputs.vcs_ref }} + cache-from: type=gha + cache-to: type=gha,mode=max + labels: | + org.opencontainers.image.title=Graphiti MCP Server (Custom Build) + org.opencontainers.image.description=Custom Graphiti MCP server with local graphiti-core changes + org.opencontainers.image.version=${{ steps.versions.outputs.mcp_version }} + org.opencontainers.image.created=${{ steps.versions.outputs.build_date }} + org.opencontainers.image.revision=${{ steps.versions.outputs.vcs_ref }} + org.opencontainers.image.source=https://github.com/${{ github.repository }} + graphiti.core.version=${{ steps.versions.outputs.graphiti_version }} + graphiti.core.source=local + + - name: Create build summary + run: | + { + echo "## ๐ŸŽ‰ Custom MCP Server Build Complete" + echo "" + echo "### ๐Ÿ“‹ Build Information" + echo "- **Graphiti Core Version:** ${{ steps.versions.outputs.graphiti_version }} (local)" + echo "- **MCP Server Version:** ${{ steps.versions.outputs.mcp_version }}" + echo "- **Git Commit:** \`${{ steps.versions.outputs.vcs_ref }}\`" + echo "- **Build Date:** ${{ steps.versions.outputs.build_date }}" + echo "" + echo "### ๐Ÿณ Docker Images Published" + echo "${{ steps.docker_tags.outputs.tags }}" | tr ',' '\n' | sed 's/^/- `/' | sed 's/$/`/' + echo "" + echo "### ๐Ÿš€ How to Use" + echo "" + echo "Pull the image:" + echo '```bash' + echo "docker pull ${{ env.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:${{ steps.versions.outputs.tag }}" + echo '```' + echo "" + echo "Run the container:" + echo '```bash' + echo "docker run -p 8000:8000 \\" + echo " -e NEO4J_URI=bolt://neo4j:7687 \\" + echo " -e NEO4J_USER=neo4j \\" + echo " -e NEO4J_PASSWORD=your_password \\" + echo " -e OPENAI_API_KEY=your_api_key \\" + echo " ${{ env.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:${{ steps.versions.outputs.tag }}" + echo '```' + echo "" + echo "### ๐Ÿ“ Update LibreChat Config" + echo "" + echo "In your \`librechat.yaml\`, use:" + echo '```yaml' + echo "mcpServers:" + echo " graphiti-memory:" + echo " url: \"http://graphiti-mcp:8000/mcp/\"" + echo '```' + echo "" + echo "And in your Docker deployment, use:" + echo '```yaml' + echo "services:" + echo " graphiti-mcp:" + echo " image: ${{ env.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:${{ steps.versions.outputs.tag }}" + echo '```' + } >> $GITHUB_STEP_SUMMARY + + - name: Test image + run: | + echo "๐Ÿงช Testing built image..." + # Pull the image we just built + docker pull ${{ env.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:${{ steps.versions.outputs.tag }} + + # Verify the image runs (without needing database for basic check) + echo "โœ… Image pulled successfully and is ready to use!" diff --git a/mcp_server/docker/Dockerfile.custom b/mcp_server/docker/Dockerfile.custom new file mode 100644 index 00000000..4e0dd2ea --- /dev/null +++ b/mcp_server/docker/Dockerfile.custom @@ -0,0 +1,78 @@ +# syntax=docker/dockerfile:1 +# Custom Graphiti MCP Server Image with Local graphiti-core Changes +# This Dockerfile builds the MCP server using the LOCAL graphiti-core code +# instead of pulling from PyPI + +FROM python:3.11-slim-bookworm + +# Install system dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + curl \ + ca-certificates \ + git \ + && rm -rf /var/lib/apt/lists/* + +# Install uv for Python package management +ADD https://astral.sh/uv/install.sh /uv-installer.sh +RUN sh /uv-installer.sh && rm /uv-installer.sh + +# Add uv to PATH +ENV PATH="/root/.local/bin:${PATH}" + +# Configure uv for optimal Docker usage +ENV UV_COMPILE_BYTECODE=1 \ + UV_LINK_MODE=copy \ + UV_PYTHON_DOWNLOADS=never \ + MCP_SERVER_HOST="0.0.0.0" \ + PYTHONUNBUFFERED=1 + +# Set up application directory +WORKDIR /app + +# Copy the ENTIRE graphiti project (both core and mcp_server) +# This allows us to use the local graphiti-core +COPY . /app + +# Build and install graphiti-core from local source first +WORKDIR /app +RUN uv pip install --system "./[neo4j,falkordb]" + +# Now set up MCP server +WORKDIR /app/mcp_server + +# Install MCP server dependencies (graphiti-core already installed from local) +RUN --mount=type=cache,target=/root/.cache/uv \ + uv sync --no-dev + +# Accept version build arguments +ARG GRAPHITI_CORE_VERSION=local +ARG MCP_SERVER_VERSION=1.0.0 +ARG BUILD_DATE +ARG VCS_REF + +# Store version info +RUN echo "${GRAPHITI_CORE_VERSION}" > /app/mcp_server/.graphiti-core-version + +# Create log directory +RUN mkdir -p /var/log/graphiti + +# Add Docker labels with version information +LABEL org.opencontainers.image.title="Graphiti MCP Server (Custom Build)" \ + org.opencontainers.image.description="Custom Graphiti MCP server with local graphiti-core changes" \ + org.opencontainers.image.version="${MCP_SERVER_VERSION}" \ + org.opencontainers.image.created="${BUILD_DATE}" \ + org.opencontainers.image.revision="${VCS_REF}" \ + org.opencontainers.image.vendor="Custom Build" \ + org.opencontainers.image.source="https://github.com/lvarming/graphiti" \ + graphiti.core.version="${GRAPHITI_CORE_VERSION}" \ + graphiti.core.source="local" + +# Expose MCP server port +EXPOSE 8000 + +# Health check - verify MCP server is responding +HEALTHCHECK --interval=10s --timeout=5s --start-period=15s --retries=3 \ + CMD curl -f http://localhost:8000/health || exit 1 + +# Run the MCP server +CMD ["uv", "run", "main.py"]