Skip to main content

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).

config.yaml
# 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

FlagDescriptionRequiredDefault
--configPath to YAML configuration fileYes-
--addressServer listen addressNo:8080
--auth-modeOverride auth mode (anonymous or oauth)No-

Registry data formats

The format field on a source specifies the JSON schema format for the registry data:

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.

note

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.

config-git.yaml
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 URL
  • branch (optional): Branch name to use (defaults to main)
  • tag (optional): Tag name to pin to a specific version
  • commit (optional): Commit SHA to pin to a specific commit
  • path (optional): Path to the registry file within the repository (defaults to registry.json)
  • auth (optional): Authentication for private repositories (see below)
tip

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:

config-git-private.yaml
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 with passwordFile): Git username for HTTP Basic authentication. For GitHub and GitLab, use oauth2 as the username when authenticating with a personal access token (PAT).
  • auth.passwordFile (required with username): Absolute path to a file containing the Git password or token. Whitespace is trimmed from the file content.
warning

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:

registry-deployment.yaml
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.

config-api.yaml
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 be upstream for API sources
note

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.

config-file.yaml
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.

config-file-url.yaml
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 when url is not specified
  • url: HTTP/HTTPS URL to fetch the registry file from. Required when path is not specified
  • timeout (optional): The timeout for HTTP requests when using URL, defaults to 30s, ignored when path is specified

Managed source

API-managed source for directly publishing and deleting MCP servers and skills via the API. Does not sync from external sources.

config-managed.yaml
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 versions
  • DELETE /{registryName}/v0.1/servers/{name}/versions/{version} - Delete versions
  • GET operations 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.

Operator-managed source

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.

config-kubernetes.yaml
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.

config-kubernetes-claims.yaml
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.

How does it work?

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
AnnotationRequiredDescription
toolhive.stacklok.dev/registry-exportYesMust be "true" to include in registry
toolhive.stacklok.dev/registry-urlYesThe external endpoint URL for this server
toolhive.stacklok.dev/registry-descriptionYesDescription text displayed in registry listings
toolhive.stacklok.dev/registry-titleNoHuman-friendly display name for the registry entry (overrides the generated name)
toolhive.stacklok.dev/toolsNoJSON array of tool name strings (e.g., ["get_weather","get_forecast"])
toolhive.stacklok.dev/tool-definitionsNoJSON 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 identifier
  • description: Human-readable description of what the tool does
  • inputSchema: JSON Schema describing the tool's input parameters
  • outputSchema: JSON Schema describing the tool's output format
  • annotations: 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.

note

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 have
  • tags.exclude: List of tags that servers must not have
note

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.

config-multi-source.yaml
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