Skip to main content

Deploy and use the ToolHive Kubernetes operator

In this tutorial, you'll learn how to deploy the ToolHive Kubernetes operator and use it to manage MCP servers in a Kubernetes cluster. By the end, you'll have a working operator deployment that automatically manages MCP servers using Kubernetes resources.

What you'll learn

  • How to set up a local Kubernetes cluster with kind
  • How to deploy the ToolHive operator to your cluster
  • How to create your first MCP server using Kubernetes resources
  • How to verify that your MCP server is running correctly
  • How to connect an AI agent (like GitHub Copilot) to your MCP server

Prerequisites

Before starting this tutorial, make sure you have:

  • Helm (v3.10 minimum, v3.14+ recommended) installed
  • kubectl installed
  • Docker or Podman installed and running
  • kind installed
  • Basic familiarity with Kubernetes concepts (pods, deployments, services)
  • An MCP client (VS Code with Copilot is used in this tutorial, but you can use any supported client)

Quickstart with Task (TL;DR)

If you want to get up and running quickly and have Task installed, you can use our automated setup:

  1. Clone the ToolHive repository:

    git clone https://github.com/stacklok/toolhive.git
    cd toolhive
  2. Run the automated setup:

    task kind-with-toolhive-operator

This creates the kind cluster, installs an nginx ingress controller, and deploys the latest ToolHive operator image. You should see output indicating successful cluster creation and operator deployment. Once complete, skip to Step 3: Create your first MCP server to continue with the tutorial.

If you prefer to understand each step or don't have Task installed, continue with the manual setup below.

Step 1: Create a kind cluster

First, create a local Kubernetes cluster using kind. This gives you a safe environment to experiment with the ToolHive operator.

Create a cluster named toolhive:

kind create cluster --name toolhive

Verify your cluster is running:

kubectl cluster-info

You should see output similar to this:

Kubernetes control plane is running at https://127.0.0.1:xxxxx
CoreDNS is running at https://127.0.0.1:xxxxx/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

This confirms your cluster is running and ready for the ToolHive operator.

What's happening?

Kind (Kubernetes in Docker) creates a local Kubernetes cluster using Docker containers. This is perfect for development and testing because it's isolated from your main system and can be easily deleted when you're done.

Step 2: Deploy the ToolHive operator

Now deploy the ToolHive operator to your cluster using Helm. The operator will watch for MCP server resources and manage their lifecycle automatically.

First, install the operator CRDs:

helm upgrade -i toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds

Then install the operator:

helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator -n toolhive-system --create-namespace

Verify that the operator deployed successfully:

kubectl get pods -n toolhive-system

You should see output similar to:

NAME                    READY   STATUS    RESTARTS   AGE
toolhive-operator-xxx 1/1 Running 0 30s

If the pod shows "Running" status, your operator is ready to manage MCP servers.

What's happening?

The ToolHive operator is a Kubernetes controller that watches for MCPServer resources. When you create an MCPServer resource, the operator automatically creates the necessary pods, services, and configurations to run that MCP server in your cluster.

Step 3: Create your first MCP server

Now for the exciting part - create an MCP server using Kubernetes resources. You'll deploy the fetch server, which allows AI agents to retrieve web content.

Apply the example fetch MCP server from the ToolHive repository:

kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/heads/main/examples/operator/mcp-servers/mcpserver_fetch.yaml
What's happening?

When you create an MCPServer resource, the ToolHive operator detects it and automatically:

  1. Creates a deployment to run the MCP server container
  2. Sets up a service to expose the server
  3. Configures the necessary networking and security settings

Check that your MCP server was created successfully:

kubectl get mcpservers -n toolhive-system

You should see:

NAME    STATUS    URL                                                             AGE
fetch Running http://mcp-fetch-proxy.toolhive-system.svc.cluster.local:8080 30s

If the status is "Pending", wait a few moments and check again. If it remains pending for a long time, see the troubleshooting section at the end of this tutorial.

Step 4: Test your MCP server

Verify that your MCP server is actually working. First, get the service details:

kubectl get service mcp-fetch-proxy -n toolhive-system

Port-forward to access the service locally:

kubectl port-forward service/mcp-fetch-proxy -n toolhive-system 8080:8080

In another terminal, test the server:

curl http://localhost:8080/health

You should see a response of OK.

This confirms your MCP server is running and responding correctly.

What's happening?

The ToolHive operator automatically creates a Kubernetes service for each MCP server. This service provides a stable network endpoint that other applications (like AI agents) can use to communicate with your MCP server.

Step 5: Connect your AI client to the MCP server

Now that your MCP server is running in Kubernetes, connect it to an AI client application. We'll use Visual Studio Code with GitHub Copilot as an example.

Make sure you still have the port-forward running from Step 4. If not, restart it in a separate terminal:

kubectl port-forward service/mcp-fetch-proxy -n toolhive-system 8080:8080

Configure Visual Studio Code to connect to your MCP server. Open VS Code and access your user settings:

  1. Open the command palette (Cmd/Ctrl+Shift+P)
  2. Type "Preferences: Open User Settings (JSON)" and select it
VS Code user settings in command paletteVS Code user settings in command palette

Add the MCP server configuration to your settings.json file. If you have existing settings, merge the following configuration into it:

{
// Other existing settings...

"mcp": {
"servers": {
"fetch": {
"url": "http://localhost:8080/sse#fetch",
"type": "sse"
}
}
}
}

Save the file and click Start to initiate the connection. The indicator should change to "Running" and show "1 tools".

VS Code MCP settingsVS Code MCP settings VS Code MCP runningVS Code MCP running

Now test the connection by asking GitHub Copilot to fetch content from a website. Open Copilot Chat in Agent mode and ask: "Can you fetch the content from https://stacklok.com and summarize it for me?"

VS Code Copilot agent mode selectionVS Code Copilot agent mode selection

GitHub Copilot should be able to use your Kubernetes-hosted MCP server to retrieve the content and provide a summary.

VS Code Copilot tool use confirmationVS Code Copilot tool use confirmation
What's happening?

You're manually configuring VS Code to connect to your MCP server running in Kubernetes. The port-forward creates a tunnel from your local machine (port 8080) to the Kubernetes service, allowing GitHub Copilot to communicate with the server using the SSE (server-sent events) protocol.

Step 6: Explore operator features

Now that you have a working MCP server, explore some operator features.

View the detailed status of your MCP server:

kubectl describe mcpserver fetch -n toolhive-system

This shows you the current state, any events, and configuration details.

Try updating your MCP server's resource limits by editing the resource:

kubectl patch mcpserver fetch -n toolhive-system --type='merge' -p '{"spec":{"resources":{"limits":{"memory":"256Mi"}}}}'

You should see output confirming the patch:

mcpserver.toolhive.stacklok.dev/fetch patched

Check that the pod has been updated with the new resource limits:

kubectl get pods -n toolhive-system -l app.kubernetes.io/instance=fetch -o jsonpath='{.items[0].spec.containers[0].resources.limits.memory}'

You should see output showing the updated memory limit:

256Mi

This demonstrates how the operator automatically updates the underlying pod when you modify the MCPServer resource.

Step 7: Clean up

When you're done experimenting, you can clean up your resources.

Delete the MCP server:

kubectl delete mcpserver fetch -n toolhive-system

Verify it's been removed:

kubectl get mcpservers -n toolhive-system

You should see:

No resources found in toolhive-system namespace.

Check that the pods are also gone:

kubectl get pods -l app.kubernetes.io/name=fetch -n toolhive-system

You should see:

No resources found in toolhive-system namespace.
What's happening?

When you delete an MCPServer resource, the operator automatically cleans up all the associated Kubernetes resources (pods, services, etc.). This ensures no orphaned resources are left behind.

When you're completely finished, delete the kind cluster:

kind delete cluster --name toolhive
For Task users

If you followed the TL;DR setup using Task, you can also run:

task kind-destroy

This will fully remove the kind cluster and clean up all associated resources.

What's next?

Congratulations! You've successfully deployed the ToolHive operator and created your first MCP server using Kubernetes resources. You now have a working Kubernetes environment where MCP servers are automatically managed by the operator.

Here are some next steps to explore:

Troubleshooting

Operator pod not starting

If the operator pod isn't starting, check the logs:

kubectl logs -n toolhive-system deployment/toolhive-operator

MCP server stuck in pending state

Check the operator logs to see what's happening:

kubectl logs -n toolhive-system deployment/toolhive-operator -f

Also check if there are any resource constraints:

kubectl describe mcpserver fetch -n toolhive-system

Can't access MCP server

Verify the service is created and has endpoints:

kubectl get service mcp-fetch-proxy -n toolhive-system
kubectl get endpoints mcp-fetch-proxy -n toolhive-system