@@ -18,8 +18,9 @@ description:
1818## Overview
1919
2020The ToolHive operator deploys the Registry server in Kubernetes by creating
21- ` MCPRegistry ` resources. This is the recommended method for deploying the
22- ToolHive Registry Server in Kubernetes environments.
21+ ` MCPRegistry ` resources. Alternatively, you can deploy the Registry Server
22+ manually by following the
23+ [ manual deployment instructions] ( ../guides-registry/deployment.mdx#manual-deployment ) .
2324
2425### High-level architecture
2526
@@ -53,11 +54,8 @@ flowchart LR
5354
5455## Create a registry
5556
56- You can create ` MCPRegistry ` resources in namespaces based on how the operator
57- was deployed.
58-
59- - ** Cluster mode (default)** : Create MCPRegistry resources in any namespace
60- - ** Namespace mode** : Create MCPRegistry resources only in allowed namespaces
57+ You can create ` MCPRegistry ` resources in the namespaces where the ToolHive
58+ Operator is deployed.
6159
6260See [ Deploy the operator] ( ./deploy-operator-helm.mdx#operator-deployment-modes )
6361to learn about the different deployment modes.
@@ -74,6 +72,8 @@ metadata:
7472 namespace : my-namespace # Update with your namespace
7573spec :
7674 displayName : My MCP Registry
75+ auth :
76+ mode : anonymous
7777 registries :
7878 - name : toolhive
7979 format : toolhive
@@ -115,12 +115,14 @@ registry configuration.
115115
116116Clone and sync from Git repositories. Ideal for version-controlled registries.
117117
118- ``` yaml {7-12 } title="registry-git.yaml"
118+ ``` yaml {9-14 } title="registry-git.yaml"
119119apiVersion : toolhive.stacklok.dev/v1alpha1
120120kind : MCPRegistry
121121metadata :
122122 name : git-registry
123123spec :
124+ auth :
125+ mode : anonymous
124126 registries :
125127 - name : toolhive
126128 format : toolhive
@@ -161,6 +163,8 @@ kind: MCPRegistry
161163metadata:
162164 name: configmap-registry
163165spec:
166+ auth:
167+ mode: anonymous
164168 registries:
165169 - name: local
166170 format: upstream
@@ -184,6 +188,8 @@ kind: MCPRegistry
184188metadata:
185189 name: pvc-registry
186190spec:
191+ auth:
192+ mode: anonymous
187193 registries:
188194 - name: shared
189195 format: upstream
@@ -214,6 +220,8 @@ kind: MCPRegistry
214220metadata:
215221 name: api-registry
216222spec:
223+ auth:
224+ mode: anonymous
217225 registries:
218226 - name: upstream
219227 format: upstream
@@ -247,6 +255,8 @@ kind: MCPRegistry
247255metadata:
248256 name: filtered-registry
249257spec:
258+ auth:
259+ mode: anonymous
250260 registries:
251261 - name: toolhive
252262 format: toolhive
@@ -274,17 +284,34 @@ spec:
274284For production deployments, configure PostgreSQL database storage for
275285persistence across restarts.
276286
277- ` ` ` yaml {6-15} title="registry-with-database.yaml"
287+ ` ` ` yaml {17-32} title="registry-with-database.yaml"
288+ apiVersion: v1
289+ kind: Secret
290+ metadata:
291+ name: registry-api-db-passwords
292+ type: Opaque
293+ stringData:
294+ db-password: app_password
295+ migration-password: migrator_password
296+ ---
278297apiVersion: toolhive.stacklok.dev/v1alpha1
279298kind: MCPRegistry
280299metadata:
281300 name: production-registry
282301spec:
302+ auth:
303+ mode: anonymous
283304 databaseConfig:
284305 host: postgres.database.svc.cluster.local
285306 port: 5432
286307 user: db_app
287308 migrationUser: db_migrator
309+ dbAppUserPasswordSecretRef:
310+ name: registry-api-db-passwords
311+ key: db-password
312+ dbMigrationUserPasswordSecretRef:
313+ name: registry-api-db-passwords
314+ key: migration-password
288315 database: registry
289316 sslMode: verify-full
290317 maxOpenConns: 25
@@ -303,26 +330,201 @@ spec:
303330
304331**Database configuration fields:**
305332
306- | Field | Default | Description |
307- | ----------------- | ------------- | ------------------------------------------------- |
308- | `host` | `postgres` | Database server hostname |
309- | `port` | `5432` | Database server port |
310- | `user` | `db_app` | Application user (SELECT, INSERT, UPDATE, DELETE) |
311- | `migrationUser` | `db_migrator` | Migration user (CREATE, ALTER, DROP) |
312- | `database` | `registry` | Database name |
313- | `sslMode` | `prefer` | SSL mode (disable, prefer, require, verify-full) |
314- | `maxOpenConns` | `10` | Maximum open connections |
315- | `maxIdleConns` | `2` | Maximum idle connections |
316- | `connMaxLifetime` | `30m` | Maximum connection lifetime |
333+ | Field | Default | Description |
334+ | ---------------------------------- | ------------- | ------------------------------------------------- |
335+ | `host` | `postgres` | Database server hostname |
336+ | `port` | `5432` | Database server port |
337+ | `user` | `db_app` | Application user (SELECT, INSERT, UPDATE, DELETE) |
338+ | `migrationUser` | `db_migrator` | Migration user (CREATE, ALTER, DROP) |
339+ | `dbAppUserPasswordSecretRef` | `` | Password of application user |
340+ | `dbMigrationUserPasswordSecretRef` | `` | Password of migration user |
341+ | `database` | `registry` | Database name |
342+ | `sslMode` | `prefer` | SSL mode (disable, prefer, require, verify-full) |
343+ | `maxOpenConns` | `10` | Maximum open connections |
344+ | `maxIdleConns` | `2` | Maximum idle connections |
345+ | `connMaxLifetime` | `30m` | Maximum connection lifetime |
317346
318347:::tip
319348
320- Provide database passwords using a pgpass file mounted as a secret. See the
321- [Database configuration](../guides-registry/database.mdx) guide for details on
322- setting up password security.
349+ Credentials are internally configured using a pgpass file mounted as a secret.
350+
351+ :: :
352+
353+ # # Configure authentication
354+
355+ You can configure authentication using the `authConfig` field in your
356+ ` MCPRegistry` resource.
357+
358+ # ## Authentication modes
359+
360+ | Mode | Description | Use case |
361+ | ----------- | ----------------------------------------------- | ---------------------------- |
362+ | `oauth` | Validates access tokens from identity providers | Production deployments |
363+ | `anonymous` | No authentication required | Development and testing only |
364+
365+ :::info[Secure by default]
366+
367+ Configuring an authentication mode is mandatory, if you're not interested you
368+ can set it to `anonymous`.
369+
370+ :: :
371+
372+ # ## OAuth authentication
373+
374+ OAuth mode validates JWT tokens from one or more identity providers. Configure
375+ providers in the `authConfig.oauth.providers` array.
376+
377+ ` ` ` yaml title="registry-oauth.yaml"
378+ apiVersion: toolhive.stacklok.dev/v1alpha1
379+ kind: MCPRegistry
380+ metadata:
381+ name: registry
382+ namespace: toolhive-system
383+ spec:
384+ displayName: 'Authenticated MCP Server Registry'
385+ authConfig:
386+ mode: oauth
387+ oauth:
388+ providers:
389+ - name: kubernetes
390+ issuerUrl: https://kubernetes.default.svc.cluster.local
391+ jwksUrl: https://kubernetes.default.svc/openid/v1/jwks
392+ audience: registry-server
393+ caCertPath: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
394+ authTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
395+ allowPrivateIP: true
396+ registries:
397+ - name: toolhive
398+ format: toolhive
399+ git:
400+ repository: https://github.com/stacklok/toolhive.git
401+ branch: main
402+ path: pkg/registry/data/registry.json
403+ ` ` `
404+
405+ # ## OAuth configuration fields
406+
407+ | Field | Required | Default | Description |
408+ | ----------------- | -------- | ----------------------------------------- | -------------------------------------------------- |
409+ | `mode` | No | `oauth` | Authentication mode (`oauth` or `anonymous`) |
410+ | `resourceUrl` | No | - | URL identifying this protected resource (RFC 9728) |
411+ | `realm` | No | `mcp-registry` | Protection space identifier for WWW-Authenticate |
412+ | `scopesSupported` | No | `[mcp-registry:read, mcp-registry:write]` | OAuth scopes supported by this resource |
413+
414+ # ## Provider configuration fields
415+
416+ | Field | Required | Description |
417+ | ------------------ | -------- | ----------------------------------------------------------------------- |
418+ | `name` | Yes | Unique identifier for this provider (for logging and monitoring) |
419+ | `issuerUrl` | Yes | OIDC issuer URL (e.g., `https://accounts.google.com`) |
420+ | `audience` | Yes | Expected audience claim in the access token |
421+ | `jwksUrl` | No | JWKS endpoint URL (skips OIDC discovery if specified) |
422+ | `clientId` | No | OAuth client ID for token introspection |
423+ | `clientSecretFile` | No | Path to file containing the client secret |
424+ | `caCertPath` | No | Path to CA certificate for TLS verification |
425+ | `authTokenFile` | No | Path to token file for authenticating to OIDC/JWKS endpoints |
426+ | `introspectionUrl` | No | Token introspection endpoint URL for opaque token validation (RFC 7662) |
427+ | `allowPrivateIP` | No | Allow connections to private IP addresses (required for in-cluster) |
428+
429+ # ## Kubernetes service account authentication
430+
431+ For in-cluster deployments, you can configure OAuth to validate Kubernetes
432+ service account tokens :
433+
434+ ` ` ` yaml title="registry-k8s-auth.yaml"
435+ apiVersion: toolhive.stacklok.dev/v1alpha1
436+ kind: MCPRegistry
437+ metadata:
438+ name: registry
439+ spec:
440+ authConfig:
441+ mode: oauth
442+ oauth:
443+ providers:
444+ - name: kubernetes
445+ issuerUrl: https://kubernetes.default.svc.cluster.local
446+ jwksUrl: https://kubernetes.default.svc/openid/v1/jwks
447+ audience: registry-server
448+ caCertPath: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
449+ authTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
450+ allowPrivateIP: true
451+ ` ` `
452+
453+ :::tip[Kubernetes provider settings]
454+
455+ - **issuerUrl**: for most Kubernetes distributions,
456+ ` https://kubernetes.default.svc.cluster.local` is the correct value to match
457+ the `iss` claim in Kubernetes service account tokens.
458+ - **jwksUrl**: Specify directly to skip OIDC discovery (the Kubernetes API
459+ server doesn't support standard discovery).
460+ - **allowPrivateIP**: Required for in-cluster communication with the API server.
461+
462+ :: :
463+
464+ # ## Multiple providers
465+
466+ You can configure multiple OAuth providers to accept tokens from different
467+ identity sources :
468+
469+ ` ` ` yaml title="registry-multi-provider.yaml"
470+ apiVersion: toolhive.stacklok.dev/v1alpha1
471+ kind: MCPRegistry
472+ metadata:
473+ name: registry
474+ spec:
475+ authConfig:
476+ mode: oauth
477+ oauth:
478+ providers:
479+ # Kubernetes service accounts (in-cluster workloads)
480+ - name: kubernetes
481+ issuerUrl: https://kubernetes.default.svc.cluster.local
482+ jwksUrl: https://kubernetes.default.svc/openid/v1/jwks
483+ audience: registry-server
484+ caCertPath: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
485+ authTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
486+ allowPrivateIP: true
487+ # External identity provider
488+ - name: okta
489+ issuerUrl: https://YOUR_DOMAIN.okta.com/oauth2/default
490+ audience: registry
491+ ` ` `
492+
493+ The server validates tokens against each provider in order until one succeeds.
494+
495+ # ## Anonymous authentication
496+
497+ For development and testing, you can disable authentication entirely :
498+
499+ ` ` ` yaml title="registry-anonymous.yaml"
500+ apiVersion: toolhive.stacklok.dev/v1alpha1
501+ kind: MCPRegistry
502+ metadata:
503+ name: registry
504+ spec:
505+ authConfig:
506+ mode: anonymous
507+ registries:
508+ - name: toolhive
509+ format: toolhive
510+ git:
511+ repository: https://github.com/stacklok/toolhive.git
512+ branch: main
513+ path: pkg/registry/data/registry.json
514+ ` ` `
515+
516+ :::danger[No access control]
517+
518+ Anonymous mode provides **no access control**. Only use it in trusted
519+ environments or when other security measures are in place. **Do not use
520+ anonymous mode in production.**
323521
324522:: :
325523
524+ For detailed information about authentication configuration, including
525+ provider-specific examples for Keycloak, Auth0, Azure AD, and Okta, see the
526+ [Authentication configuration](../guides-registry/authentication.mdx) guide.
527+
326528# # Customize the Registry server pod
327529
328530You can customize the Registry server pod using the `podTemplateSpec` field.
@@ -345,6 +547,8 @@ spec:
345547 requests:
346548 cpu: '100m'
347549 memory: '128Mi'
550+ auth:
551+ mode: anonymous
348552 registries:
349553 - name: toolhive
350554 format: toolhive
@@ -360,7 +564,10 @@ spec:
360564
361565When customizing containers in `podTemplateSpec`, you must use
362566`name : registry-api` for the main container to ensure the operator can properly
363- manage the Registry server.
567+ manage the Registry server. The container name is hardcoded to avoid conflict
568+ issues with user provided containers. Mandating a container name on the Operator
569+ side explicitly tells the Operator it is the main registry server container and
570+ any other containers provided by the use are sidecars/init containers.
364571
365572:: :
366573
0 commit comments