Publishing MCP Servers to GitHub Container Registry: A Complete Guide

You can publish MCP servers to GitHub Container Registry (GHCR) by building a multi-stage Docker image, configuring a GitHub Actions workflow to automate the build and push process, and then referencing the resulting image in your MCP client configuration.

Publishing MCP servers to GitHub Container Registry enables seamless distribution and deployment of containerized Model Context Protocol implementations. The cr2007/mcp-wordle-python repository demonstrates this pattern by packaging a FastMCP server that exposes Wordle solutions into a lightweight Docker image hosted on GHCR. This guide walks through the architecture, manual and automated publishing workflows, and client configuration required to implement this pattern in your own MCP projects.

Understanding the MCP Server Architecture

The Wordle MCP server follows a layered architecture that separates the MCP protocol handling from business logic and external API communication.

Core Components

FastMCP Server Layer

The server initialization occurs in src/mcp_wordle/main.py where FastMCP("WordleMCP") creates the protocol handler. The get_wordle_data function is decorated with @mcp.tool() to register it as an available tool. When mcp.run() executes, FastMCP manages the stdio transport, request parsing, and response serialization.

Wordle API Client

The same file contains the API interaction logic. The function constructs a URL targeting https://www.nytimes.com/svc/wordle/v2/{target_date}.json and executes requests.get(url, timeout=300).json(). The date parameter defaults to the current ISO format date if not specified.

Dependency Management

The pyproject.toml file declares fastmcp>=2.9.2 and requests>=2.32.4 as dependencies. The project uses uv as its package manager, with uv.lock ensuring deterministic builds across environments.

Container Build Strategy

The Dockerfile implements a multi-stage build to minimize the final image size while maintaining build reproducibility.

Builder Stage

Based on ghcr.io/astral-sh/uv:0.7-python3.10-bookworm-slim, this stage:

  • Copies pyproject.toml and uv.lock
  • Executes uv sync --frozen --no-cache --compile-bytecode to create a virtual environment with compiled Python bytecode
  • Copies the source code from src/mcp_wordle/

Final Stage

Based on python:3.10-slim-bookworm, this stage:

  • Copies the pre-built virtual environment from the builder
  • Sets ENTRYPOINT ["mcp-wordle"] to launch the server immediately on container start

This approach yields an image that contains only the Python runtime and the application code, excluding build tools and caches.

How to Publish MCP Servers to GitHub Container Registry

Publishing requires either manual intervention for testing or automated workflows for continuous deployment.

Prerequisites

Before publishing, ensure you have:

  • A GitHub repository with a working Dockerfile at the root
  • The GITHUB_TOKEN secret available in your repository (enabled by default)
  • For local testing: Docker Buildx installed (docker buildx create --use)

Manual Build and Push

For testing or one-off releases, build and push manually:


# Enable Buildx for multi-platform support

docker buildx create --use

# Build and push multi-architecture image

docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t ghcr.io/USERNAME/mcp-server-name:latest \
  --push .

Replace USERNAME with your GitHub username and mcp-server-name with your repository name. The --push flag uploads directly to GHCR.

Verify the upload:

docker pull ghcr.io/USERNAME/mcp-server-name:latest
docker run --rm ghcr.io/USERNAME/mcp-server-name:latest --help

Automated Publishing with GitHub Actions

The repository includes .github/workflows/publish-image.yml to automate publishing. The workflow triggers on pushes to master that modify build-related files:

on:
  push:
    branches: [master]
    paths:
      - 'uv.lock'
      - 'pyproject.toml'
      - 'Dockerfile'
      - 'src/**'
      - '.github/workflows/publish-image.yml'

The workflow steps:

  1. Checkout the repository
  2. Set up QEMU for cross-platform builds
  3. Set up Docker Buildx
  4. Login to GHCR using ${{ secrets.GITHUB_TOKEN }}
  5. Build and push multi-platform images (linux/amd64, linux/arm64) with the latest tag

This ensures that any code change automatically produces an updated container image available at ghcr.io/cr2007/mcp-wordle-python:latest.

Configuring MCP Clients to Use GHCR Images

Once published, configure MCP-compatible clients to invoke the containerized server.

Docker-based Configuration

For Claude Desktop or similar clients, add this server configuration:

{
  "mcpServers": {
    "Wordle MCP (Python)": {
      "command": "docker",
      "args": [
        "run",
        "--rm",
        "-i",
        "--init",
        "-e",
        "DOCKER_CONTAINER=true",
        "ghcr.io/cr2007/mcp-wordle-python:latest"
      ]
    }
  }
}

The flags ensure:

  • --rm: Container cleans up after exit
  • -i: Interactive mode for stdio transport
  • --init: Proper signal handling
  • -e DOCKER_CONTAINER=true: Environment flag used by the application logic

Alternative uvx Installation

For environments where Docker is unavailable, use uvx to run directly from the repository:

{
  "mcpServers": {
    "Wordle MCP (Python)": {
      "command": "uvx",
      "args": [
        "--from",
        "git+https://github.com/cr2007/mcp-wordle-python",
        "mcp-wordle"
      ]
    }
  }
}

uvx handles dependency resolution and virtual environment creation automatically, pulling the code from GitHub and executing the mcp-wordle entrypoint defined in pyproject.toml.

Summary

Publishing MCP servers to GitHub Container Registry streamlines distribution and ensures consistent runtime environments across different client implementations.

  • Multi-stage Docker builds using uv produce minimal, secure images containing only the Python runtime and compiled application code
  • GitHub Actions automation in .github/workflows/publish-image.yml handles multi-platform builds (linux/amd64, linux/arm64) and pushes to GHCR on every relevant code change
  • Client configuration requires only a JSON snippet specifying the docker run command with the GHCR image URL, enabling immediate integration with Claude Desktop and other MCP clients
  • Alternative deployment via uvx provides a container-free option for development or resource-constrained environments

Frequently Asked Questions

How do I authenticate to GitHub Container Registry for manual pushes?

GitHub Container Registry uses the same authentication as Docker Hub but requires a GitHub Personal Access Token (PAT) with write:packages and read:packages scopes. Generate a token in your GitHub Settings under Developer settings > Personal access tokens > Tokens (classic). Then login using echo $CR_PAT | docker login ghcr.io -u USERNAME --password-stdin where $CR_PAT is your token. The GitHub Actions workflow uses the built-in GITHUB_TOKEN secret automatically, so no manual authentication is required for automated builds.

Why does the Dockerfile use a multi-stage build with uv instead of pip?

The multi-stage build strategy minimizes the final image size and attack surface by excluding build tools and caches from the runtime environment. The builder stage uses ghcr.io/astral-sh/uv:0.7-python3.10-bookworm-slim to install dependencies via uv sync --frozen --no-cache --compile-bytecode, which creates a deterministic virtual environment with pre-compiled Python bytecode. The final stage copies only this virtual environment into a slim python:3.10-slim-bookworm base image, resulting in a container that contains only the Python runtime and application code without uv, pip, or build dependencies.

How do I troubleshoot a container that exits immediately when run by an MCP client?

If the container exits immediately when invoked by an MCP client like Claude Desktop, check three common configuration issues. First, ensure the client configuration includes the -i (interactive) flag, as MCP servers communicate over stdio and require an interactive TTY to function. Second, verify that the --init flag is present to ensure proper signal handling, preventing zombie processes when the client terminates the connection. Third, check that the image tag is correct and accessible by running docker pull ghcr.io/USERNAME/IMAGE:latest manually to confirm the image exists and your Docker daemon has authentication to access GHCR.

Can I run the MCP server without Docker using the published GHCR image?

No, the GHCR image is specifically a Docker container image and requires a container runtime such as Docker, Podman, or containerd to execute. However, the repository provides an alternative installation method using uvx that achieves similar convenience without containerization. The uvx command fetches the source code directly from GitHub, creates a temporary virtual environment with all dependencies, and executes the mcp-wordle entrypoint defined in pyproject.toml. This method is ideal for development environments or systems where running Docker containers is impractical, though it requires the uv tool to be installed locally.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →