Build MCP server containers
This guide explains how to use the thv build
command to build MCP server containers from protocol schemes without running
them. This is useful for pre-building containers for Kubernetes deployments,
CI/CD pipelines, and container registry workflows.
Overview
The thv build
command allows you to build containers from protocol schemes
(uvx://
, npx://
, go://
) without immediately running them. This provides
several benefits:
- Pre-build containers for faster deployment in Kubernetes environments
- Separate build and run phases in CI/CD pipelines
- Custom image tagging for container registry workflows
- Dockerfile generation for inspection and customization
- Build validation before deployment
Basic usage
To build a container from a protocol scheme:
thv build <PROTOCOL_SCHEME>
For example:
# Build a Python MCP server using uvx
thv build uvx://mcp-server-git
# Build a Node.js MCP server using npx
thv build npx://@modelcontextprotocol/server-filesystem
# Build a Go MCP server
thv build go://github.com/example/my-mcp-server@latest
When you run thv build
, ToolHive:
- Detects the protocol scheme and extracts the package reference
- Generates a Dockerfile based on the appropriate template
- Builds a Docker image with the package installed
- Tags the image with an auto-generated name or your custom tag
- Displays the built image name for use with other tools
Custom image tagging
Use the --tag
(or -t
) flag to specify a custom name and tag for the built
image:
thv build --tag my-custom-name:latest npx://@modelcontextprotocol/server-filesystem
This is particularly useful for:
- Container registries: Tag images for pushing to registries
- Kubernetes deployments: Use predictable image names in manifests
- Version management: Tag images with specific versions
Tagging examples
- Container Registry
- Kubernetes
- Version Management
Build and tag for pushing to a container registry:
# Build and tag for Docker Hub
thv build --tag myusername/mcp-git-server:v1.0.0 uvx://mcp-server-git
# Build and tag for GitHub Container Registry
thv build --tag ghcr.io/myorg/mcp-filesystem:latest npx://@modelcontextprotocol/server-filesystem
# Push to registry
docker push ghcr.io/myorg/mcp-filesystem:latest
Build images with predictable names for Kubernetes manifests:
# Build with a consistent tag
thv build --tag mcp-servers/git-server:stable uvx://mcp-server-git
# Use in Kubernetes manifest
# spec:
# image: mcp-servers/git-server:stable
Build multiple versions of the same server:
# Build different versions
thv build --tag mcp-git:v1.0.0 uvx://mcp-server-git@1.0.0
thv build --tag mcp-git:v1.1.0 uvx://mcp-server-git@1.1.0
thv build --tag mcp-git:latest uvx://mcp-server-git@latest
Protocol schemes
The thv build
command supports the same protocol schemes as
thv run
:
Python (uvx)
Build Python-based MCP servers using the uv package manager:
# Build with auto-generated name
thv build uvx://mcp-server-git
# Build with custom tag
thv build --tag my-git-server:latest uvx://mcp-server-git@1.2.0
Node.js (npx)
Build Node.js-based MCP servers using npm:
# Build with auto-generated name
thv build npx://@modelcontextprotocol/server-filesystem
# Build with custom tag
thv build --tag filesystem-server:v2.0 npx://@modelcontextprotocol/server-filesystem@2.0.0
Go
Build Go-based MCP servers:
# Build from remote Go module
thv build --tag grafana-mcp:latest go://github.com/grafana/mcp-grafana/cmd/mcp-grafana@latest
# Build from local Go project
thv build --tag my-local-server:dev go://./cmd/my-mcp-server
Dockerfile generation
Use the --dry-run
flag to generate the Dockerfile without building the image:
# Output Dockerfile to stdout
thv build --dry-run uvx://mcp-server-git
# Save Dockerfile to a file
thv build --dry-run --output Dockerfile.mcp-git uvx://mcp-server-git
This is useful for:
- Inspecting the build process before building
- Customizing Dockerfiles for specific requirements
- Understanding dependencies and build steps
- Debugging build issues
Example Dockerfile output
# Generated by: thv build --dry-run uvx://mcp-server-git
FROM python:3.12-slim
# Install uv
RUN pip install uv
# Install the package
RUN uv tool install mcp-server-git
# Set the entrypoint
ENTRYPOINT ["uv", "tool", "run", "mcp-server-git"]
Kubernetes workflows
The thv build
command is especially useful for Kubernetes deployments where
you want to pre-build containers before deploying them.
Pre-build workflow
-
Build the container with a specific tag:
thv build --tag ghcr.io/myorg/mcp-git:v1.0.0 uvx://mcp-server-git@1.0.0
-
Push to container registry:
docker push ghcr.io/myorg/mcp-git:v1.0.0
-
Deploy to Kubernetes using the pre-built image:
apiVersion: toolhive.stacklok.dev/v1alpha1
kind: MCPServer
metadata:
name: git-server
namespace: production
spec:
image: ghcr.io/myorg/mcp-git:v1.0.0
transport: stdio
permissionProfile:
type: builtin
name: network
CI/CD integration
Integrate thv build
into your CI/CD pipeline for automated container building:
# Example GitHub Actions workflow
name: Build and Deploy MCP Server
on:
push:
tags: ['v*']
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install ToolHive
run: |
# Install ToolHive CLI (replace with your preferred installation method)
# See: https://github.com/stacklok/toolhive/releases
wget https://github.com/stacklok/toolhive/releases/latest/download/toolhive_linux_amd64.tar.gz
tar -xzf toolhive_linux_amd64.tar.gz
sudo install -m 0755 thv /usr/local/bin/
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build MCP server
run: |
thv build --tag ghcr.io/${{ github.repository }}/mcp-server:${{ github.ref_name }} \
uvx://mcp-server-git@${{ github.ref_name }}
- name: Push to registry
run: |
docker push ghcr.io/${{ github.repository }}/mcp-server:${{ github.ref_name }}
For more advanced CI/CD patterns including multi-architecture builds, supply chain security, and change detection, see the Advanced CI/CD with ToolHive guide.
Advanced usage
Build with custom CA certificates
For corporate environments with custom certificate authorities:
# Use global CA certificate configuration
thv config set-ca-cert /path/to/corporate-ca.crt
thv build uvx://internal-mcp-server
# Override CA certificate for specific build
thv build --ca-cert /path/to/special-ca.crt uvx://special-server
Build local Go projects
Build MCP servers from local Go projects:
# Build from current directory
cd my-go-mcp-project
thv build --tag my-server:dev go://.
# Build from relative path
thv build --tag my-server:dev go://./cmd/server
# Build from absolute path
thv build --tag my-server:dev go:///path/to/my-project
Comparison with thv run
Feature | thv build | thv run |
---|---|---|
Purpose | Build containers only | Build and run containers |
Output | Container image | Running MCP server |
Use case | Pre-building, CI/CD | Development, testing |
Kubernetes | Pre-build for deployment | Direct development |
Custom tagging | ✅ --tag flag | ❌ Auto-generated names |
Dockerfile generation | ✅ --dry-run flag | ❌ Not available |
Next steps
- Use built containers with
thv run
for local development - Deploy pre-built containers to Kubernetes
- Set up CI/CD pipelines for automated building
- Learn about container registry workflows
Related information
Troubleshooting
Build fails with network errors
If builds fail with network connectivity issues:
- Check internet connectivity for downloading packages
- Configure CA certificates for corporate environments:
thv config set-ca-cert /path/to/corporate-ca.crt
- Use proxy settings if required by your network
- Verify package names and versions exist in the respective registries
Invalid image tag format
If you get image tag validation errors:
- Use valid Docker image tag format:
name:tag
orregistry/name:tag
- Avoid special characters except hyphens, underscores, and dots
- Use lowercase names for compatibility
- Check tag length limits (typically 128 characters)
Example valid tags:
thv build --tag my-server:latest uvx://package
thv build --tag ghcr.io/org/server:v1.0.0 npx://package
Package not found errors
If the build fails because a package cannot be found:
-
Verify package exists in the respective registry:
-
Check version specifiers:
# Correct version formats
thv build uvx://package@1.0.0
thv build npx://package@latest
thv build go://github.com/user/repo@v1.0.0 -
For Go modules, ensure the path includes the correct import path