Configuration
All configuration is done via YAML files. The server requires a --config flag
pointing to a YAML configuration file.
Configuration file structure
The configuration file has two main sections: sources (where data comes from) and registries (named API surfaces that aggregate one or more sources).
# Sources define where registry data comes from
sources:
- name: toolhive
format: toolhive
git:
repository: https://github.com/stacklok/toolhive-catalog.git
branch: main
path: pkg/catalog/toolhive/data/registry-legacy.json
syncPolicy:
interval: '30m'
# Optional: filter which entries are synced
filter:
names:
include: ['official/*']
exclude: ['*/deprecated']
tags:
include: ['production']
exclude: ['experimental']
# Optional: claims inherited by entries during sync (for authorization)
claims:
org: 'acme'
# Registries aggregate sources into named API endpoints
registries:
- name: default
sources: ['toolhive']
# Optional: claims for access gating (caller JWT must satisfy these)
claims:
org: 'acme'
# Authentication configuration (required)
# See authentication.mdx for detailed configuration options
auth:
mode: anonymous
# Database configuration (required)
database:
host: localhost
port: 5432
user: registry
database: registry
sslMode: require
maxOpenConns: 25
maxIdleConns: 5
connMaxLifetime: '5m'
maxMetaSize: 262144
# Optional: dynamic authentication (alternative to pgpass)
# dynamicAuth:
# awsRdsIam:
# region: us-east-1
Sources vs. registries
- A source defines where data comes from and how to sync it. Each source has a type (Git, API, file, managed, or Kubernetes), sync policy, optional filters, and optional claims.
- A registry is a named API surface that aggregates one or more sources.
Clients access entries through a registry name in the URL (for example,
/{registryName}/v0.1/servers). A registry can also have claims that act as an access gate — only callers whose JWT claims satisfy the registry's claims can access its entries.
You can create multiple registries from the same sources to serve different
audiences. For example, a public registry with no claims and a team-internal
registry with team-scoped claims — both backed by the same source data.
Command-line flags
| Flag | Description | Required | Default |
|---|---|---|---|
--config | Path to YAML configuration file | Yes | - |
--address | Server listen address | No | :8080 |
--auth-mode | Override auth mode (anonymous or oauth) | No | - |
Registry data formats
The format field on a source specifies the JSON schema format for the registry
data:
upstream: The official upstream MCP registry format, used by the MCP Registry API and recommended for new registries.toolhive: The ToolHive-native format, used by the built-in ToolHive registry.
Registry sources
The server supports five source types, each with its own configuration options. You can configure multiple sources in a single server instance.
Git repository source
Clone and sync from Git repositories. Ideal for version-controlled registries.
When the registry server clones a Git repository, it stores the local copy in
/data. Mounting a persistent volume there is optional but recommended for
production deployments. Without it, the registry server re-clones the repository
on every container restart, adding startup latency and increasing network usage.
sources:
- name: toolhive
format: toolhive
git:
repository: https://github.com/stacklok/toolhive-catalog.git
branch: main
path: pkg/catalog/toolhive/data/registry-legacy.json
syncPolicy:
interval: '30m'
registries:
- name: default
sources: ['toolhive']
Configuration options:
repository(required): Git repository URLbranch(optional): Branch name to use (defaults tomain)tag(optional): Tag name to pin to a specific versioncommit(optional): Commit SHA to pin to a specific commitpath(optional): Path to the registry file within the repository (defaults toregistry.json)auth(optional): Authentication for private repositories (see below)
You can use branch, tag, or commit to pin to a specific version. If
multiple are specified, commit takes precedence over tag, which takes
precedence over branch.
Private repository authentication
To access private Git repositories, configure the auth section with your
credentials:
sources:
- name: private-registry
format: toolhive
git:
repository: https://github.com/my-org/private-registry.git
branch: main
path: registry.json
auth:
username: oauth2
passwordFile: /secrets/git/token
syncPolicy:
interval: '30m'
registries:
- name: default
sources: ['private-registry']
Authentication options:
auth.username(required withpasswordFile): Git username for HTTP Basic authentication. For GitHub and GitLab, useoauth2as the username when authenticating with a personal access token (PAT).auth.passwordFile(required withusername): Absolute path to a file containing the Git password or token. Whitespace is trimmed from the file content.
Both username and passwordFile must be specified together. If only one is
provided, the configuration will fail validation.
Using with Kubernetes secrets:
In Kubernetes deployments, mount a secret containing your Git token and reference the mount path:
apiVersion: v1
kind: Secret
metadata:
name: git-credentials
type: Opaque
stringData:
token: <YOUR_GITHUB_TOKEN>
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: registry-server
spec:
# Other deployment fields omitted, refer to the Deployment in Kubernetes guide
template:
spec:
containers:
- name: registry
volumeMounts:
- name: git-credentials
mountPath: /secrets/git
readOnly: true
- name: data
mountPath: /data
readOnly: false
volumes:
- name: git-credentials
secret:
secretName: git-credentials
items:
- key: token
path: token
- name: data
emptyDir: {}
API endpoint source
Sync from upstream MCP Registry APIs. Supports federation and aggregation scenarios.
sources:
- name: mcp-upstream
format: upstream
api:
endpoint: https://registry.modelcontextprotocol.io
syncPolicy:
interval: '1h'
registries:
- name: default
sources: ['mcp-upstream']
Configuration options:
endpoint(required): Base URL of the upstream MCP Registry API (without path)format(required): Must beupstreamfor API sources
The server automatically appends the appropriate API paths (/v0.1/servers,
etc.) to the endpoint URL.
File source
Read from filesystem. Ideal for local development and testing.
sources:
- name: local
format: upstream
file:
path: /data/registry.json
syncPolicy:
interval: '15m'
registries:
- name: default
sources: ['local']
Alternatively, the source can be a custom URL.
sources:
- name: remote
format: upstream
file:
url: https://www.example.com/registry.json
timeout: 5s
syncPolicy:
interval: '15m'
registries:
- name: default
sources: ['remote']
The fields path and url are mutually exclusive.
Configuration options:
path: Path to the registry file on the filesystem. Required whenurlis not specifiedurl: HTTP/HTTPS URL to fetch the registry file from. Required whenpathis not specifiedtimeout(optional): The timeout for HTTP requests when using URL, defaults to 30s, ignored whenpathis specified
Managed source
API-managed source for directly publishing and deleting MCP servers and skills via the API. Does not sync from external sources.
sources:
- name: internal
managed: {}
registries:
- name: default
sources: ['internal']
Configuration options:
managed(required): Empty object to indicate managed source type- No sync policy required (managed sources are updated via API, not synced)
Supported operations:
POST /{registryName}/v0.1/publish- Publish new server versionsDELETE /{registryName}/v0.1/servers/{name}/versions/{version}- Delete versionsGEToperations for listing and retrieving servers- Skills management via the extensions API
See the Registry API reference for complete endpoint documentation and request/response examples.
Kubernetes source
Discovers MCP servers from running Kubernetes deployments. Automatically creates registry entries for deployed MCP servers in your cluster.
When using the ToolHive operator, a single Kubernetes source named default is
automatically created and managed. You cannot configure additional Kubernetes
sources through the MCPRegistry CR or configuration file, as only one
Kubernetes source instance is supported per Registry Server instance.
The configuration example below is for reference when running the Registry Server standalone (without the operator).
By default, the Registry server discovers resources in all namespaces
(cluster-wide). You can restrict discovery to specific namespaces using the
THV_REGISTRY_WATCH_NAMESPACE environment variable. See
Workload discovery for configuration
details and RBAC requirements.
sources:
- name: default
format: toolhive
kubernetes: {}
registries:
- name: default
sources: ['default']
Configuration options:
kubernetes(required): Empty object or claim mapping configuration (see below)- No sync policy required (Kubernetes sources query live deployments on-demand)
- Only one Kubernetes source is supported per Registry Server instance
Claim mapping
You can map Kubernetes labels or annotations to authorization claims using the
claimMapping field. This allows entries discovered from Kubernetes workloads
to inherit claims based on their metadata — for example, mapping a team label
to a claim so that only members of that team can see the entry.
sources:
- name: default
format: toolhive
kubernetes:
claimMapping:
'toolhive.stacklok.dev/team': 'team'
registries:
- name: default
sources: ['default']
In this example, a workload annotated with
toolhive.stacklok.dev/team: platform would produce entries with
claims: {team: "platform"}. See Authorization for how
claims control entry visibility.
Kubernetes workload discovery works by looking for annotations in a specific set
of workloads. The types being watched are
MCPServer,
MCPRemoteProxy, and
VirtualMCPServer.
The Registry server will receive events when a resource among those listed above is annotated with the following annotations:
apiVersion: toolhive.stacklok.dev/v1alpha1
kind: MCPServer
metadata:
name: my-mcp-server
namespace: toolhive-system
annotations:
toolhive.stacklok.dev/registry-export: 'true'
toolhive.stacklok.dev/registry-title: 'Code Analysis Server'
toolhive.stacklok.dev/registry-url: 'https://mcp.example.com/servers/my-mcp-server'
toolhive.stacklok.dev/registry-description: |
Production MCP server for code analysis
toolhive.stacklok.dev/tool-definitions:
'[{"name":"analyze_code","description":"Analyze code for potential
issues","inputSchema":{"type":"object","properties":{"file":{"type":"string","description":"Path
to the file to
analyze"},"rules":{"type":"array","items":{"type":"string"},"description":"List
of rule IDs to check"}},"required":["file"]}}]'
spec:
# ... MCP server spec
| Annotation | Required | Description |
|---|---|---|
toolhive.stacklok.dev/registry-export | Yes | Must be "true" to include in registry |
toolhive.stacklok.dev/registry-url | Yes | The external endpoint URL for this server |
toolhive.stacklok.dev/registry-description | Yes | Description text displayed in registry listings |
toolhive.stacklok.dev/registry-title | No | Human-friendly display name for the registry entry (overrides the generated name) |
toolhive.stacklok.dev/tools | No | JSON array of tool name strings (e.g., ["get_weather","get_forecast"]) |
toolhive.stacklok.dev/tool-definitions | No | JSON array of tool definitions with MCP tool metadata (name, description, schema) |
Tool definitions format:
The tool-definitions annotation accepts a JSON array containing tool metadata
that follows the MCP specification. Each tool definition can include:
name(required): Tool identifierdescription: Human-readable description of what the tool doesinputSchema: JSON Schema describing the tool's input parametersoutputSchema: JSON Schema describing the tool's output formatannotations: Additional metadata about the tool
The Registry server validates JSON syntax only. Schema validation is performed by the operator during resource reconciliation. If the annotation contains invalid JSON, a warning is logged and the tool definitions are omitted from the registry, but the server is still registered normally.
Tool definitions appear in the Registry API response under the server's registry URL within the publisher-provided metadata:
{
"_meta": {
"io.modelcontextprotocol.registry/publisher-provided": {
"io.github.stacklok": {
"https://mcp.example.com/servers/my-mcp-server": {
"tool_definitions": [...]
}
}
}
}
}
The registry URL from the registry-url annotation becomes a key in the JSON
structure.
This feature requires the Registry server to be granted access to those resources via a Service Account, check the details in the deployment section.
Use cases:
- Discover MCP servers deployed via ToolHive Operator
- Automatically expose running MCP servers to end users
- Separate MCP server development from user consumption
Sync policy
Configure automatic synchronization of registry data on a per-source basis. Only applicable to synced sources (Git, API, File). Not required for managed or Kubernetes sources.
sources:
- name: toolhive
format: toolhive
git:
repository: https://github.com/stacklok/toolhive-catalog.git
branch: main
path: pkg/catalog/toolhive/data/registry-legacy.json
syncPolicy:
# Sync interval (e.g., "30m", "1h", "24h")
interval: '30m'
The interval field specifies how often the server should fetch updates from
the source. Use Go duration format (e.g., "30m", "1h", "24h").
The server also triggers a sync when filter configuration changes, even if the upstream data has not changed. This ensures that updated filter rules take effect without waiting for the next scheduled sync.
Sync policy is per-source and must be specified within each source configuration. Managed and Kubernetes sources do not require sync policies.
Server filtering
Optionally filter which servers are exposed through the API on a per-source basis. Only applicable to synced sources (Git, API, File).
sources:
- name: toolhive
format: toolhive
git:
repository: https://github.com/stacklok/toolhive-catalog.git
branch: main
path: pkg/catalog/toolhive/data/registry-legacy.json
syncPolicy:
interval: '30m'
filter:
names:
include: ['official/*']
exclude: ['*/deprecated']
tags:
include: ['production']
exclude: ['experimental']
Filter options:
names.include: List of name patterns to include (supports wildcards)names.exclude: List of name patterns to exclude (supports wildcards)tags.include: List of tags that servers must havetags.exclude: List of tags that servers must not have
Filters are per-source and specified within each source configuration.
Multiple sources and registries
You can configure multiple sources and combine them into one or more registries. This enables scenarios like aggregating data from different upstream registries, or serving different audiences from the same data with different access controls.
sources:
- name: toolhive-catalog
format: toolhive
git:
repository: https://github.com/stacklok/toolhive-catalog.git
branch: main
path: pkg/catalog/toolhive/data/registry-legacy.json
syncPolicy:
interval: '30m'
- name: upstream-mcp
format: upstream
api:
endpoint: https://registry.modelcontextprotocol.io
syncPolicy:
interval: '1h'
- name: internal
managed: {}
registries:
# Public registry with all sources
- name: public
sources: ['toolhive-catalog', 'upstream-mcp', 'internal']
# Team-scoped registry with access control
- name: platform-team
sources: ['toolhive-catalog', 'internal']
claims:
org: 'acme'
team: 'platform'
When a registry aggregates multiple sources, entries from earlier sources in the list take precedence over entries with the same name from later sources.
Authentication configuration
The server supports multiple authentication modes to fit different deployment
scenarios. All authentication configuration is done via the auth section in
your configuration file.
Available modes:
- Anonymous: No authentication (development/testing)
- OAuth with Kubernetes: Service account token validation
- OAuth with generic providers: Integration with Keycloak, Auth0, Okta, etc.
See the Authentication configuration guide for detailed information on configuring each mode.
Authorization configuration
When authentication is enabled, you can configure role-based access control (RBAC) and claims-based authorization to control who can manage sources, registries, and entries.
See the Authorization guide for detailed information on configuring roles and claims.
Database configuration
The server requires a PostgreSQL database for storing registry state and metadata. See the Database configuration guide for detailed information.
Telemetry configuration
The server supports OpenTelemetry for comprehensive observability, including distributed tracing and metrics collection. See the Telemetry and metrics guide for detailed information.
Next steps
- Configure authentication to secure access to your registry
- Set up authorization to control access with roles and claims
- Set up the database for production storage