Quickstart: Registry Server
In this tutorial, you'll deploy the ToolHive Registry Server with Helm on a local Kubernetes cluster. You'll load one MCP server entry from a file and query it through the Registry API.
This quickstart covers the standalone Registry Server that you host and curate. If you're looking for the built-in catalog that ships with the ToolHive CLI and UI, see the Introduction for a comparison.
What you'll learn
- How to deploy a minimal PostgreSQL database for local testing
- How to define registry data in the ToolHive registry format
- How to configure and install the Registry Server with Helm
- How to query the Registry API
Prerequisites
Before starting, make sure you have:
- Helm (v3.10 minimum, v3.14+ recommended)
kubectl- Docker or Podman running
- kind
jqfor formatting API responses- Basic familiarity with Kubernetes concepts
Step 1: Create a kind cluster
Create a local Kubernetes cluster named registry-quickstart:
kind create cluster --name registry-quickstart
Create the namespace for the database and Registry Server:
kubectl create namespace toolhive-system
Step 2: Deploy PostgreSQL
The Registry Server requires PostgreSQL. For this quickstart, deploy a single-pod database without persistent storage or TLS. Do not use this database configuration for production workloads.
Create a manifest that defines the credentials Secret, Deployment, and Service:
apiVersion: v1
kind: Secret
metadata:
name: postgres-credentials
namespace: toolhive-system
type: Opaque
stringData:
POSTGRES_USER: registry
POSTGRES_PASSWORD: quickstart
POSTGRES_DB: registry
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
namespace: toolhive-system
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:16
envFrom:
- secretRef:
name: postgres-credentials
ports:
- containerPort: 5432
readinessProbe:
exec:
command: ['pg_isready', '-U', 'registry', '-d', 'registry']
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: postgres
namespace: toolhive-system
spec:
selector:
app: postgres
ports:
- port: 5432
targetPort: 5432
Apply the manifest and wait for PostgreSQL to become ready:
kubectl apply -f postgres.yaml
kubectl wait --for=condition=Ready pod -l app=postgres \
-n toolhive-system --timeout=120s
The Secret initializes the local database and later supplies the same password to the Registry Server. For production, use persistent storage, TLS, and separate application and migration users. See Configure the database for that pattern.
Step 3: Define the registry data
Create a ConfigMap containing one MCP server entry in the ToolHive registry format:
apiVersion: v1
kind: ConfigMap
metadata:
name: registry-data
namespace: toolhive-system
data:
registry.json: |
{
"version": "1.0.0",
"meta": {
"last_updated": "2026-04-21T00:00:00Z"
},
"data": {
"servers": [
{
"name": "io.github.stacklok/fetch",
"description": "Fetch web content for AI agents",
"title": "fetch",
"repository": {
"url": "https://github.com/StacklokLabs/gofetch",
"source": "github"
},
"version": "1.0.0",
"packages": [
{
"registryType": "oci",
"identifier": "ghcr.io/stackloklabs/gofetch/server:latest",
"transport": {
"type": "stdio"
}
}
],
"_meta": {
"io.modelcontextprotocol.registry/publisher-provided": {
"io.github.stacklok": {
"ghcr.io/stackloklabs/gofetch/server:latest": {
"status": "Active",
"tier": "Community",
"tags": ["web", "fetch"],
"tools": ["fetch"]
}
}
}
}
}
]
}
}
Apply the ConfigMap:
kubectl apply -f registry-data.yaml
Step 4: Configure the Registry Server
Create a Helm values file that connects the Registry Server to PostgreSQL and mounts the registry data:
fullnameOverride: registry-server
config:
sources:
- name: local
file:
path: /data/registry/registry.json
syncPolicy:
interval: '15m'
registries:
- name: default
sources: ['local']
auth:
mode: anonymous
database:
host: postgres
port: 5432
user: registry
database: registry
sslMode: disable
extraEnv:
- name: THV_REGISTRY_DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-credentials
key: POSTGRES_PASSWORD
extraVolumes:
- name: registry-data
configMap:
name: registry-data
extraVolumeMounts:
- name: registry-data
mountPath: /data/registry
readOnly: true
This is the complete Registry Server configuration for the quickstart. It
defines a file source, exposes that source through the default registry, uses
anonymous access, and supplies the database password from a Kubernetes Secret.
This example injects the password through an environment variable to keep the
local setup short. For a persistent deployment, use the
pgpass pattern.
Anonymous access and disabled database TLS are suitable only for this local cluster. You will replace both settings in a production deployment.
Step 5: Install the Registry Server
Install the Helm chart with the values file:
helm upgrade --install registry-server \
oci://ghcr.io/stacklok/toolhive-registry-server \
-n toolhive-system \
-f values.yaml \
--wait --timeout=3m
Helm creates a Deployment and Service named registry-server. On startup, the
Registry Server runs database migrations, loads the entry from the mounted file,
and starts serving the API on port 8080.
Confirm that the pod is ready:
kubectl get pods -n toolhive-system \
-l app.kubernetes.io/instance=registry-server
Step 6: Query the Registry API
Port-forward the Registry Server Service to your local machine. This command blocks, so leave it running and open a separate terminal for the API requests:
kubectl port-forward svc/registry-server 8080:8080 -n toolhive-system
In the separate terminal, list the entries in the default registry:
curl -s http://localhost:8080/registry/default/v0.1/servers | jq .
The response should contain the io.github.stacklok/fetch entry from the
ConfigMap.
Fetch version 1.0.0 of that entry. Server names use a reverse-domain
identifier, so URL-encode the / as %2F:
curl -s http://localhost:8080/registry/default/v0.1/servers/io.github.stacklok%2Ffetch/versions/1.0.0 \
| jq .
Step 7: Clean up
Stop the port-forward with Ctrl+C. Then delete the kind cluster to remove all
resources created in this tutorial:
kind delete cluster --name registry-quickstart
The Registry Server is available in both ToolHive Community and Stacklok Enterprise. Enterprise adds turnkey IdP integration, supply-chain-attested images, and SLA-backed support for teams running an internal MCP catalog at scale.
Next steps
- Configure sources and registries to replace the local file with Git, an upstream API, or Kubernetes discovery
- Deploy with Helm to adapt this installation for a persistent environment
- Set up authentication to replace anonymous access
Related information
- Configure the database explains production credentials, migration users, TLS, and connection settings
- Deployment options compares Helm with the other supported deployment methods
Troubleshooting
The Helm installation times out
Check the pod status, events, and Registry Server logs:
kubectl get pods -n toolhive-system
kubectl describe pods -n toolhive-system \
-l app.kubernetes.io/instance=registry-server
kubectl logs -n toolhive-system deployment/registry-server
Common causes include:
- PostgreSQL is not ready. Confirm the pod is running with
kubectl get pods -l app=postgres -n toolhive-system. - The database password does not match. Reapply
postgres.yaml, then reinstall the chart so both containers use the same Secret. - The registry JSON is invalid. Check the logs for parsing errors and
compare
registry-data.yamlwith the example in Step 3.
The /servers endpoint returns no entries
Check that the ConfigMap contains the registry file:
kubectl get configmap registry-data -n toolhive-system \
-o jsonpath='{.data.registry\.json}' | jq .
If you changed the ConfigMap after installation, restart the Deployment to trigger an immediate source sync:
kubectl rollout restart deployment/registry-server -n toolhive-system
kubectl rollout status deployment/registry-server \
-n toolhive-system --timeout=120s
Port 8080 is already in use
Forward the Service to a different local port, such as 8081:
kubectl port-forward svc/registry-server 8081:8080 -n toolhive-system
Then send requests to http://localhost:8081.