From 0351671bb1c62559697b43f0d0cd183640770479 Mon Sep 17 00:00:00 2001 From: Radoslav Dimitrov Date: Wed, 11 Feb 2026 17:06:16 +0200 Subject: [PATCH 1/3] Registry server documentation for v0.6.0 Signed-off-by: Radoslav Dimitrov --- .../guides-registry/configuration.mdx | 46 +++++--- docs/toolhive/guides-registry/database.mdx | 70 ++++++++--- docs/toolhive/guides-registry/deployment.mdx | 109 +++++++++++++++--- 3 files changed, 176 insertions(+), 49 deletions(-) diff --git a/docs/toolhive/guides-registry/configuration.mdx b/docs/toolhive/guides-registry/configuration.mdx index 9c67a18a..736db019 100644 --- a/docs/toolhive/guides-registry/configuration.mdx +++ b/docs/toolhive/guides-registry/configuration.mdx @@ -44,7 +44,7 @@ registries: auth: mode: anonymous -# Optional: Database configuration +# Database configuration (required) database: host: localhost port: 5432 @@ -54,14 +54,20 @@ database: maxOpenConns: 25 maxIdleConns: 5 connMaxLifetime: '5m' + maxMetaSize: 65536 + # Optional: dynamic authentication (alternative to pgpass) + # dynamicAuth: + # awsRdsIam: + # region: us-east-1 ``` ## Command-line flags -| Flag | Description | Required | Default | -| ----------- | ------------------------------- | -------- | ------- | -| `--config` | Path to YAML configuration file | Yes | - | -| `--address` | Server listen address | No | `:8080` | +| 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 @@ -101,7 +107,11 @@ registries: - `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` (required): Path to the registry file within the repository +- `path` (optional): Path to the registry file within the repository (defaults + to `registry.json`) +- `auth.username` (optional): Username for Git authentication +- `auth.passwordFile` (optional): Path to a file containing the Git password or + token :::tip @@ -221,12 +231,11 @@ Server standalone (without the operator). ::: -:::note - -Currently, only resources in the same namespace as the Registry Server are -discovered. - -::: +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](./deployment.mdx#workload-discovery) for configuration +details and RBAC requirements. ```yaml title="config-kubernetes.yaml" registries: @@ -258,7 +267,7 @@ apiVersion: toolhive.stacklok.dev/v1alpha1 kind: MCPServer metadata: name: my-mcp-server - namespace: toolhive-system # Must match Registry Server namespace + namespace: toolhive-system annotations: toolhive.stacklok.dev/registry-export: 'true' toolhive.stacklok.dev/registry-url: 'https://mcp.example.com/servers/my-mcp-server' @@ -279,6 +288,7 @@ spec: | `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/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:** @@ -303,7 +313,7 @@ URL within the publisher-provided metadata: ```json { "_meta": { - "publisher-provided": { + "io.modelcontextprotocol.registry/publisher-provided": { "io.github.stacklok": { "https://mcp.example.com/servers/my-mcp-server": { "tool_definitions": [...] @@ -410,9 +420,9 @@ information on configuring each mode. ## Database configuration -The server optionally supports PostgreSQL database connectivity for storing -registry state and metadata. See the [Database configuration](./database.mdx) -guide for detailed information. +The server requires a PostgreSQL database for storing registry state and +metadata. See the [Database configuration](./database.mdx) guide for detailed +information. ## Telemetry configuration @@ -425,4 +435,4 @@ distributed tracing and metrics collection. See the - [Configure authentication](./authentication.mdx) for secure access - [Configure telemetry](./telemetry-metrics.mdx) for metrics and tracing - [Deploy the server](./deployment.mdx) with your configuration -- [Configure database storage](./database.mdx) for production use +- [Configure database storage](./database.mdx) diff --git a/docs/toolhive/guides-registry/database.mdx b/docs/toolhive/guides-registry/database.mdx index ea618f4b..54c621df 100644 --- a/docs/toolhive/guides-registry/database.mdx +++ b/docs/toolhive/guides-registry/database.mdx @@ -5,9 +5,9 @@ description: Registry server --- -The Registry server optionally supports PostgreSQL database connectivity for -storing registry state and metadata. This enables persistence across restarts -and provides a foundation for advanced features. +The Registry server requires a PostgreSQL database for storing registry state +and metadata. This enables persistence across restarts and provides a foundation +for advanced features. ## Configuration @@ -23,24 +23,27 @@ database: maxOpenConns: 25 maxIdleConns: 5 connMaxLifetime: '5m' + maxMetaSize: 65536 ``` ### Configuration fields -| Field | Type | Required | Default | Description | -| ----------------- | ------ | -------- | --------- | -------------------------------------------------------------------------- | -| `host` | string | Yes | - | Database server hostname or IP address | -| `port` | int | Yes | - | Database server port | -| `user` | string | Yes | - | Database username for normal operations | -| `migrationUser` | string | No | `user` | Database username for running migrations (should have elevated privileges) | -| `database` | string | Yes | - | Database name | -| `sslMode` | string | No | `require` | SSL mode (`disable`, `require`, `verify-ca`, `verify-full`) | -| `maxOpenConns` | int | No | `25` | Maximum number of open connections to the database | -| `maxIdleConns` | int | No | `5` | Maximum number of idle connections in the pool | -| `connMaxLifetime` | string | No | `5m` | Maximum lifetime of a connection (e.g., "1h", "30m") | +| Field | Type | Required | Default | Description | +| ----------------- | ------ | -------- | --------- | -------------------------------------------------------------------------------------------------- | +| `host` | string | Yes | - | Database server hostname or IP address | +| `port` | int | Yes | - | Database server port | +| `user` | string | Yes | - | Database username for normal operations | +| `migrationUser` | string | No | `user` | Database username for running migrations (should have elevated privileges) | +| `database` | string | Yes | - | Database name | +| `sslMode` | string | No | `require` | SSL mode (`disable`, `require`, `verify-ca`, `verify-full`) | +| `maxOpenConns` | int | No | `25` | Maximum number of open connections to the database | +| `maxIdleConns` | int | No | `5` | Maximum number of idle connections in the pool | +| `connMaxLifetime` | string | No | `5m` | Maximum lifetime of a connection (e.g., "1h", "30m") | +| `maxMetaSize` | int | No | `65536` | Maximum allowed size in bytes for publisher-provided metadata extensions | +| `dynamicAuth` | object | No | - | Dynamic authentication configuration (see [Dynamic authentication](#dynamic-authentication) below) | \* Password configuration is required but has multiple sources (see Password -Security below) +Security and Dynamic authentication below) ## Password security @@ -119,6 +122,43 @@ for more details. You can find more details about user creation and initial configuration in this [test file](https://github.com/stacklok/toolhive-registry-server/blob/301ccf4e3ad13daad28be7b669d8e5fca337ec14/cmd/thv-registry-api/app/serve_test.go#L56-L103). +## Dynamic authentication + +As an alternative to pgpass files, you can use dynamic authentication to +generate database credentials at runtime. This is useful for cloud-hosted +databases that support IAM-based authentication. + +### AWS RDS IAM authentication + +When running on AWS, you can authenticate to RDS using IAM credentials instead +of static passwords. The server generates short-lived authentication tokens +using the IAM role attached to the workload. + +```yaml title="config-aws-rds.yaml" +database: + host: my-database.123456789.us-east-1.rds.amazonaws.com + port: 5432 + user: my_app_user + database: registry + sslMode: require + dynamicAuth: + awsRdsIam: + region: us-east-1 +``` + +**Configuration options:** + +- `dynamicAuth.awsRdsIam.region` (required): The AWS region of the RDS instance. + Set to `detect` to automatically determine the region from the EC2 instance + metadata service (IMDS). + +:::note + +Dynamic authentication replaces pgpass files. The server generates +authentication tokens automatically before each connection. + +::: + ## Database migrations The server uses database migrations to manage schema changes. Migrations run diff --git a/docs/toolhive/guides-registry/deployment.mdx b/docs/toolhive/guides-registry/deployment.mdx index 7b73cc2c..a72add50 100644 --- a/docs/toolhive/guides-registry/deployment.mdx +++ b/docs/toolhive/guides-registry/deployment.mdx @@ -11,10 +11,6 @@ development to production Kubernetes clusters. The Registry server is designed to run as an independent deployment, possibly alongside the ToolHive Operator. -Although it is possible to run ToolHive Registry to use an in-memory store, it -is unreliable to run multiple replicas as they would not share state, and you -should run it with a proper Postgres database. - ### ToolHive Operator ToolHive Operator supports deploying the Registry server. See @@ -189,17 +185,31 @@ of workloads. The types being watched are [`MCPRemoteProxy`](../guides-k8s/remote-mcp-proxy.mdx), and [`VirtualMCPServer`](../guides-vmcp/configuration.mdx). -:::note +By default, the Registry Server discovers resources in all namespaces +(cluster-wide). You can restrict discovery to specific namespaces by setting the +`THV_REGISTRY_WATCH_NAMESPACE` environment variable to a comma-separated list of +namespace names in your deployment: -Currently, only resources in the same namespace as the Registry server are -discovered. +```yaml +env: + - name: THV_REGISTRY_WATCH_NAMESPACE + value: toolhive-system,production +``` -::: +When `THV_REGISTRY_WATCH_NAMESPACE` is set, only resources in the specified +namespaces are discovered. When unset, the server watches all namespaces. -This feature requires the Registry server to be granted access to those -resources. +Both RBAC options below use the same ClusterRole for workload discovery and a +separate namespace-scoped Role for leader election. The difference is how the +ClusterRole is bound. -```yaml title="registry-service-account.yaml" +### Cluster-wide discovery (default) + +When `THV_REGISTRY_WATCH_NAMESPACE` is not set, the server watches all +namespaces and requires a ClusterRole and ClusterRoleBinding: + +{/* prettier-ignore */} +```yaml title="registry-rbac-cluster.yaml" apiVersion: v1 kind: ServiceAccount metadata: @@ -208,13 +218,13 @@ metadata: name: registry-api namespace: toolhive-system --- +# Manager role for workload discovery (ToolHive CRDs + services) apiVersion: rbac.authorization.k8s.io/v1 -kind: Role +kind: ClusterRole metadata: labels: toolhive.stacklok.io/registry-name: registry-api - name: registry-api - namespace: toolhive-system + name: registry-api-manager rules: - apiGroups: - toolhive.stacklok.dev @@ -234,6 +244,16 @@ rules: - get - list - watch +--- +# Leader election role (namespace-scoped, always required) +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + toolhive.stacklok.io/registry-name: registry-api + name: registry-api-leader-election + namespace: toolhive-system +rules: - apiGroups: - '' resources: @@ -266,23 +286,71 @@ rules: - create - patch --- +# Leader election binding (always namespace-scoped) apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: labels: toolhive.stacklok.io/registry-name: registry-api - name: registry-api + name: registry-api-leader-election namespace: toolhive-system roleRef: apiGroup: rbac.authorization.k8s.io kind: Role - name: registry-api + name: registry-api-leader-election +subjects: + - kind: ServiceAccount + name: registry-api + namespace: toolhive-system +--- +# Cluster-wide binding for the manager role +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + toolhive.stacklok.io/registry-name: registry-api + name: registry-api-manager +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: registry-api-manager +subjects: + - kind: ServiceAccount + name: registry-api + namespace: toolhive-system +``` + +### Namespace-scoped discovery + +When `THV_REGISTRY_WATCH_NAMESPACE` is set, use the same ClusterRole but bind it +with a RoleBinding in each watched namespace instead of a ClusterRoleBinding. +Create one RoleBinding per namespace: + +{/* prettier-ignore */} +```yaml title="registry-rbac-namespace.yaml" +# Use the same ServiceAccount, ClusterRole, and leader election +# Role/RoleBinding from the cluster-wide example above. +# Replace the ClusterRoleBinding with one RoleBinding per namespace: +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + toolhive.stacklok.io/registry-name: registry-api + name: registry-api-manager + namespace: toolhive-system # repeat for each watched namespace +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: registry-api-manager subjects: - kind: ServiceAccount name: registry-api namespace: toolhive-system ``` +### Applying the service account + Apply the service account to the registry server deployment in the `spec.template.spec` section: @@ -292,3 +360,12 @@ spec: spec: serviceAccountName: registry-api ``` + +:::tip + +If you run multiple Registry Server instances in the same namespace, set the +`THV_REGISTRY_LEADER_ELECTION_ID` environment variable to a unique value for +each instance to avoid leader election lease conflicts. The Helm chart handles +this automatically. + +::: From 2d2761bcd42a6f40a40d375dfe0e5753170a1900 Mon Sep 17 00:00:00 2001 From: Radoslav Dimitrov Date: Fri, 13 Feb 2026 00:56:32 +0200 Subject: [PATCH 2/3] Address the items flagged by the docs-review skill Signed-off-by: Radoslav Dimitrov --- docs/toolhive/guides-registry/configuration.mdx | 4 ++-- docs/toolhive/guides-registry/database.mdx | 5 ++--- docs/toolhive/guides-registry/deployment.mdx | 7 +++---- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/docs/toolhive/guides-registry/configuration.mdx b/docs/toolhive/guides-registry/configuration.mdx index 736db019..b460e51b 100644 --- a/docs/toolhive/guides-registry/configuration.mdx +++ b/docs/toolhive/guides-registry/configuration.mdx @@ -231,10 +231,10 @@ Server standalone (without the operator). ::: -By default, the Registry Server discovers resources in all namespaces +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](./deployment.mdx#workload-discovery) for configuration +[Workload discovery](./deployment.mdx#workload-discovery) for configuration details and RBAC requirements. ```yaml title="config-kubernetes.yaml" diff --git a/docs/toolhive/guides-registry/database.mdx b/docs/toolhive/guides-registry/database.mdx index 54c621df..f9732ad0 100644 --- a/docs/toolhive/guides-registry/database.mdx +++ b/docs/toolhive/guides-registry/database.mdx @@ -124,9 +124,8 @@ You can find more details about user creation and initial configuration in this ## Dynamic authentication -As an alternative to pgpass files, you can use dynamic authentication to -generate database credentials at runtime. This is useful for cloud-hosted -databases that support IAM-based authentication. +As an alternative to pgpass files, the server supports dynamic credential +generation for cloud-hosted databases. ### AWS RDS IAM authentication diff --git a/docs/toolhive/guides-registry/deployment.mdx b/docs/toolhive/guides-registry/deployment.mdx index a72add50..7242331e 100644 --- a/docs/toolhive/guides-registry/deployment.mdx +++ b/docs/toolhive/guides-registry/deployment.mdx @@ -177,7 +177,7 @@ Apply the deployment: kubectl apply -f deployment.yaml ``` -## Workload Discovery +## Workload discovery Kubernetes workload discovery works by looking for annotations in a specific set of workloads. The types being watched are @@ -185,7 +185,7 @@ of workloads. The types being watched are [`MCPRemoteProxy`](../guides-k8s/remote-mcp-proxy.mdx), and [`VirtualMCPServer`](../guides-vmcp/configuration.mdx). -By default, the Registry Server discovers resources in all namespaces +By default, the Registry server discovers resources in all namespaces (cluster-wide). You can restrict discovery to specific namespaces by setting the `THV_REGISTRY_WATCH_NAMESPACE` environment variable to a comma-separated list of namespace names in your deployment: @@ -205,8 +205,7 @@ ClusterRole is bound. ### Cluster-wide discovery (default) -When `THV_REGISTRY_WATCH_NAMESPACE` is not set, the server watches all -namespaces and requires a ClusterRole and ClusterRoleBinding: +For cluster-wide discovery, apply the following resources: {/* prettier-ignore */} ```yaml title="registry-rbac-cluster.yaml" From a57af24514c1bec634c95c916347de136c8413a6 Mon Sep 17 00:00:00 2001 From: Radoslav Dimitrov Date: Fri, 13 Feb 2026 01:12:12 +0200 Subject: [PATCH 3/3] Remove implications that the db is optional Signed-off-by: Radoslav Dimitrov --- docs/toolhive/guides-k8s/deploy-registry.mdx | 5 ++--- docs/toolhive/guides-registry/intro.mdx | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/toolhive/guides-k8s/deploy-registry.mdx b/docs/toolhive/guides-k8s/deploy-registry.mdx index 80acc729..fdb3946c 100644 --- a/docs/toolhive/guides-k8s/deploy-registry.mdx +++ b/docs/toolhive/guides-k8s/deploy-registry.mdx @@ -13,7 +13,7 @@ description: with your cluster - The ToolHive operator installed in your cluster (see [Deploy the operator](./deploy-operator.mdx)) -- A PostgreSQL database (recommended for production deployments) +- A PostgreSQL database ## Overview @@ -281,8 +281,7 @@ spec: ## Configure database storage -For production deployments, configure PostgreSQL database storage for -persistence across restarts. +Configure PostgreSQL database storage for the Registry server. ```yaml {17-32} title="registry-with-database.yaml" apiVersion: v1 diff --git a/docs/toolhive/guides-registry/intro.mdx b/docs/toolhive/guides-registry/intro.mdx index e0a13e58..22197eae 100644 --- a/docs/toolhive/guides-registry/intro.mdx +++ b/docs/toolhive/guides-registry/intro.mdx @@ -14,7 +14,7 @@ The Registry server aggregates MCP server metadata from various sources and exposes it through a standardized API. When you start the server, it: 1. Loads configuration from a YAML file -2. Runs database migrations automatically (if database is configured) +2. Runs database migrations automatically 3. Immediately fetches registry data from the configured sources 4. Starts background sync coordinator for automatic updates (for synced registries)