The ToolHive Kubernetes Operator manages MCP (Model Context Protocol) servers and registries in Kubernetes clusters. It allows you to define MCP servers and registries as Kubernetes resources and automates their deployment and management.
This operator is built using Kubebuilder, a framework for building Kubernetes APIs using Custom Resource Definitions (CRDs).
The operator introduces two main Custom Resource Definitions (CRDs):
Represents an MCP server in Kubernetes. When you create an MCPServer resource, the operator automatically:
- Creates a Deployment to run the MCP server
- Sets up a Service to expose the MCP server
- Configures the appropriate permissions and settings
- Manages the lifecycle of the MCP server
Represents an MCP server registry in Kubernetes. When you create an MCPRegistry resource, the operator automatically:
- Synchronizes registry data from various sources (ConfigMap, Git)
- Deploys a Registry API service for server discovery
- Provides content filtering and image validation
- Manages automatic and manual synchronization policies
For detailed MCPRegistry documentation, see REGISTRY.md.
---
config:
theme: dark
look: classic
layout: dagre
---
flowchart LR
subgraph Kubernetes
direction LR
namespace
User1["Client"]
end
subgraph namespace[namespace: toolhive-system]
operator["POD: Operator"]
sse
streamable-http
stdio
end
subgraph sse[SSE MCP Server Components]
operator -- creates --> THVProxySSE[POD: ToolHive-Proxy] & TPSSSE[SVC: ToolHive-Proxy]
THVProxySSE -- creates --> MCPServerSSE[POD: MCPServer] & MCPHeadlessSSE[SVC: MCPServer-HeadlessService]
User1 -- HTTP/SSE --> TPSSSE
TPSSSE -- HTTP/SSE --> THVProxySSE
THVProxySSE -- HTTP/SSE --> MCPHeadlessSSE
MCPHeadlessSSE -- HTTP/SSE --> MCPServerSSE
end
subgraph stdio[STDIO MCP Server Components]
operator -- creates --> THVProxySTDIO[POD: ToolHive-Proxy] & TPSSTDIO[SVC: ToolHive-Proxy]
THVProxySTDIO -- creates --> MCPServerSTDIO[POD: MCPServer]
User1 -- HTTP/SSE --> TPSSTDIO
TPSSTDIO -- HTTP/SSE --> THVProxySTDIO
THVProxySTDIO -- Attaches/STDIO --> MCPServerSTDIO
end
To run the basic operator-only tests (unit and integration), use the following command from the root of the project:
task operator:operator-testThis will run all Go tests in the operator codebase.
The task commands for the operator are designed to be run from the root of the project.
To run the Operator end-to-end (E2E) tests locally, ensure you have the following installed:
- Go
- Kind
- Kind Load Balancer
- Task
- Chainsaw (automatically installed by the Taskfile for local runs)
- Set up the Kind cluster:
task operator:kind-setup- Run the Operator E2E tests:
task operator:operator-e2e-testNote: The Taskfile will ensure Chainsaw is installed locally if not present. In CI, Chainsaw is installed via the GitHub Action.
- Kubernetes cluster (v1.19+)
- kubectl configured to communicate with your cluster
- Install the CRD:
helm upgrade -i toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds- Install the operator:
# Standard installation
helm upgrade -i <release_name> oci://ghcr.io/stacklok/toolhive/toolhive-operator --version=<version> -n toolhive-system --create-namespaceTo create an MCP server, define an MCPServer resource and apply it to your cluster:
apiVersion: toolhive.stacklok.dev/v1alpha1
kind: MCPServer
metadata:
name: fetch
spec:
image: docker.io/mcp/fetch
transport: stdio
proxyPort: 8080
mcpPort: 8080
resources:
limits:
cpu: "100m"
memory: "128Mi"
requests:
cpu: "50m"
memory: "64Mi"Apply this resource:
kubectl apply -f your-mcpserver.yamlFor MCP servers that require authentication tokens or other secrets:
apiVersion: toolhive.stacklok.dev/v1alpha1
kind: MCPServer
metadata:
name: github
namespace: toolhive-system
spec:
image: ghcr.io/github/github-mcp-server
proxyPort: 8080
mcpPort: 8080
secrets:
- name: github-token
key: token
targetEnvName: GITHUB_PERSONAL_ACCESS_TOKENFirst, create the secret:
kubectl create secret generic github-token -n toolhive-system --from-literal=token=<YOUR_GITHUB_TOKEN>Then apply the MCPServer resource.
The secrets field has the following parameters:
name: The name of the Kubernetes secret (required)key: The key in the secret itself (required)targetEnvName: The environment variable to be used when setting up the secret in the MCP server (optional). If left unspecified, it defaults to the key.
To check the status of your MCP servers:
kubectl get mcpserversThis will show the status, URL, and age of each MCP server.
For more details about a specific MCP server:
kubectl describe mcpserver <name>| Field | Description | Required | Default |
|---|---|---|---|
image |
Container image for the MCP server | Yes | - |
transport |
Transport method (stdio, streamable-http or sse) | No | stdio |
proxyPort |
Port to expose the MCP server on | No | 8080 |
mcpPort |
Port that MCP server listens to | No | - |
args |
Additional arguments to pass to the MCP server | No | - |
env |
Environment variables to set in the container | No | - |
volumes |
Volumes to mount in the container | No | - |
resources |
Resource requirements for the container | No | - |
secrets |
References to secrets to mount in the container | No | - |
permissionProfile |
Permission profile configuration (not implemented) | No | - |
tools |
Allow-list filter on the list of tools | No | - |
First, create a ConfigMap containing ToolHive registry data. The ConfigMap must be user-defined and is not managed by the operator:
# Create ConfigMap from existing registry data
kubectl create configmap my-registry-data --from-file registry.json=pkg/registry/data/registry.json -n toolhive-system
# Or create from your own registry file
kubectl create configmap my-registry-data --from-file registry.json=/path/to/your/registry.json -n toolhive-systemThen create the MCPRegistry resource that references the ConfigMap:
apiVersion: toolhive.stacklok.dev/v1alpha1
kind: MCPRegistry
metadata:
name: my-registry
namespace: toolhive-system
spec:
displayName: "My MCP Registry"
source:
type: configmap
configmap:
name: my-registry-data # References the user-created ConfigMap
key: registry.json # Key in ConfigMap (default: "registry.json")
syncPolicy:
interval: "1h"
filter:
tags:
include: ["production"]
exclude: ["experimental"]For complete MCPRegistry examples and documentation, see REGISTRY.md.
- MCPServer examples:
examples/operator/mcp-servers/directory - MCPRegistry examples:
examples/operator/mcp-registries/directory
To build the operator:
go build -o bin/thv-operator cmd/thv-operator/main.goFor development, you can run the operator locally:
go run cmd/thv-operator/main.goThis will use your current kubeconfig to connect to the cluster.
This operator is scaffolded using Kubebuilder. If you want to make changes to the API or controller, you can use Kubebuilder commands to help you.
- Install Kubebuilder: https://book.kubebuilder.io/quick-start.html#installation
Generate CRD manifests:
kubebuilder create api --group toolhive --version v1alpha1 --kind MCPServerUpdate CRD manifests after changing API types:
task operator-manifestsRun the controller locally:
task operator-runThe Kubebuilder project structure is as follows:
api/v1alpha1/: Contains the API definitions for the CRDscontrollers/: Contains the reconciliation logic for the controllersconfig/: Contains the Kubernetes manifests for deploying the operatorPROJECT: Kubebuilder project configuration file
For more information on Kubebuilder, see the Kubebuilder Book.