# Publishing MCP Servers to GitHub Container Registry: A Complete Guide

> Learn to publish MCP servers to GitHub Container Registry with this guide. Automate Docker builds and pushes using GitHub Actions for seamless deployment.

- Repository: [CSK/mcp-wordle-python](https://github.com/cr2007/mcp-wordle-python)
- Tags: tutorial
- Published: 2026-02-28

---

**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`](https://github.com/cr2007/mcp-wordle-python/blob/main/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`](https://github.com/cr2007/mcp-wordle-python/blob/main/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`](https://github.com/cr2007/mcp-wordle-python/blob/main/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:

```bash

# 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:

```bash
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`](https://github.com/cr2007/mcp-wordle-python/blob/main/.github/workflows/publish-image.yml) to automate publishing. The workflow triggers on pushes to `master` that modify build-related files:

```yaml
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:

```json
{
  "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:

```json
{
  "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`](https://github.com/cr2007/mcp-wordle-python/blob/main/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`](https://github.com/cr2007/mcp-wordle-python/blob/main/.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`](https://github.com/cr2007/mcp-wordle-python/blob/main/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.