This repo sets up a complete ToolHive stack locally in Kubernetes using kind.
The goal is a fully functional ToolHive platform running locally to exercise all core features and interoperability with minimal external dependencies, making it suitable for demos, development, and testing.
So far, it includes:
- A ToolHive Registry Server with group-based access control (engineering, finance, shared tools) plus K8s auto-discovery
- Keycloak for OpenID Connect authentication with demo users and claims-based authorization
- The ToolHive Cloud UI connected to the registry server with Keycloak authentication
- Persona-specific vMCP gateways:
- vMCP Infra (engineering) — Prometheus, Grafana, OSV, OCI registry, and MKP (Kubernetes)
- vMCP Infra (Optimized) — same backends, but optimizer-enabled: exposes only
find_toolandcall_toolvia semantic tool search - vMCP Docs (shared) — Context7, internal docs fetch, and a remote-proxy to hosted ToolHive docs
- vMCP Finance (finance) — stubbed, awaiting real finance backends
- vMCP Research (shared) — arXiv with a composite research-topic tool
- The MKP MCP server for managing the cluster, also exposed directly
- A shared text-embeddings server (HuggingFace TEI) backing the optimizer-enabled vMCPs
- Traefik as the gateway for routing traffic into the cluster
- An observability stack (Prometheus, Loki, Grafana, OTel collector, fluent-bit) capturing metrics and audit logs from the MCP servers. Distributed tracing via Tempo is opt-in through the observability-tempo addon.
- Grafana dashboards for MCP server metrics, audit logs, and the registry
- macOS, Linux, or Windows (with WSL2, see note) with Docker (Podman might work too, but untested)
- kind, kubectl, and helm
- cloud-provider-kind
Note
Windows support is experimental and may require additional configuration. See Windows notes section for details.
- k9s for viewing cluster resources
- Clone this repo
- Run
./bootstrap.shfrom the repo root - When prompted, run
sudo cloud-provider-kindin a separate terminal to assign a local IP to the traefik Gateway (you can also just keep this running all the time) - Accept the self-signed certificate for both
https://ui-<IP>.sslip.ioandhttps://auth-<IP>.sslip.ioin your browser before logging in - Point the ToolHive CLI at the public registry:
thv config set-registry http://registry-<IP>.sslip.io/registry/public --allow-private-ip
- Access the Cloud UI, MCP servers, and Grafana via the URLs printed at the end of the bootstrap process
The bootstrap script is idempotent and can be re-run to fix any issues or reapply configurations.
The demo uses Keycloak for OpenID Connect authentication:
-
Admin Console:
https://auth-<IP>.sslip.io/admin- Username:
admin - Password:
admin
- Username:
-
Demo Users (realm:
toolhive-demo, client:toolhive-cloud-ui):User Password Groups Gateways they can reach demodemoeveryone, engineering, finance All gateways + registry superAdmin alicealiceeveryone, engineering vMCP Infra (Prometheus/Grafana/OSV/OCI/MKP), vMCP Docs, vMCP Research, MKP bobbobeveryone, finance vMCP Finance (stub), vMCP Docs, vMCP Research All users also see shared catalog entries (Notion, Time, ToolHive docs). Alice additionally sees engineering-only catalog entries (AWS docs, Playwright, GitLab, Figma, Postman). Bob sees finance-only catalog entries (Stripe remote).
If the bootstrap fails, check the output for errors. You can also use kubectl or k9s to inspect the cluster state.
To re-run the bootstrap with more verbosity, you can set the DEBUG environment variable:
DEBUG=1 ./bootstrap.shTo validate all services are working:
./validate.shFor symptom-specific recipes (Keycloak redirect-URI drift, vMCP crashloops, rate-limit lockouts, useful one-liners for minting tokens / inspecting rendered vMCP configs / listing registry entries per persona), see TROUBLESHOOTING.md.
Run the ./cleanup.sh script to delete the cluster.
./dev-reload.sh builds the operator / proxyrunner / vMCP images from a local
ToolHive checkout, retags them to match the demo's expected image refs, loads
them into the kind cluster, and restarts affected pods so new code takes
effect.
Requires ko, task, docker,
kind, and kubectl.
# Point at your local toolhive checkout (default: ../toolhive or ../../stacklok/toolhive)
export TOOLHIVE_SRC=~/path/to/toolhive
./dev-reload.sh # rebuild & reload all three (default)
./dev-reload.sh --operator # operator only
./dev-reload.sh --proxyrunner # MCPServer / MCPRemoteProxy pods
./dev-reload.sh --vmcp # VirtualMCPServer pods- After
./bootstrap.sh: the base demo pods exist; this replaces their images with your local build. Use this to iterate on ToolHive changes against the live demo. - Before deploying addons (e.g.
addons/aws-mcp,addons/aws-vmcp): once the local images are loaded into the node cache with the tag the operator expects, any new pods the addon creates use your local build automatically (thanks toimagePullPolicy: IfNotPresent). - After deploying addons: re-run with
--proxyrunneror--vmcpto refresh addon-created pods with updated code.
None at this time. Please open issues if you encounter any problems.
- Add an observability stack to catch traces/metrics
- Add pre-configured Grafana dashboard for MCP server metrics
- Harden the bootstrap script with more error checking and idempotency
- Add toolhive-cloud-ui deployment
- Add MCP Optimizer demo server
- Deploy registry server using the ToolHive Operator instead of manually
- Add a Keycloak instance for authentication
- Claims-based authorization with group-scoped registry sources
- Add an authenticated version of the vMCP server
- Persona-specific vMCP server demos
A successful bootstrap should end with output similar to this:
./bootstrap.sh
Running preflight checks...
Checking required binaries... ✓
Creating Kind cluster... ✓
Adding Helm repositories... ✓
Updating Helm repositories... ✓
Installing cert-manager... ✓
Installing Traefik... ✓
Installing observability stack... ✓
Installing ToolHive Operator... ✓
Checking for Traefik Gateway IP... ✓ (172.19.0.3)
Installing Keycloak... ✓
Creating PostgreSQL server for ToolHive Registry Server... ✓
Creating Traefik CA ConfigMap for registry server TLS verification... ✓
Installing shared MCPTelemetryConfig resource... ✓
Installing persona MCPGroups and backends... ✓
Installing embedding server for optimizer-enabled vMCPs... ✓
Installing persona vMCP gateways... ✓
Installing Registry Server... ✓
Installing Cloud UI... ✓
Configuring Grafana HTTPRoute... ✓
Waiting for all pods to be ready... ✓
Validating registry server... ✓ (18 unique servers detected)
Writing endpoint information to demo-endpoints.json... ✓
Bootstrap complete! Access your demo services at the following URLs:
- Keycloak Admin Console at https://auth-172-19-0-3.sslip.io/admin (admin/admin)
- ToolHive Cloud UI at https://ui-172-19-0-3.sslip.io (NOTE: self-signed cert, expect a browser warning)
Demo Users:
demo / demo — Admin persona (registry superAdmin, sees all tools)
alice / alice — Engineering persona
bob / bob — Finance persona
All users see shared tools and in-cluster MCP servers.
- ToolHive Registry Server at http://registry-172-19-0-3.sslip.io/registry/demo-registry
(Note: registry requires authentication — use the Cloud UI or a valid Keycloak Bearer token)
- Public Registry (no auth) at http://registry-172-19-0-3.sslip.io/registry/public for the ToolHive CLI or UI
(run 'thv config set-registry http://registry-172-19-0-3.sslip.io/registry/public --allow-private-ip' or addin the UI settings)
- MKP MCP server (standalone, engineering) at http://mcp-172-19-0-3.sslip.io/mkp/mcp
- vMCP Infra gateway (alice/engineering) at http://mcp-172-19-0-3.sslip.io/vmcp-infra/mcp
- vMCP Infra gateway (optimizer-enabled) at http://mcp-172-19-0-3.sslip.io/vmcp-infra-optimized/mcp
- vMCP Docs gateway (shared) at http://mcp-172-19-0-3.sslip.io/vmcp-docs/mcp
- vMCP Finance gateway (bob/finance, stub) at http://mcp-172-19-0-3.sslip.io/vmcp-finance/mcp
- vMCP Platform-Ops gateway (alice/engineering, composite-tool demo) at http://mcp-172-19-0-3.sslip.io/vmcp-platform/mcp
- Grafana at http://grafana-172-19-0-3.sslip.io
- On Windows, the bootstrap script must be run from a WSL2 terminal with Docker Desktop configured to use the WSL2 backend.
- kind requires at least version 2.5.1 of WSL2 (update using
wsl --updateif needed). - The IP assigned by cloud-provider-kind will not be reachable from Windows host applications. For example, to access the Cloud UI, you will need to use a browser inside WSL2 (e.g., Firefox or Chromium installed in WSL2) or set up port forwarding from WSL2 to Windows.