From 89be7ca02cb3902ffa3a480228983a8d0ac5be04 Mon Sep 17 00:00:00 2001 From: amirejaz Date: Tue, 25 Nov 2025 14:42:27 +0000 Subject: [PATCH 01/14] add option to skip Virtual MCP CRDs installation --- deploy/charts/operator-crds/Chart.yaml | 2 +- deploy/charts/operator-crds/README.md | 21 ++++++++++++++++++- ...ev_virtualmcpcompositetooldefinitions.yaml | 0 ...olhive.stacklok.dev_virtualmcpservers.yaml | 0 ...irtualmcpcompositetooldefinitions-crd.yaml | 4 ++++ .../templates/virtualmcpservers-crd.yaml | 4 ++++ deploy/charts/operator-crds/values.yaml | 8 ++++++- 7 files changed, 36 insertions(+), 3 deletions(-) rename deploy/charts/operator-crds/{crds => crds-virtualmcp}/toolhive.stacklok.dev_virtualmcpcompositetooldefinitions.yaml (100%) rename deploy/charts/operator-crds/{crds => crds-virtualmcp}/toolhive.stacklok.dev_virtualmcpservers.yaml (100%) create mode 100644 deploy/charts/operator-crds/templates/virtualmcpcompositetooldefinitions-crd.yaml create mode 100644 deploy/charts/operator-crds/templates/virtualmcpservers-crd.yaml diff --git a/deploy/charts/operator-crds/Chart.yaml b/deploy/charts/operator-crds/Chart.yaml index 0ef1923b6..85b12b756 100644 --- a/deploy/charts/operator-crds/Chart.yaml +++ b/deploy/charts/operator-crds/Chart.yaml @@ -2,5 +2,5 @@ apiVersion: v2 name: toolhive-operator-crds description: A Helm chart for installing the ToolHive Operator CRDs into Kubernetes. type: application -version: 0.0.62 +version: 0.0.63 appVersion: "0.0.1" diff --git a/deploy/charts/operator-crds/README.md b/deploy/charts/operator-crds/README.md index b4c157d28..58dd86be4 100644 --- a/deploy/charts/operator-crds/README.md +++ b/deploy/charts/operator-crds/README.md @@ -1,6 +1,6 @@ # ToolHive Operator CRDs Helm Chart -![Version: 0.0.62](https://img.shields.io/badge/Version-0.0.62-informational?style=flat-square) +![Version: 0.0.63](https://img.shields.io/badge/Version-0.0.63-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for installing the ToolHive Operator CRDs into Kubernetes. @@ -40,3 +40,22 @@ To uninstall/delete the `toolhive-operator-crds` deployment: helm uninstall ``` +### Configuration + +#### Skipping Virtual MCP CRDs + +By default, all CRDs including Virtual MCP CRDs (VirtualMCPServer and VirtualMCPCompositeToolDefinition) are installed. Users who only need core MCP server management can skip installing Virtual MCP aggregation features to reduce the chart size: + +```shell +helm upgrade -i toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds \ + --set crds.install.virtualMCP=false +``` + +This saves approximately 54KB of CRDs and is useful for deployments that don't require Virtual MCP aggregation features. + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| crds.install.virtualMCP | bool | `true` | Install Virtual MCP CRDs (VirtualMCPServer and VirtualMCPCompositeToolDefinition). Set to `false` to skip Virtual MCP aggregation features if you only need core MCP server management. | + diff --git a/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_virtualmcpcompositetooldefinitions.yaml b/deploy/charts/operator-crds/crds-virtualmcp/toolhive.stacklok.dev_virtualmcpcompositetooldefinitions.yaml similarity index 100% rename from deploy/charts/operator-crds/crds/toolhive.stacklok.dev_virtualmcpcompositetooldefinitions.yaml rename to deploy/charts/operator-crds/crds-virtualmcp/toolhive.stacklok.dev_virtualmcpcompositetooldefinitions.yaml diff --git a/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_virtualmcpservers.yaml b/deploy/charts/operator-crds/crds-virtualmcp/toolhive.stacklok.dev_virtualmcpservers.yaml similarity index 100% rename from deploy/charts/operator-crds/crds/toolhive.stacklok.dev_virtualmcpservers.yaml rename to deploy/charts/operator-crds/crds-virtualmcp/toolhive.stacklok.dev_virtualmcpservers.yaml diff --git a/deploy/charts/operator-crds/templates/virtualmcpcompositetooldefinitions-crd.yaml b/deploy/charts/operator-crds/templates/virtualmcpcompositetooldefinitions-crd.yaml new file mode 100644 index 000000000..0470e63c1 --- /dev/null +++ b/deploy/charts/operator-crds/templates/virtualmcpcompositetooldefinitions-crd.yaml @@ -0,0 +1,4 @@ +{{- if .Values.crds.install.virtualMCP }} +{{- $crd := .Files.Get "crds-virtualmcp/toolhive.stacklok.dev_virtualmcpcompositetooldefinitions.yaml" | fromYaml }} +{{- toYaml $crd | nindent 0 }} +{{- end }} diff --git a/deploy/charts/operator-crds/templates/virtualmcpservers-crd.yaml b/deploy/charts/operator-crds/templates/virtualmcpservers-crd.yaml new file mode 100644 index 000000000..8f831ba79 --- /dev/null +++ b/deploy/charts/operator-crds/templates/virtualmcpservers-crd.yaml @@ -0,0 +1,4 @@ +{{- if .Values.crds.install.virtualMCP }} +{{- $crd := .Files.Get "crds-virtualmcp/toolhive.stacklok.dev_virtualmcpservers.yaml" | fromYaml }} +{{- toYaml $crd | nindent 0 }} +{{- end }} diff --git a/deploy/charts/operator-crds/values.yaml b/deploy/charts/operator-crds/values.yaml index 804d4fc18..53b286b41 100644 --- a/deploy/charts/operator-crds/values.yaml +++ b/deploy/charts/operator-crds/values.yaml @@ -1 +1,7 @@ -# empty values file as we do not need any values for the CRDs at the moment +# Configuration for CRD installation +crds: + install: + # -- Install Virtual MCP CRDs (VirtualMCPServer and VirtualMCPCompositeToolDefinition) + # Users who only need core MCP server management can set this to false to skip + # installing Virtual MCP aggregation features (saves ~54KB of CRDs) + virtualMCP: true From 53d40ea31ac461c584ecf65277b7570655078865 Mon Sep 17 00:00:00 2001 From: amirejaz Date: Tue, 25 Nov 2025 14:59:35 +0000 Subject: [PATCH 02/14] fix helm charts linting --- deploy/charts/operator-crds/README.md | 18 ++---------------- deploy/charts/operator-crds/values.yaml | 4 ++-- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/deploy/charts/operator-crds/README.md b/deploy/charts/operator-crds/README.md index 58dd86be4..52180a59d 100644 --- a/deploy/charts/operator-crds/README.md +++ b/deploy/charts/operator-crds/README.md @@ -40,22 +40,8 @@ To uninstall/delete the `toolhive-operator-crds` deployment: helm uninstall ``` -### Configuration - -#### Skipping Virtual MCP CRDs - -By default, all CRDs including Virtual MCP CRDs (VirtualMCPServer and VirtualMCPCompositeToolDefinition) are installed. Users who only need core MCP server management can skip installing Virtual MCP aggregation features to reduce the chart size: - -```shell -helm upgrade -i toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds \ - --set crds.install.virtualMCP=false -``` - -This saves approximately 54KB of CRDs and is useful for deployments that don't require Virtual MCP aggregation features. - ## Values | Key | Type | Default | Description | -|-----|------|---------|-------------| -| crds.install.virtualMCP | bool | `true` | Install Virtual MCP CRDs (VirtualMCPServer and VirtualMCPCompositeToolDefinition). Set to `false` to skip Virtual MCP aggregation features if you only need core MCP server management. | - +|-----|-------------|------|---------| +| crds.install.virtualMCP | bool | `true` | Install Virtual MCP CRDs (VirtualMCPServer and VirtualMCPCompositeToolDefinition). Users who only need core MCP server management can set this to false to skip installing Virtual MCP aggregation features (saves ~54KB of CRDs). | diff --git a/deploy/charts/operator-crds/values.yaml b/deploy/charts/operator-crds/values.yaml index 53b286b41..9d0d49951 100644 --- a/deploy/charts/operator-crds/values.yaml +++ b/deploy/charts/operator-crds/values.yaml @@ -1,7 +1,7 @@ # Configuration for CRD installation crds: install: - # -- Install Virtual MCP CRDs (VirtualMCPServer and VirtualMCPCompositeToolDefinition) + # -- Install Virtual MCP CRDs (VirtualMCPServer and VirtualMCPCompositeToolDefinition). # Users who only need core MCP server management can set this to false to skip - # installing Virtual MCP aggregation features (saves ~54KB of CRDs) + # installing Virtual MCP aggregation features (saves ~54KB of CRDs). virtualMCP: true From dc8a3c9d9cdb33bb200fb24cd08148ee851bea8e Mon Sep 17 00:00:00 2001 From: amirejaz Date: Tue, 25 Nov 2025 17:19:09 +0000 Subject: [PATCH 03/14] adds feature flags for controllers --- cmd/thv-operator/main.go | 86 ++++++++++++++++++-- deploy/charts/operator-crds/README.md | 36 ++++++++ deploy/charts/operator-crds/README.md.gotmpl | 39 ++++++++- deploy/charts/operator/README.md | 3 +- deploy/charts/operator/values.yaml | 5 ++ 5 files changed, 157 insertions(+), 12 deletions(-) diff --git a/cmd/thv-operator/main.go b/cmd/thv-operator/main.go index 831d64436..c78b89d0d 100644 --- a/cmd/thv-operator/main.go +++ b/cmd/thv-operator/main.go @@ -12,6 +12,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" + // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) // to ensure that exec-entrypoint and run can make use of them. _ "k8s.io/client-go/plugin/pkg/client/auth" @@ -111,7 +112,50 @@ func main() { // setupControllersAndWebhooks sets up all controllers and webhooks with the manager func setupControllersAndWebhooks(mgr ctrl.Manager) error { + // Check feature flags + enableServer := isFeatureEnabled("ENABLE_SERVER", true) + enableRegistry := isFeatureEnabled("ENABLE_REGISTRY", true) + enableAggregation := isFeatureEnabled("ENABLE_AGGREGATION", true) + + // Set up server-related controllers + if enableServer { + if err := setupServerControllers(mgr, enableRegistry); err != nil { + return err + } + } else { + setupLog.Info("ENABLE_SERVER is disabled, skipping server-related controllers") + } + + // Set up registry controller + if enableRegistry { + if err := setupRegistryController(mgr); err != nil { + return err + } + } else { + setupLog.Info("ENABLE_REGISTRY is disabled, skipping MCPRegistry controller") + } + + // Set up aggregation controllers and webhooks + if enableAggregation { + if !enableServer { + setupLog.Info("ENABLE_AGGREGATION requires ENABLE_SERVER to be enabled, skipping aggregation controllers") + } else { + if err := setupAggregationControllers(mgr); err != nil { + return err + } + } + } else { + setupLog.Info("ENABLE_AGGREGATION is disabled, skipping Virtual MCP controllers and webhooks") + } + + //+kubebuilder:scaffold:builder + return nil +} + +// setupServerControllers sets up server-related controllers (MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, ToolConfig) +func setupServerControllers(mgr ctrl.Manager, enableRegistry bool) error { // Set up field indexing for MCPServer.Spec.GroupRef + // This is needed for MCPGroup controller to find MCPServers that belong to a group if err := mgr.GetFieldIndexer().IndexField( context.Background(), &mcpv1alpha1.MCPServer{}, @@ -129,19 +173,28 @@ func setupControllersAndWebhooks(mgr ctrl.Manager) error { // Create a shared platform detector for all controllers sharedPlatformDetector := ctrlutil.NewSharedPlatformDetector() + + // Set image validation mode based on whether registry is enabled + // If ENABLE_REGISTRY is enabled, enforce registry-based image validation + // Otherwise, allow all images + imageValidation := validation.ImageValidationAlwaysAllow + if enableRegistry { + imageValidation = validation.ImageValidationRegistryEnforcing + } + + // Set up MCPServer controller rec := &controllers.MCPServerReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorderFor("mcpserver-controller"), PlatformDetector: sharedPlatformDetector, - ImageValidation: validation.ImageValidationAlwaysAllow, + ImageValidation: imageValidation, } - if err := rec.SetupWithManager(mgr); err != nil { return fmt.Errorf("unable to create controller MCPServer: %w", err) } - // Register MCPToolConfig controller + // Set up MCPToolConfig controller if err := (&controllers.ToolConfigReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), @@ -149,7 +202,7 @@ func setupControllersAndWebhooks(mgr ctrl.Manager) error { return fmt.Errorf("unable to create controller MCPToolConfig: %w", err) } - // Register MCPExternalAuthConfig controller + // Set up MCPExternalAuthConfig controller if err := (&controllers.MCPExternalAuthConfigReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), @@ -157,7 +210,7 @@ func setupControllersAndWebhooks(mgr ctrl.Manager) error { return fmt.Errorf("unable to create controller MCPExternalAuthConfig: %w", err) } - // Register MCPRemoteProxy controller + // Set up MCPRemoteProxy controller if err := (&controllers.MCPRemoteProxyReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), @@ -166,13 +219,20 @@ func setupControllersAndWebhooks(mgr ctrl.Manager) error { return fmt.Errorf("unable to create controller MCPRemoteProxy: %w", err) } - // Only register MCPRegistry controller if feature flag is enabled - rec.ImageValidation = validation.ImageValidationRegistryEnforcing + return nil +} +// setupRegistryController sets up the MCPRegistry controller +func setupRegistryController(mgr ctrl.Manager) error { if err := (controllers.NewMCPRegistryReconciler(mgr.GetClient(), mgr.GetScheme())).SetupWithManager(mgr); err != nil { return fmt.Errorf("unable to create controller MCPRegistry: %w", err) } + return nil +} +// setupAggregationControllers sets up aggregation-related controllers and webhooks +// (MCPGroup, VirtualMCPServer, and their webhooks) +func setupAggregationControllers(mgr ctrl.Manager) error { // Set up MCPGroup controller if err := (&controllers.MCPGroupReconciler{ Client: mgr.GetClient(), @@ -199,11 +259,21 @@ func setupControllersAndWebhooks(mgr ctrl.Manager) error { if err := (&mcpv1alpha1.VirtualMCPCompositeToolDefinition{}).SetupWebhookWithManager(mgr); err != nil { return fmt.Errorf("unable to create webhook VirtualMCPCompositeToolDefinition: %w", err) } - //+kubebuilder:scaffold:builder return nil } +// isFeatureEnabled checks if a feature flag environment variable is enabled. +// If the environment variable is not set, it returns the default value. +// The environment variable is considered enabled if it's set to "true" (case-insensitive). +func isFeatureEnabled(envVar string, defaultValue bool) bool { + value, found := os.LookupEnv(envVar) + if !found { + return defaultValue + } + return strings.EqualFold(value, "true") +} + // getDefaultNamespaces returns a map of namespaces to cache.Config for the operator to watch. // if WATCH_NAMESPACE is not set, returns nil which is defaulted to a cluster scope. func getDefaultNamespaces() map[string]cache.Config { diff --git a/deploy/charts/operator-crds/README.md b/deploy/charts/operator-crds/README.md index 52180a59d..db10772be 100644 --- a/deploy/charts/operator-crds/README.md +++ b/deploy/charts/operator-crds/README.md @@ -40,6 +40,42 @@ To uninstall/delete the `toolhive-operator-crds` deployment: helm uninstall ``` +### Skipping Virtual MCP CRDs + +By default, all CRDs including Virtual MCP CRDs (VirtualMCPServer and VirtualMCPCompositeToolDefinition) are installed. Users who only need core MCP server management can skip installing Virtual MCP aggregation features to reduce the chart size: + +```shell +helm upgrade -i toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds \ + --set crds.install.virtualMCP=false +``` + +**Important:** When Virtual MCP CRDs are not installed, you should also disable the Virtual MCP controllers in the operator to prevent errors. Set the `ENABLE_AGGREGATION` environment variable to `false` in the operator deployment: + +```shell +helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator \ + -n toolhive-system --create-namespace \ + --set operator.env.ENABLE_AGGREGATION=false +``` + +This saves approximately 54KB of CRDs and is useful for deployments that don't require Virtual MCP aggregation features. When `ENABLE_AGGREGATION=false`, the operator will skip setting up the VirtualMCPServer controller, MCPGroup controller, and their associated webhooks. + +### Feature Flags + +The operator supports the following feature flags (via `operator.env`) to enable/disable specific controller groups: + +- **`ENABLE_SERVER`** (default: `true`): Controls MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, and ToolConfig controllers +- **`ENABLE_REGISTRY`** (default: `true`): Controls MCPRegistry controller +- **`ENABLE_AGGREGATION`** (default: `true`): Controls VirtualMCPServer, MCPGroup controllers and their webhooks. Requires `ENABLE_SERVER=true`. + +Example: Disable all features except core server management: + +```shell +helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator \ + -n toolhive-system --create-namespace \ + --set operator.env.ENABLE_REGISTRY=false \ + --set operator.env.ENABLE_AGGREGATION=false +``` + ## Values | Key | Type | Default | Description | diff --git a/deploy/charts/operator-crds/README.md.gotmpl b/deploy/charts/operator-crds/README.md.gotmpl index d6d24d0b5..215f36083 100644 --- a/deploy/charts/operator-crds/README.md.gotmpl +++ b/deploy/charts/operator-crds/README.md.gotmpl @@ -48,7 +48,42 @@ To uninstall/delete the `toolhive-operator-crds` deployment: helm uninstall ``` -{{ template "chart.requirementsSection" . }} +### Skipping Virtual MCP CRDs + +By default, all CRDs including Virtual MCP CRDs (VirtualMCPServer and VirtualMCPCompositeToolDefinition) are installed. Users who only need core MCP server management can skip installing Virtual MCP aggregation features to reduce the chart size: + +```shell +helm upgrade -i toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds \ + --set crds.install.virtualMCP=false +``` + +**Important:** When Virtual MCP CRDs are not installed, you should also disable the Virtual MCP controllers in the operator to prevent errors. Set the `ENABLE_AGGREGATION` environment variable to `false` in the operator deployment: + +```shell +helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator \ + -n toolhive-system --create-namespace \ + --set operator.env.ENABLE_AGGREGATION=false +``` -{{ template "chart.valuesSection" . }} +This saves approximately 54KB of CRDs and is useful for deployments that don't require Virtual MCP aggregation features. When `ENABLE_AGGREGATION=false`, the operator will skip setting up the VirtualMCPServer controller, MCPGroup controller, and their associated webhooks. + +### Feature Flags + +The operator supports the following feature flags (via `operator.env`) to enable/disable specific controller groups: + +- **`ENABLE_SERVER`** (default: `true`): Controls MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, and ToolConfig controllers +- **`ENABLE_REGISTRY`** (default: `true`): Controls MCPRegistry controller +- **`ENABLE_AGGREGATION`** (default: `true`): Controls VirtualMCPServer, MCPGroup controllers and their webhooks. Requires `ENABLE_SERVER=true`. + +Example: Disable all features except core server management: + +```shell +helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator \ + -n toolhive-system --create-namespace \ + --set operator.env.ENABLE_REGISTRY=false \ + --set operator.env.ENABLE_AGGREGATION=false +``` + +{{ template "chart.requirementsSection" . }} +{{ template "chart.valuesSection" . }} \ No newline at end of file diff --git a/deploy/charts/operator/README.md b/deploy/charts/operator/README.md index 46f4cea10..a47fa0ad1 100644 --- a/deploy/charts/operator/README.md +++ b/deploy/charts/operator/README.md @@ -60,7 +60,7 @@ The command removes all the Kubernetes components associated with the chart and | operator.autoscaling.minReplicas | int | `1` | Minimum number of replicas | | operator.autoscaling.targetCPUUtilizationPercentage | int | `80` | Target CPU utilization percentage for autoscaling | | operator.containerSecurityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsNonRoot":true,"runAsUser":1000,"seccompProfile":{"type":"RuntimeDefault"}}` | Container security context settings for the operator | -| operator.env | object | `{}` | Environment variables to set in the operator container | +| operator.env | object | `{}` | Environment variables to set in the operator container Feature flags can be set here to enable/disable controller groups: - ENABLE_SERVER (default: true): Controls MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, and ToolConfig controllers - ENABLE_REGISTRY (default: true): Controls MCPRegistry controller - ENABLE_AGGREGATION (default: true): Controls VirtualMCPServer, MCPGroup controllers and webhooks (requires ENABLE_SERVER=true) Example: env: { ENABLE_AGGREGATION: "false", ENABLE_REGISTRY: "false" } | | operator.gc | object | `{"gogc":75,"gomeglimit":"150MiB"}` | Go memory limits and garbage collection percentage for the operator container | | operator.gc.gogc | int | `75` | Go garbage collection percentage for the operator container | | operator.gc.gomeglimit | string | `"150MiB"` | Go memory limits for the operator container | @@ -101,4 +101,3 @@ The command removes all the Kubernetes components associated with the chart and | registryAPI.serviceAccount.automountServiceAccountToken | bool | `true` | Automatically mount a ServiceAccount's API credentials | | registryAPI.serviceAccount.labels | object | `{}` | Labels to add to the registry API service account | | registryAPI.serviceAccount.name | string | `"toolhive-registry-api"` | The name of the service account to use for the registry API | - diff --git a/deploy/charts/operator/values.yaml b/deploy/charts/operator/values.yaml index d8264f6b7..ce7b6eb3f 100644 --- a/deploy/charts/operator/values.yaml +++ b/deploy/charts/operator/values.yaml @@ -25,6 +25,11 @@ operator: proxyHost: 0.0.0.0 # -- Environment variables to set in the operator container + # Feature flags can be set here to enable/disable controller groups: + # - ENABLE_SERVER (default: true): Controls MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, and ToolConfig controllers + # - ENABLE_REGISTRY (default: true): Controls MCPRegistry controller + # - ENABLE_AGGREGATION (default: true): Controls VirtualMCPServer, MCPGroup controllers and webhooks (requires ENABLE_SERVER=true) + # Example: env: { ENABLE_AGGREGATION: "false", ENABLE_REGISTRY: "false" } env: {} # -- List of ports to expose from the operator container From ce6aab5e00c296ac5e5bdf6fd2d121b8761f116b Mon Sep 17 00:00:00 2001 From: amirejaz Date: Tue, 25 Nov 2025 17:31:28 +0000 Subject: [PATCH 04/14] fix helm linting and test --- deploy/charts/operator/Chart.yaml | 2 +- deploy/charts/operator/README.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/deploy/charts/operator/Chart.yaml b/deploy/charts/operator/Chart.yaml index 602cfceb4..7be652a2d 100644 --- a/deploy/charts/operator/Chart.yaml +++ b/deploy/charts/operator/Chart.yaml @@ -2,5 +2,5 @@ apiVersion: v2 name: toolhive-operator description: A Helm chart for deploying the ToolHive Operator into Kubernetes. type: application -version: 0.5.3 +version: 0.5.4 appVersion: "v0.6.6" diff --git a/deploy/charts/operator/README.md b/deploy/charts/operator/README.md index a47fa0ad1..5214c9bc9 100644 --- a/deploy/charts/operator/README.md +++ b/deploy/charts/operator/README.md @@ -1,6 +1,6 @@ # ToolHive Operator Helm Chart -![Version: 0.5.3](https://img.shields.io/badge/Version-0.5.3-informational?style=flat-square) +![Version: 0.5.4](https://img.shields.io/badge/Version-0.5.4-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying the ToolHive Operator into Kubernetes. @@ -101,3 +101,4 @@ The command removes all the Kubernetes components associated with the chart and | registryAPI.serviceAccount.automountServiceAccountToken | bool | `true` | Automatically mount a ServiceAccount's API credentials | | registryAPI.serviceAccount.labels | object | `{}` | Labels to add to the registry API service account | | registryAPI.serviceAccount.name | string | `"toolhive-registry-api"` | The name of the service account to use for the registry API | + From 6892232954e3985013ef13b155b51614f11e34fb Mon Sep 17 00:00:00 2001 From: amirejaz Date: Tue, 25 Nov 2025 17:43:37 +0000 Subject: [PATCH 05/14] fix import linting --- cmd/thv-operator/main.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cmd/thv-operator/main.go b/cmd/thv-operator/main.go index c78b89d0d..15e05417e 100644 --- a/cmd/thv-operator/main.go +++ b/cmd/thv-operator/main.go @@ -9,12 +9,11 @@ import ( "os" "strings" + // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) + // to ensure that exec-entrypoint and run can make use of them. "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" - - // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) - // to ensure that exec-entrypoint and run can make use of them. _ "k8s.io/client-go/plugin/pkg/client/auth" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/cache" From c358ab728a8b2b0c4d261d993047108c8e9e4776 Mon Sep 17 00:00:00 2001 From: amirejaz Date: Tue, 25 Nov 2025 23:21:38 +0000 Subject: [PATCH 06/14] load the new crds-virtualmcp crds --- .../test-integration/virtualmcp/suite_test.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/cmd/thv-operator/test-integration/virtualmcp/suite_test.go b/cmd/thv-operator/test-integration/virtualmcp/suite_test.go index 54f18616b..7097bdcce 100644 --- a/cmd/thv-operator/test-integration/virtualmcp/suite_test.go +++ b/cmd/thv-operator/test-integration/virtualmcp/suite_test.go @@ -61,7 +61,10 @@ var _ = BeforeSuite(func() { By("bootstrapping test environment") testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crds")}, + CRDDirectoryPaths: []string{ + filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crds"), + filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crds-virtualmcp"), + }, ErrorIfCRDPathMissing: true, } @@ -125,6 +128,14 @@ var _ = BeforeSuite(func() { }).SetupWithManager(k8sManager) Expect(err).ToNot(HaveOccurred()) + // Set up VirtualMCPServer webhook + err = (&mcpv1alpha1.VirtualMCPServer{}).SetupWebhookWithManager(k8sManager) + Expect(err).ToNot(HaveOccurred()) + + // Set up VirtualMCPCompositeToolDefinition webhook + err = (&mcpv1alpha1.VirtualMCPCompositeToolDefinition{}).SetupWebhookWithManager(k8sManager) + Expect(err).ToNot(HaveOccurred()) + // Start the manager in a goroutine go func() { defer GinkgoRecover() From 8444a05af3c998376c1a19db10cd94d53ccc7975 Mon Sep 17 00:00:00 2001 From: amirejaz Date: Wed, 26 Nov 2025 15:15:43 +0000 Subject: [PATCH 07/14] add helm feature flags for operator controllers --- deploy/charts/operator-crds/README.md | 28 +++---- deploy/charts/operator-crds/README.md.gotmpl | 23 +----- deploy/charts/operator/README.md | 76 ++++++++++++++++++- .../charts/operator/templates/deployment.yaml | 6 ++ deploy/charts/operator/values-openshift.yaml | 19 ++++- deploy/charts/operator/values.yaml | 22 ++++-- 6 files changed, 125 insertions(+), 49 deletions(-) diff --git a/deploy/charts/operator-crds/README.md b/deploy/charts/operator-crds/README.md index db10772be..a37385a41 100644 --- a/deploy/charts/operator-crds/README.md +++ b/deploy/charts/operator-crds/README.md @@ -49,35 +49,29 @@ helm upgrade -i toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive- --set crds.install.virtualMCP=false ``` -**Important:** When Virtual MCP CRDs are not installed, you should also disable the Virtual MCP controllers in the operator to prevent errors. Set the `ENABLE_AGGREGATION` environment variable to `false` in the operator deployment: +**Important:** When Virtual MCP CRDs are not installed, you should also disable the Virtual MCP controllers in the operator to prevent errors. Use the `operator.features.virtualMCP` helm value: ```shell helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator \ -n toolhive-system --create-namespace \ - --set operator.env.ENABLE_AGGREGATION=false + --set operator.features.virtualMCP=false ``` -This saves approximately 54KB of CRDs and is useful for deployments that don't require Virtual MCP aggregation features. When `ENABLE_AGGREGATION=false`, the operator will skip setting up the VirtualMCPServer controller, MCPGroup controller, and their associated webhooks. +This saves approximately 54KB of CRDs and is useful for deployments that don't require Virtual MCP aggregation features. When `operator.features.virtualMCP=false`, the operator will skip setting up the VirtualMCPServer controller, MCPGroup controller, and their associated webhooks. -### Feature Flags - -The operator supports the following feature flags (via `operator.env`) to enable/disable specific controller groups: +## Values -- **`ENABLE_SERVER`** (default: `true`): Controls MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, and ToolConfig controllers -- **`ENABLE_REGISTRY`** (default: `true`): Controls MCPRegistry controller -- **`ENABLE_AGGREGATION`** (default: `true`): Controls VirtualMCPServer, MCPGroup controllers and their webhooks. Requires `ENABLE_SERVER=true`. +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| crds.install.virtualMCP | bool | `true` | Install Virtual MCP CRDs (VirtualMCPServer and VirtualMCPCompositeToolDefinition). Users who only need core MCP server management can set this to false to skip installing Virtual MCP aggregation features (saves ~54KB of CRDs). |# toolhive-operator-crds -Example: Disable all features except core server management: +![Version: 0.0.63](https://img.shields.io/badge/Version-0.0.63-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.0.1](https://img.shields.io/badge/AppVersion-0.0.1-informational?style=flat-square) -```shell -helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator \ - -n toolhive-system --create-namespace \ - --set operator.env.ENABLE_REGISTRY=false \ - --set operator.env.ENABLE_AGGREGATION=false -``` +A Helm chart for installing the ToolHive Operator CRDs into Kubernetes. ## Values | Key | Type | Default | Description | -|-----|-------------|------|---------| +|-----|------|---------|-------------| | crds.install.virtualMCP | bool | `true` | Install Virtual MCP CRDs (VirtualMCPServer and VirtualMCPCompositeToolDefinition). Users who only need core MCP server management can set this to false to skip installing Virtual MCP aggregation features (saves ~54KB of CRDs). | + diff --git a/deploy/charts/operator-crds/README.md.gotmpl b/deploy/charts/operator-crds/README.md.gotmpl index 215f36083..428d19353 100644 --- a/deploy/charts/operator-crds/README.md.gotmpl +++ b/deploy/charts/operator-crds/README.md.gotmpl @@ -57,32 +57,15 @@ helm upgrade -i toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive- --set crds.install.virtualMCP=false ``` -**Important:** When Virtual MCP CRDs are not installed, you should also disable the Virtual MCP controllers in the operator to prevent errors. Set the `ENABLE_AGGREGATION` environment variable to `false` in the operator deployment: +**Important:** When Virtual MCP CRDs are not installed, you should also disable the Virtual MCP controllers in the operator to prevent errors. Use the `operator.features.virtualMCP` helm value: ```shell helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator \ -n toolhive-system --create-namespace \ - --set operator.env.ENABLE_AGGREGATION=false + --set operator.features.virtualMCP=false ``` -This saves approximately 54KB of CRDs and is useful for deployments that don't require Virtual MCP aggregation features. When `ENABLE_AGGREGATION=false`, the operator will skip setting up the VirtualMCPServer controller, MCPGroup controller, and their associated webhooks. - -### Feature Flags - -The operator supports the following feature flags (via `operator.env`) to enable/disable specific controller groups: - -- **`ENABLE_SERVER`** (default: `true`): Controls MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, and ToolConfig controllers -- **`ENABLE_REGISTRY`** (default: `true`): Controls MCPRegistry controller -- **`ENABLE_AGGREGATION`** (default: `true`): Controls VirtualMCPServer, MCPGroup controllers and their webhooks. Requires `ENABLE_SERVER=true`. - -Example: Disable all features except core server management: - -```shell -helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator \ - -n toolhive-system --create-namespace \ - --set operator.env.ENABLE_REGISTRY=false \ - --set operator.env.ENABLE_AGGREGATION=false -``` +This saves approximately 54KB of CRDs and is useful for deployments that don't require Virtual MCP aggregation features. When `operator.features.virtualMCP=false`, the operator will skip setting up the VirtualMCPServer controller, MCPGroup controller, and their associated webhooks. {{ template "chart.requirementsSection" . }} diff --git a/deploy/charts/operator/README.md b/deploy/charts/operator/README.md index 5214c9bc9..1168dfd96 100644 --- a/deploy/charts/operator/README.md +++ b/deploy/charts/operator/README.md @@ -49,10 +49,10 @@ The command removes all the Kubernetes components associated with the chart and ## Values | Key | Type | Default | Description | -|-----|-------------|------|---------| +|-----|------|---------|-------------| | fullnameOverride | string | `"toolhive-operator"` | Provide a fully-qualified name override for resources | | nameOverride | string | `""` | Override the name of the chart | -| operator | object | `{"affinity":{},"autoscaling":{"enabled":false,"maxReplicas":100,"minReplicas":1,"targetCPUUtilizationPercentage":80},"containerSecurityContext":{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsNonRoot":true,"runAsUser":1000,"seccompProfile":{"type":"RuntimeDefault"}},"env":{},"features":{"experimental":false},"gc":{"gogc":75,"gomeglimit":"150MiB"},"image":"ghcr.io/stacklok/toolhive/operator:v0.6.6","imagePullPolicy":"IfNotPresent","imagePullSecrets":[],"leaderElectionRole":{"binding":{"name":"toolhive-operator-leader-election-rolebinding"},"name":"toolhive-operator-leader-election-role","rules":[{"apiGroups":[""],"resources":["configmaps"],"verbs":["get","list","watch","create","update","patch","delete"]},{"apiGroups":["coordination.k8s.io"],"resources":["leases"],"verbs":["get","list","watch","create","update","patch","delete"]},{"apiGroups":[""],"resources":["events"],"verbs":["create","patch"]}]},"livenessProbe":{"httpGet":{"path":"/healthz","port":"health"},"initialDelaySeconds":15,"periodSeconds":20},"nodeSelector":{},"podAnnotations":{},"podLabels":{},"podSecurityContext":{"runAsNonRoot":true},"ports":[{"containerPort":8080,"name":"metrics","protocol":"TCP"},{"containerPort":8081,"name":"health","protocol":"TCP"}],"proxyHost":"0.0.0.0","rbac":{"allowedNamespaces":[],"scope":"cluster"},"readinessProbe":{"httpGet":{"path":"/readyz","port":"health"},"initialDelaySeconds":5,"periodSeconds":10},"replicaCount":1,"resources":{"limits":{"cpu":"500m","memory":"128Mi"},"requests":{"cpu":"10m","memory":"64Mi"}},"serviceAccount":{"annotations":{},"automountServiceAccountToken":true,"create":true,"labels":{},"name":"toolhive-operator"},"tolerations":[],"toolhiveRunnerImage":"ghcr.io/stacklok/toolhive/proxyrunner:v0.6.6","volumeMounts":[],"volumes":[]}` | All values for the operator deployment and associated resources | +| operator | object | `{"affinity":{},"autoscaling":{"enabled":false,"maxReplicas":100,"minReplicas":1,"targetCPUUtilizationPercentage":80},"containerSecurityContext":{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsNonRoot":true,"runAsUser":1000,"seccompProfile":{"type":"RuntimeDefault"}},"env":{},"features":{"experimental":false,"registry":true,"server":true,"virtualMCP":true},"gc":{"gogc":75,"gomeglimit":"150MiB"},"image":"ghcr.io/stacklok/toolhive/operator:v0.6.6","imagePullPolicy":"IfNotPresent","imagePullSecrets":[],"leaderElectionRole":{"binding":{"name":"toolhive-operator-leader-election-rolebinding"},"name":"toolhive-operator-leader-election-role","rules":[{"apiGroups":[""],"resources":["configmaps"],"verbs":["get","list","watch","create","update","patch","delete"]},{"apiGroups":["coordination.k8s.io"],"resources":["leases"],"verbs":["get","list","watch","create","update","patch","delete"]},{"apiGroups":[""],"resources":["events"],"verbs":["create","patch"]}]},"livenessProbe":{"httpGet":{"path":"/healthz","port":"health"},"initialDelaySeconds":15,"periodSeconds":20},"nodeSelector":{},"podAnnotations":{},"podLabels":{},"podSecurityContext":{"runAsNonRoot":true},"ports":[{"containerPort":8080,"name":"metrics","protocol":"TCP"},{"containerPort":8081,"name":"health","protocol":"TCP"}],"proxyHost":"0.0.0.0","rbac":{"allowedNamespaces":[],"scope":"cluster"},"readinessProbe":{"httpGet":{"path":"/readyz","port":"health"},"initialDelaySeconds":5,"periodSeconds":10},"replicaCount":1,"resources":{"limits":{"cpu":"500m","memory":"128Mi"},"requests":{"cpu":"10m","memory":"64Mi"}},"serviceAccount":{"annotations":{},"automountServiceAccountToken":true,"create":true,"labels":{},"name":"toolhive-operator"},"tolerations":[],"toolhiveRunnerImage":"ghcr.io/stacklok/toolhive/proxyrunner:v0.6.6","volumeMounts":[],"volumes":[]}` | All values for the operator deployment and associated resources | | operator.affinity | object | `{}` | Affinity settings for the operator pod | | operator.autoscaling | object | `{"enabled":false,"maxReplicas":100,"minReplicas":1,"targetCPUUtilizationPercentage":80}` | Configuration for horizontal pod autoscaling | | operator.autoscaling.enabled | bool | `false` | Enable autoscaling for the operator | @@ -60,7 +60,77 @@ The command removes all the Kubernetes components associated with the chart and | operator.autoscaling.minReplicas | int | `1` | Minimum number of replicas | | operator.autoscaling.targetCPUUtilizationPercentage | int | `80` | Target CPU utilization percentage for autoscaling | | operator.containerSecurityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsNonRoot":true,"runAsUser":1000,"seccompProfile":{"type":"RuntimeDefault"}}` | Container security context settings for the operator | -| operator.env | object | `{}` | Environment variables to set in the operator container Feature flags can be set here to enable/disable controller groups: - ENABLE_SERVER (default: true): Controls MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, and ToolConfig controllers - ENABLE_REGISTRY (default: true): Controls MCPRegistry controller - ENABLE_AGGREGATION (default: true): Controls VirtualMCPServer, MCPGroup controllers and webhooks (requires ENABLE_SERVER=true) Example: env: { ENABLE_AGGREGATION: "false", ENABLE_REGISTRY: "false" } | +| operator.env | object | `{}` | Additional environment variables to set in the operator container. Note: Feature flags (server, registry, virtualMCP) should be set via operator.features.* helm values instead of using env variables directly. This allows for proper documentation and validation. Use this only for custom environment variables not covered by feature flags. | +| operator.features.experimental | bool | `false` | Enable experimental features | +| operator.features.registry | bool | `true` | Enable registry controller (MCPRegistry). This automatically sets ENABLE_REGISTRY environment variable. | +| operator.features.server | bool | `true` | Enable server-related controllers (MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, and ToolConfig). This automatically sets ENABLE_SERVER environment variable. | +| operator.features.virtualMCP | bool | `true` | Enable Virtual MCP aggregation features (VirtualMCPServer, MCPGroup controllers and webhooks). Set to false to disable Virtual MCP controllers when Virtual MCP CRDs are not installed. This automatically sets ENABLE_AGGREGATION environment variable. Requires server to be enabled (server: true). | +| operator.gc | object | `{"gogc":75,"gomeglimit":"150MiB"}` | Go memory limits and garbage collection percentage for the operator container | +| operator.gc.gogc | int | `75` | Go garbage collection percentage for the operator container | +| operator.gc.gomeglimit | string | `"150MiB"` | Go memory limits for the operator container | +| operator.image | string | `"ghcr.io/stacklok/toolhive/operator:v0.6.6"` | Container image for the operator | +| operator.imagePullPolicy | string | `"IfNotPresent"` | Image pull policy for the operator container | +| operator.imagePullSecrets | list | `[]` | List of image pull secrets to use | +| operator.leaderElectionRole | object | `{"binding":{"name":"toolhive-operator-leader-election-rolebinding"},"name":"toolhive-operator-leader-election-role","rules":[{"apiGroups":[""],"resources":["configmaps"],"verbs":["get","list","watch","create","update","patch","delete"]},{"apiGroups":["coordination.k8s.io"],"resources":["leases"],"verbs":["get","list","watch","create","update","patch","delete"]},{"apiGroups":[""],"resources":["events"],"verbs":["create","patch"]}]}` | Leader election role configuration | +| operator.leaderElectionRole.binding.name | string | `"toolhive-operator-leader-election-rolebinding"` | Name of the role binding for leader election | +| operator.leaderElectionRole.name | string | `"toolhive-operator-leader-election-role"` | Name of the role for leader election | +| operator.leaderElectionRole.rules | list | `[{"apiGroups":[""],"resources":["configmaps"],"verbs":["get","list","watch","create","update","patch","delete"]},{"apiGroups":["coordination.k8s.io"],"resources":["leases"],"verbs":["get","list","watch","create","update","patch","delete"]},{"apiGroups":[""],"resources":["events"],"verbs":["create","patch"]}]` | Rules for the leader election role | +| operator.livenessProbe | object | `{"httpGet":{"path":"/healthz","port":"health"},"initialDelaySeconds":15,"periodSeconds":20}` | Liveness probe configuration for the operator | +| operator.nodeSelector | object | `{}` | Node selector for the operator pod | +| operator.podAnnotations | object | `{}` | Annotations to add to the operator pod | +| operator.podLabels | object | `{}` | Labels to add to the operator pod | +| operator.podSecurityContext | object | `{"runAsNonRoot":true}` | Pod security context settings | +| operator.ports | list | `[{"containerPort":8080,"name":"metrics","protocol":"TCP"},{"containerPort":8081,"name":"health","protocol":"TCP"}]` | List of ports to expose from the operator container | +| operator.proxyHost | string | `"0.0.0.0"` | Host for the proxy deployed by the operator | +| operator.rbac | object | `{"allowedNamespaces":[],"scope":"cluster"}` | RBAC configuration for the operator | +| operator.rbac.allowedNamespaces | list | `[]` | List of namespaces that the operator is allowed to have permissions to manage. Only used if scope is set to "namespace". | +| operator.rbac.scope | string | `"cluster"` | Scope of the RBAC configuration. - cluster: The operator will have cluster-wide permissions via ClusterRole and ClusterRoleBinding. - namespace: The operator will have permissions to manage resources in the namespaces specified in `allowedNamespaces`. The operator will have a ClusterRole and RoleBinding for each namespace in `allowedNamespaces`. | +| operator.readinessProbe | object | `{"httpGet":{"path":"/readyz","port":"health"},"initialDelaySeconds":5,"periodSeconds":10}` | Readiness probe configuration for the operator | +| operator.replicaCount | int | `1` | Number of replicas for the operator deployment | +| operator.resources | object | `{"limits":{"cpu":"500m","memory":"128Mi"},"requests":{"cpu":"10m","memory":"64Mi"}}` | Resource requests and limits for the operator container | +| operator.serviceAccount | object | `{"annotations":{},"automountServiceAccountToken":true,"create":true,"labels":{},"name":"toolhive-operator"}` | Service account configuration for the operator | +| operator.serviceAccount.annotations | object | `{}` | Annotations to add to the service account | +| operator.serviceAccount.automountServiceAccountToken | bool | `true` | Automatically mount a ServiceAccount's API credentials | +| operator.serviceAccount.create | bool | `true` | Specifies whether a service account should be created | +| operator.serviceAccount.labels | object | `{}` | Labels to add to the service account | +| operator.serviceAccount.name | string | `"toolhive-operator"` | The name of the service account to use. If not set and create is true, a name is generated. | +| operator.tolerations | list | `[]` | Tolerations for the operator pod | +| operator.toolhiveRunnerImage | string | `"ghcr.io/stacklok/toolhive/proxyrunner:v0.6.6"` | Image to use for Toolhive runners | +| operator.volumeMounts | list | `[]` | Additional volume mounts on the operator container | +| operator.volumes | list | `[]` | Additional volumes to mount on the operator pod | +| registryAPI | object | `{"image":"ghcr.io/stacklok/thv-registry-api:v0.2.0","serviceAccount":{"annotations":{},"automountServiceAccountToken":true,"labels":{},"name":"toolhive-registry-api"}}` | All values for the registry API deployment and associated resources | +| registryAPI.image | string | `"ghcr.io/stacklok/thv-registry-api:v0.2.0"` | Container image for the registry API | +| registryAPI.serviceAccount | object | `{"annotations":{},"automountServiceAccountToken":true,"labels":{},"name":"toolhive-registry-api"}` | Service account configuration for the registry API | +| registryAPI.serviceAccount.annotations | object | `{}` | Annotations to add to the registry API service account | +| registryAPI.serviceAccount.automountServiceAccountToken | bool | `true` | Automatically mount a ServiceAccount's API credentials | +| registryAPI.serviceAccount.labels | object | `{}` | Labels to add to the registry API service account | +| registryAPI.serviceAccount.name | string | `"toolhive-registry-api"` | The name of the service account to use for the registry API | + +# toolhive-operator + +![Version: 0.5.4](https://img.shields.io/badge/Version-0.5.4-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v0.6.6](https://img.shields.io/badge/AppVersion-v0.6.6-informational?style=flat-square) + +A Helm chart for deploying the ToolHive Operator into Kubernetes. + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| fullnameOverride | string | `"toolhive-operator"` | Provide a fully-qualified name override for resources | +| nameOverride | string | `""` | Override the name of the chart | +| operator | object | `{"affinity":{},"autoscaling":{"enabled":false,"maxReplicas":100,"minReplicas":1,"targetCPUUtilizationPercentage":80},"containerSecurityContext":{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsNonRoot":true,"runAsUser":1000,"seccompProfile":{"type":"RuntimeDefault"}},"env":{},"features":{"experimental":false,"registry":true,"server":true,"virtualMCP":true},"gc":{"gogc":75,"gomeglimit":"150MiB"},"image":"ghcr.io/stacklok/toolhive/operator:v0.6.6","imagePullPolicy":"IfNotPresent","imagePullSecrets":[],"leaderElectionRole":{"binding":{"name":"toolhive-operator-leader-election-rolebinding"},"name":"toolhive-operator-leader-election-role","rules":[{"apiGroups":[""],"resources":["configmaps"],"verbs":["get","list","watch","create","update","patch","delete"]},{"apiGroups":["coordination.k8s.io"],"resources":["leases"],"verbs":["get","list","watch","create","update","patch","delete"]},{"apiGroups":[""],"resources":["events"],"verbs":["create","patch"]}]},"livenessProbe":{"httpGet":{"path":"/healthz","port":"health"},"initialDelaySeconds":15,"periodSeconds":20},"nodeSelector":{},"podAnnotations":{},"podLabels":{},"podSecurityContext":{"runAsNonRoot":true},"ports":[{"containerPort":8080,"name":"metrics","protocol":"TCP"},{"containerPort":8081,"name":"health","protocol":"TCP"}],"proxyHost":"0.0.0.0","rbac":{"allowedNamespaces":[],"scope":"cluster"},"readinessProbe":{"httpGet":{"path":"/readyz","port":"health"},"initialDelaySeconds":5,"periodSeconds":10},"replicaCount":1,"resources":{"limits":{"cpu":"500m","memory":"128Mi"},"requests":{"cpu":"10m","memory":"64Mi"}},"serviceAccount":{"annotations":{},"automountServiceAccountToken":true,"create":true,"labels":{},"name":"toolhive-operator"},"tolerations":[],"toolhiveRunnerImage":"ghcr.io/stacklok/toolhive/proxyrunner:v0.6.6","volumeMounts":[],"volumes":[]}` | All values for the operator deployment and associated resources | +| operator.affinity | object | `{}` | Affinity settings for the operator pod | +| operator.autoscaling | object | `{"enabled":false,"maxReplicas":100,"minReplicas":1,"targetCPUUtilizationPercentage":80}` | Configuration for horizontal pod autoscaling | +| operator.autoscaling.enabled | bool | `false` | Enable autoscaling for the operator | +| operator.autoscaling.maxReplicas | int | `100` | Maximum number of replicas | +| operator.autoscaling.minReplicas | int | `1` | Minimum number of replicas | +| operator.autoscaling.targetCPUUtilizationPercentage | int | `80` | Target CPU utilization percentage for autoscaling | +| operator.containerSecurityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsNonRoot":true,"runAsUser":1000,"seccompProfile":{"type":"RuntimeDefault"}}` | Container security context settings for the operator | +| operator.env | object | `{}` | Additional environment variables to set in the operator container. Note: Feature flags (server, registry, virtualMCP) should be set via operator.features.* helm values instead of using env variables directly. This allows for proper documentation and validation. Use this only for custom environment variables not covered by feature flags. | +| operator.features.experimental | bool | `false` | Enable experimental features | +| operator.features.registry | bool | `true` | Enable registry controller (MCPRegistry). This automatically sets ENABLE_REGISTRY environment variable. | +| operator.features.server | bool | `true` | Enable server-related controllers (MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, and ToolConfig). This automatically sets ENABLE_SERVER environment variable. | +| operator.features.virtualMCP | bool | `true` | Enable Virtual MCP aggregation features (VirtualMCPServer, MCPGroup controllers and webhooks). Set to false to disable Virtual MCP controllers when Virtual MCP CRDs are not installed. This automatically sets ENABLE_AGGREGATION environment variable. Requires server to be enabled (server: true). | | operator.gc | object | `{"gogc":75,"gomeglimit":"150MiB"}` | Go memory limits and garbage collection percentage for the operator container | | operator.gc.gogc | int | `75` | Go garbage collection percentage for the operator container | | operator.gc.gomeglimit | string | `"150MiB"` | Go memory limits for the operator container | diff --git a/deploy/charts/operator/templates/deployment.yaml b/deploy/charts/operator/templates/deployment.yaml index d42e00f19..5f475d308 100644 --- a/deploy/charts/operator/templates/deployment.yaml +++ b/deploy/charts/operator/templates/deployment.yaml @@ -58,6 +58,12 @@ spec: value: "true" - name: ENABLE_EXPERIMENTAL_FEATURES value: {{ .Values.operator.features.experimental | quote }} + - name: ENABLE_SERVER + value: {{ .Values.operator.features.server | quote }} + - name: ENABLE_REGISTRY + value: {{ .Values.operator.features.registry | quote }} + - name: ENABLE_AGGREGATION + value: {{ .Values.operator.features.virtualMCP | quote }} {{- if eq .Values.operator.rbac.scope "namespace" }} - name: WATCH_NAMESPACE value: "{{ .Values.operator.rbac.allowedNamespaces | join "," }}" diff --git a/deploy/charts/operator/values-openshift.yaml b/deploy/charts/operator/values-openshift.yaml index 20fd79915..a2b24d4e0 100644 --- a/deploy/charts/operator/values-openshift.yaml +++ b/deploy/charts/operator/values-openshift.yaml @@ -5,9 +5,21 @@ fullnameOverride: "toolhive-operator" # -- All values for the operator deployment and associated resources operator: - # Whether to enable experimental features + # Feature flags to enable/disable controller groups features: + # -- Enable experimental features experimental: false + # -- Enable server-related controllers (MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, and ToolConfig). + # This automatically sets ENABLE_SERVER environment variable. + server: true + # -- Enable registry controller (MCPRegistry). + # This automatically sets ENABLE_REGISTRY environment variable. + registry: true + # -- Enable Virtual MCP aggregation features (VirtualMCPServer, MCPGroup controllers and webhooks). + # Set to false to disable Virtual MCP controllers when Virtual MCP CRDs are not installed. + # This automatically sets ENABLE_AGGREGATION environment variable. + # Requires server to be enabled (server: true). + virtualMCP: true # -- Number of replicas for the operator deployment replicaCount: 1 @@ -24,7 +36,10 @@ operator: # -- Host for the proxy deployed by the operator proxyHost: 0.0.0.0 - # -- Environment variables to set in the operator container + # -- Additional environment variables to set in the operator container. + # Note: Feature flags (server, registry, virtualMCP) should be set via operator.features.* helm values + # instead of using env variables directly. This allows for proper documentation and validation. + # Use this only for custom environment variables not covered by feature flags. env: {} # -- List of ports to expose from the operator container diff --git a/deploy/charts/operator/values.yaml b/deploy/charts/operator/values.yaml index ce7b6eb3f..2fc58b5da 100644 --- a/deploy/charts/operator/values.yaml +++ b/deploy/charts/operator/values.yaml @@ -5,9 +5,21 @@ fullnameOverride: "toolhive-operator" # -- All values for the operator deployment and associated resources operator: - # Whether to enable experimental features + # Feature flags to enable/disable controller groups features: + # -- Enable experimental features experimental: false + # -- Enable server-related controllers (MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, and ToolConfig). + # This automatically sets ENABLE_SERVER environment variable. + server: true + # -- Enable registry controller (MCPRegistry). + # This automatically sets ENABLE_REGISTRY environment variable. + registry: true + # -- Enable Virtual MCP aggregation features (VirtualMCPServer, MCPGroup controllers and webhooks). + # Set to false to disable Virtual MCP controllers when Virtual MCP CRDs are not installed. + # This automatically sets ENABLE_AGGREGATION environment variable. + # Requires server to be enabled (server: true). + virtualMCP: true # -- Number of replicas for the operator deployment replicaCount: 1 @@ -24,12 +36,8 @@ operator: # -- Host for the proxy deployed by the operator proxyHost: 0.0.0.0 - # -- Environment variables to set in the operator container - # Feature flags can be set here to enable/disable controller groups: - # - ENABLE_SERVER (default: true): Controls MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, and ToolConfig controllers - # - ENABLE_REGISTRY (default: true): Controls MCPRegistry controller - # - ENABLE_AGGREGATION (default: true): Controls VirtualMCPServer, MCPGroup controllers and webhooks (requires ENABLE_SERVER=true) - # Example: env: { ENABLE_AGGREGATION: "false", ENABLE_REGISTRY: "false" } + # -- Additional environment variables to set in the operator container. + env: {} # -- List of ports to expose from the operator container From 253d8be98c6555c792459cdbd005caeb8386d6d9 Mon Sep 17 00:00:00 2001 From: amirejaz Date: Wed, 26 Nov 2025 15:36:14 +0000 Subject: [PATCH 08/14] bump operator crd version --- deploy/charts/operator-crds/Chart.yaml | 2 +- deploy/charts/operator-crds/README.md | 2 +- deploy/charts/operator/values.yaml | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/deploy/charts/operator-crds/Chart.yaml b/deploy/charts/operator-crds/Chart.yaml index 85b12b756..706ebf286 100644 --- a/deploy/charts/operator-crds/Chart.yaml +++ b/deploy/charts/operator-crds/Chart.yaml @@ -2,5 +2,5 @@ apiVersion: v2 name: toolhive-operator-crds description: A Helm chart for installing the ToolHive Operator CRDs into Kubernetes. type: application -version: 0.0.63 +version: 0.0.64 appVersion: "0.0.1" diff --git a/deploy/charts/operator-crds/README.md b/deploy/charts/operator-crds/README.md index a37385a41..67b257620 100644 --- a/deploy/charts/operator-crds/README.md +++ b/deploy/charts/operator-crds/README.md @@ -1,6 +1,6 @@ # ToolHive Operator CRDs Helm Chart -![Version: 0.0.63](https://img.shields.io/badge/Version-0.0.63-informational?style=flat-square) +![Version: 0.0.64](https://img.shields.io/badge/Version-0.0.64-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for installing the ToolHive Operator CRDs into Kubernetes. diff --git a/deploy/charts/operator/values.yaml b/deploy/charts/operator/values.yaml index 2fc58b5da..7f6168038 100644 --- a/deploy/charts/operator/values.yaml +++ b/deploy/charts/operator/values.yaml @@ -37,7 +37,6 @@ operator: proxyHost: 0.0.0.0 # -- Additional environment variables to set in the operator container. - env: {} # -- List of ports to expose from the operator container From add3a28a58aa4748ced66efe9fc94f29eee1a5cf Mon Sep 17 00:00:00 2001 From: amirejaz Date: Wed, 26 Nov 2025 16:28:28 +0000 Subject: [PATCH 09/14] organize CRDs into feature-specific directories --- .github/workflows/operator-ci.yml | 2 +- cmd/thv-operator/Taskfile.yml | 37 +++++++++++++ .../mcp-external-auth/suite_test.go | 4 +- .../test-integration/mcp-group/suite_test.go | 5 +- .../mcp-registry/suite_test.go | 2 +- .../test-integration/mcp-server/suite_test.go | 5 +- .../test-integration/virtualmcp/suite_test.go | 2 +- deploy/charts/operator-crds/README.md | 55 ++++++++++++++----- deploy/charts/operator-crds/README.md.gotmpl | 48 +++++++++++++++- .../toolhive.stacklok.dev_mcpregistries.yaml | 0 ...e.stacklok.dev_mcpexternalauthconfigs.yaml | 0 ...oolhive.stacklok.dev_mcpremoteproxies.yaml | 0 .../toolhive.stacklok.dev_mcpservers.yaml | 0 .../toolhive.stacklok.dev_mcptoolconfigs.yaml | 0 .../toolhive.stacklok.dev_mcpgroups.yaml | 0 .../templates/mcpexternalauthconfigs-crd.yaml | 5 ++ .../templates/mcpgroups-crd.yaml | 5 ++ .../templates/mcpregistries-crd.yaml | 5 ++ .../templates/mcpremoteproxies-crd.yaml | 5 ++ .../templates/mcpservers-crd.yaml | 5 ++ .../templates/mcptoolconfigs-crd.yaml | 5 ++ deploy/charts/operator-crds/values.yaml | 8 +++ deploy/charts/operator/README.md | 4 +- 23 files changed, 178 insertions(+), 24 deletions(-) rename deploy/charts/operator-crds/{crds => crds-registry}/toolhive.stacklok.dev_mcpregistries.yaml (100%) rename deploy/charts/operator-crds/{crds => crds-server}/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml (100%) rename deploy/charts/operator-crds/{crds => crds-server}/toolhive.stacklok.dev_mcpremoteproxies.yaml (100%) rename deploy/charts/operator-crds/{crds => crds-server}/toolhive.stacklok.dev_mcpservers.yaml (100%) rename deploy/charts/operator-crds/{crds => crds-server}/toolhive.stacklok.dev_mcptoolconfigs.yaml (100%) rename deploy/charts/operator-crds/{crds => crds-virtualmcp}/toolhive.stacklok.dev_mcpgroups.yaml (100%) create mode 100644 deploy/charts/operator-crds/templates/mcpexternalauthconfigs-crd.yaml create mode 100644 deploy/charts/operator-crds/templates/mcpgroups-crd.yaml create mode 100644 deploy/charts/operator-crds/templates/mcpregistries-crd.yaml create mode 100644 deploy/charts/operator-crds/templates/mcpremoteproxies-crd.yaml create mode 100644 deploy/charts/operator-crds/templates/mcpservers-crd.yaml create mode 100644 deploy/charts/operator-crds/templates/mcptoolconfigs-crd.yaml diff --git a/.github/workflows/operator-ci.yml b/.github/workflows/operator-ci.yml index f72eea2c3..97ce265d1 100644 --- a/.github/workflows/operator-ci.yml +++ b/.github/workflows/operator-ci.yml @@ -95,7 +95,7 @@ jobs: - name: Check for changes id: git-check run: | - git diff --exit-code deploy/charts/operator-crds/crds || echo "crd-changes=true" >> $GITHUB_OUTPUT + git diff --exit-code deploy/charts/operator-crds/crds deploy/charts/operator-crds/crds-server deploy/charts/operator-crds/crds-registry deploy/charts/operator-crds/crds-virtualmcp || echo "crd-changes=true" >> $GITHUB_OUTPUT git diff --exit-code deploy/charts/operator/templates || echo "operator-changes=true" >> $GITHUB_OUTPUT - name: Fail if CRDs are not up to date diff --git a/cmd/thv-operator/Taskfile.yml b/cmd/thv-operator/Taskfile.yml index 52cf8e1af..25af4bb08 100644 --- a/cmd/thv-operator/Taskfile.yml +++ b/cmd/thv-operator/Taskfile.yml @@ -186,6 +186,43 @@ tasks: - go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.17.3 - $(go env GOPATH)/bin/controller-gen crd webhook paths="./cmd/thv-operator/..." output:crd:artifacts:config=deploy/charts/operator-crds/crds - $(go env GOPATH)/bin/controller-gen rbac:roleName=toolhive-operator-manager-role paths="./cmd/thv-operator/..." output:rbac:artifacts:config=deploy/charts/operator/templates/clusterrole + # Organize CRDs into feature-specific directories + - cmd: mkdir -p deploy/charts/operator-crds/crds-server deploy/charts/operator-crds/crds-registry deploy/charts/operator-crds/crds-virtualmcp + platforms: [linux, darwin] + - cmd: cmd.exe /c mkdir deploy\charts\operator-crds\crds-server deploy\charts\operator-crds\crds-registry deploy\charts\operator-crds\crds-virtualmcp + platforms: [windows] + ignore_error: true + # Move server CRDs + - cmd: | + mv deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpservers.yaml deploy/charts/operator-crds/crds-server/ 2>/dev/null || true + mv deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml deploy/charts/operator-crds/crds-server/ 2>/dev/null || true + mv deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpremoteproxies.yaml deploy/charts/operator-crds/crds-server/ 2>/dev/null || true + mv deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcptoolconfigs.yaml deploy/charts/operator-crds/crds-server/ 2>/dev/null || true + platforms: [linux, darwin] + - cmd: | + if exist deploy\charts\operator-crds\crds\toolhive.stacklok.dev_mcpservers.yaml move deploy\charts\operator-crds\crds\toolhive.stacklok.dev_mcpservers.yaml deploy\charts\operator-crds\crds-server\ + if exist deploy\charts\operator-crds\crds\toolhive.stacklok.dev_mcpexternalauthconfigs.yaml move deploy\charts\operator-crds\crds\toolhive.stacklok.dev_mcpexternalauthconfigs.yaml deploy\charts\operator-crds\crds-server\ + if exist deploy\charts\operator-crds\crds\toolhive.stacklok.dev_mcpremoteproxies.yaml move deploy\charts\operator-crds\crds\toolhive.stacklok.dev_mcpremoteproxies.yaml deploy\charts\operator-crds\crds-server\ + if exist deploy\charts\operator-crds\crds\toolhive.stacklok.dev_mcptoolconfigs.yaml move deploy\charts\operator-crds\crds\toolhive.stacklok.dev_mcptoolconfigs.yaml deploy\charts\operator-crds\crds-server\ + platforms: [windows] + # Move registry CRD + - cmd: | + mv deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpregistries.yaml deploy/charts/operator-crds/crds-registry/ 2>/dev/null || true + platforms: [linux, darwin] + - cmd: | + if exist deploy\charts\operator-crds\crds\toolhive.stacklok.dev_mcpregistries.yaml move deploy\charts\operator-crds\crds\toolhive.stacklok.dev_mcpregistries.yaml deploy\charts\operator-crds\crds-registry\ + platforms: [windows] + # Move Virtual MCP CRDs + - cmd: | + mv deploy/charts/operator-crds/crds/toolhive.stacklok.dev_virtualmcpservers.yaml deploy/charts/operator-crds/crds-virtualmcp/ 2>/dev/null || true + mv deploy/charts/operator-crds/crds/toolhive.stacklok.dev_virtualmcpcompositetooldefinitions.yaml deploy/charts/operator-crds/crds-virtualmcp/ 2>/dev/null || true + mv deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpgroups.yaml deploy/charts/operator-crds/crds-virtualmcp/ 2>/dev/null || true + platforms: [linux, darwin] + - cmd: | + if exist deploy\charts\operator-crds\crds\toolhive.stacklok.dev_virtualmcpservers.yaml move deploy\charts\operator-crds\crds\toolhive.stacklok.dev_virtualmcpservers.yaml deploy\charts\operator-crds\crds-virtualmcp\ + if exist deploy\charts\operator-crds\crds\toolhive.stacklok.dev_virtualmcpcompositetooldefinitions.yaml move deploy\charts\operator-crds\crds\toolhive.stacklok.dev_virtualmcpcompositetooldefinitions.yaml deploy\charts\operator-crds\crds-virtualmcp\ + if exist deploy\charts\operator-crds\crds\toolhive.stacklok.dev_mcpgroups.yaml move deploy\charts\operator-crds\crds\toolhive.stacklok.dev_mcpgroups.yaml deploy\charts\operator-crds\crds-virtualmcp\ + platforms: [windows] operator-test: desc: Run tests for the operator diff --git a/cmd/thv-operator/test-integration/mcp-external-auth/suite_test.go b/cmd/thv-operator/test-integration/mcp-external-auth/suite_test.go index 8a3d9f989..ba70856b5 100644 --- a/cmd/thv-operator/test-integration/mcp-external-auth/suite_test.go +++ b/cmd/thv-operator/test-integration/mcp-external-auth/suite_test.go @@ -57,7 +57,9 @@ var _ = BeforeSuite(func() { By("bootstrapping test environment") testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crds")}, + CRDDirectoryPaths: []string{ + filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crds-server"), + }, ErrorIfCRDPathMissing: true, } diff --git a/cmd/thv-operator/test-integration/mcp-group/suite_test.go b/cmd/thv-operator/test-integration/mcp-group/suite_test.go index cbfb64b27..1c7b13b28 100644 --- a/cmd/thv-operator/test-integration/mcp-group/suite_test.go +++ b/cmd/thv-operator/test-integration/mcp-group/suite_test.go @@ -58,7 +58,10 @@ var _ = BeforeSuite(func() { By("bootstrapping test environment") testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crds")}, + CRDDirectoryPaths: []string{ + filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crds-server"), + filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crds-virtualmcp"), + }, ErrorIfCRDPathMissing: true, } diff --git a/cmd/thv-operator/test-integration/mcp-registry/suite_test.go b/cmd/thv-operator/test-integration/mcp-registry/suite_test.go index 516284300..74b90f2df 100644 --- a/cmd/thv-operator/test-integration/mcp-registry/suite_test.go +++ b/cmd/thv-operator/test-integration/mcp-registry/suite_test.go @@ -68,7 +68,7 @@ var _ = BeforeSuite(func() { testEnv = &envtest.Environment{ UseExistingCluster: &useExistingCluster, CRDDirectoryPaths: []string{ - filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crds"), + filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crds-registry"), }, ErrorIfCRDPathMissing: true, BinaryAssetsDirectory: kubebuilderAssets, diff --git a/cmd/thv-operator/test-integration/mcp-server/suite_test.go b/cmd/thv-operator/test-integration/mcp-server/suite_test.go index 17acaab3b..f2beb7467 100644 --- a/cmd/thv-operator/test-integration/mcp-server/suite_test.go +++ b/cmd/thv-operator/test-integration/mcp-server/suite_test.go @@ -61,7 +61,10 @@ var _ = BeforeSuite(func() { By("bootstrapping test environment") testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crds")}, + CRDDirectoryPaths: []string{ + filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crds-server"), + filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crds-virtualmcp"), + }, ErrorIfCRDPathMissing: true, } diff --git a/cmd/thv-operator/test-integration/virtualmcp/suite_test.go b/cmd/thv-operator/test-integration/virtualmcp/suite_test.go index 7097bdcce..0062a5d0f 100644 --- a/cmd/thv-operator/test-integration/virtualmcp/suite_test.go +++ b/cmd/thv-operator/test-integration/virtualmcp/suite_test.go @@ -62,7 +62,7 @@ var _ = BeforeSuite(func() { By("bootstrapping test environment") testEnv = &envtest.Environment{ CRDDirectoryPaths: []string{ - filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crds"), + filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crds-server"), filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crds-virtualmcp"), }, ErrorIfCRDPathMissing: true, diff --git a/deploy/charts/operator-crds/README.md b/deploy/charts/operator-crds/README.md index 67b257620..937a23663 100644 --- a/deploy/charts/operator-crds/README.md +++ b/deploy/charts/operator-crds/README.md @@ -40,38 +40,67 @@ To uninstall/delete the `toolhive-operator-crds` deployment: helm uninstall ``` -### Skipping Virtual MCP CRDs +### Skipping CRDs -By default, all CRDs including Virtual MCP CRDs (VirtualMCPServer and VirtualMCPCompositeToolDefinition) are installed. Users who only need core MCP server management can skip installing Virtual MCP aggregation features to reduce the chart size: +By default, all CRDs are installed. You can selectively disable CRD groups based on your needs: + +#### Skipping Server CRDs + +To skip server-related CRDs (MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, and ToolConfig): ```shell helm upgrade -i toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds \ - --set crds.install.virtualMCP=false + --set crds.install.server=false ``` -**Important:** When Virtual MCP CRDs are not installed, you should also disable the Virtual MCP controllers in the operator to prevent errors. Use the `operator.features.virtualMCP` helm value: +**Important:** When server CRDs are not installed, you should also disable the server controllers in the operator: ```shell helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator \ -n toolhive-system --create-namespace \ - --set operator.features.virtualMCP=false + --set operator.features.server=false ``` -This saves approximately 54KB of CRDs and is useful for deployments that don't require Virtual MCP aggregation features. When `operator.features.virtualMCP=false`, the operator will skip setting up the VirtualMCPServer controller, MCPGroup controller, and their associated webhooks. +#### Skipping Registry CRD -## Values +To skip the registry CRD (MCPRegistry): -| Key | Type | Default | Description | -|-----|------|---------|-------------| -| crds.install.virtualMCP | bool | `true` | Install Virtual MCP CRDs (VirtualMCPServer and VirtualMCPCompositeToolDefinition). Users who only need core MCP server management can set this to false to skip installing Virtual MCP aggregation features (saves ~54KB of CRDs). |# toolhive-operator-crds +```shell +helm upgrade -i toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds \ + --set crds.install.registry=false +``` -![Version: 0.0.63](https://img.shields.io/badge/Version-0.0.63-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.0.1](https://img.shields.io/badge/AppVersion-0.0.1-informational?style=flat-square) +**Important:** When registry CRD is not installed, you should also disable the registry controller in the operator: -A Helm chart for installing the ToolHive Operator CRDs into Kubernetes. +```shell +helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator \ + -n toolhive-system --create-namespace \ + --set operator.features.registry=false +``` + +#### Skipping Virtual MCP CRDs + +To skip Virtual MCP CRDs (VirtualMCPServer, VirtualMCPCompositeToolDefinition, and MCPGroup): + +```shell +helm upgrade -i toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds \ + --set crds.install.virtualMCP=false +``` + +**Important:** When Virtual MCP CRDs are not installed, you should also disable the Virtual MCP controllers in the operator: + +```shell +helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator \ + -n toolhive-system --create-namespace \ + --set operator.features.virtualMCP=false +``` + +This saves approximately 54KB of CRDs and is useful for deployments that don't require Virtual MCP aggregation features. When `operator.features.virtualMCP=false`, the operator will skip setting up the VirtualMCPServer controller, MCPGroup controller, and their associated webhooks. ## Values | Key | Type | Default | Description | |-----|------|---------|-------------| +| crds.install.registry | bool | `true` | Install registry CRD (MCPRegistry). Users who only need server management without registry features can set this to false to skip installing the registry CRD. | +| crds.install.server | bool | `true` | Install server-related CRDs (MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, and ToolConfig). Users who only need registry or aggregation features can set this to false to skip installing server management CRDs. | | crds.install.virtualMCP | bool | `true` | Install Virtual MCP CRDs (VirtualMCPServer and VirtualMCPCompositeToolDefinition). Users who only need core MCP server management can set this to false to skip installing Virtual MCP aggregation features (saves ~54KB of CRDs). | - diff --git a/deploy/charts/operator-crds/README.md.gotmpl b/deploy/charts/operator-crds/README.md.gotmpl index 428d19353..2738ce00c 100644 --- a/deploy/charts/operator-crds/README.md.gotmpl +++ b/deploy/charts/operator-crds/README.md.gotmpl @@ -48,16 +48,56 @@ To uninstall/delete the `toolhive-operator-crds` deployment: helm uninstall ``` -### Skipping Virtual MCP CRDs +### Skipping CRDs -By default, all CRDs including Virtual MCP CRDs (VirtualMCPServer and VirtualMCPCompositeToolDefinition) are installed. Users who only need core MCP server management can skip installing Virtual MCP aggregation features to reduce the chart size: +By default, all CRDs are installed. You can selectively disable CRD groups based on your needs: + +#### Skipping Server CRDs + +To skip server-related CRDs (MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, and ToolConfig): + +```shell +helm upgrade -i toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds \ + --set crds.install.server=false +``` + +**Important:** When server CRDs are not installed, you should also disable the server controllers in the operator: + +```shell +helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator \ + -n toolhive-system --create-namespace \ + --set operator.features.server=false +``` + +#### Skipping Registry CRD + +To skip the registry CRD (MCPRegistry): + +```shell +helm upgrade -i toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds \ + --set crds.install.registry=false +``` + +**Important:** When registry CRD is not installed, you should also disable the registry controller in the operator: + +```shell +helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator \ + -n toolhive-system --create-namespace \ + --set operator.features.registry=false +``` + +#### Skipping Virtual MCP CRDs + +To skip Virtual MCP CRDs (VirtualMCPServer, VirtualMCPCompositeToolDefinition, and MCPGroup): ```shell helm upgrade -i toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds \ --set crds.install.virtualMCP=false ``` -**Important:** When Virtual MCP CRDs are not installed, you should also disable the Virtual MCP controllers in the operator to prevent errors. Use the `operator.features.virtualMCP` helm value: +You can also combine this with disabling the registry CRD (see [Skipping Registry CRD](#skipping-registry-crd) section above) if you don't need registry features. + +**Important:** When Virtual MCP CRDs are not installed, you should also disable the Virtual MCP controllers in the operator: ```shell helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator \ @@ -65,6 +105,8 @@ helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-opera --set operator.features.virtualMCP=false ``` +If you also disabled the registry CRD, disable the registry controller as well (see the [Skipping Registry CRD](#skipping-registry-crd) section above). + This saves approximately 54KB of CRDs and is useful for deployments that don't require Virtual MCP aggregation features. When `operator.features.virtualMCP=false`, the operator will skip setting up the VirtualMCPServer controller, MCPGroup controller, and their associated webhooks. {{ template "chart.requirementsSection" . }} diff --git a/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpregistries.yaml b/deploy/charts/operator-crds/crds-registry/toolhive.stacklok.dev_mcpregistries.yaml similarity index 100% rename from deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpregistries.yaml rename to deploy/charts/operator-crds/crds-registry/toolhive.stacklok.dev_mcpregistries.yaml diff --git a/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml b/deploy/charts/operator-crds/crds-server/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml similarity index 100% rename from deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml rename to deploy/charts/operator-crds/crds-server/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml diff --git a/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpremoteproxies.yaml b/deploy/charts/operator-crds/crds-server/toolhive.stacklok.dev_mcpremoteproxies.yaml similarity index 100% rename from deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpremoteproxies.yaml rename to deploy/charts/operator-crds/crds-server/toolhive.stacklok.dev_mcpremoteproxies.yaml diff --git a/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpservers.yaml b/deploy/charts/operator-crds/crds-server/toolhive.stacklok.dev_mcpservers.yaml similarity index 100% rename from deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpservers.yaml rename to deploy/charts/operator-crds/crds-server/toolhive.stacklok.dev_mcpservers.yaml diff --git a/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcptoolconfigs.yaml b/deploy/charts/operator-crds/crds-server/toolhive.stacklok.dev_mcptoolconfigs.yaml similarity index 100% rename from deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcptoolconfigs.yaml rename to deploy/charts/operator-crds/crds-server/toolhive.stacklok.dev_mcptoolconfigs.yaml diff --git a/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpgroups.yaml b/deploy/charts/operator-crds/crds-virtualmcp/toolhive.stacklok.dev_mcpgroups.yaml similarity index 100% rename from deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpgroups.yaml rename to deploy/charts/operator-crds/crds-virtualmcp/toolhive.stacklok.dev_mcpgroups.yaml diff --git a/deploy/charts/operator-crds/templates/mcpexternalauthconfigs-crd.yaml b/deploy/charts/operator-crds/templates/mcpexternalauthconfigs-crd.yaml new file mode 100644 index 000000000..d214cbc6c --- /dev/null +++ b/deploy/charts/operator-crds/templates/mcpexternalauthconfigs-crd.yaml @@ -0,0 +1,5 @@ +{{- if .Values.crds.install.server }} +{{- $crd := .Files.Get "crds-server/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml" | fromYaml }} +{{- toYaml $crd | nindent 0 }} +{{- end }} + diff --git a/deploy/charts/operator-crds/templates/mcpgroups-crd.yaml b/deploy/charts/operator-crds/templates/mcpgroups-crd.yaml new file mode 100644 index 000000000..d81b1cd23 --- /dev/null +++ b/deploy/charts/operator-crds/templates/mcpgroups-crd.yaml @@ -0,0 +1,5 @@ +{{- if .Values.crds.install.virtualMCP }} +{{- $crd := .Files.Get "crds-virtualmcp/toolhive.stacklok.dev_mcpgroups.yaml" | fromYaml }} +{{- toYaml $crd | nindent 0 }} +{{- end }} + diff --git a/deploy/charts/operator-crds/templates/mcpregistries-crd.yaml b/deploy/charts/operator-crds/templates/mcpregistries-crd.yaml new file mode 100644 index 000000000..9928853db --- /dev/null +++ b/deploy/charts/operator-crds/templates/mcpregistries-crd.yaml @@ -0,0 +1,5 @@ +{{- if .Values.crds.install.registry }} +{{- $crd := .Files.Get "crds-registry/toolhive.stacklok.dev_mcpregistries.yaml" | fromYaml }} +{{- toYaml $crd | nindent 0 }} +{{- end }} + diff --git a/deploy/charts/operator-crds/templates/mcpremoteproxies-crd.yaml b/deploy/charts/operator-crds/templates/mcpremoteproxies-crd.yaml new file mode 100644 index 000000000..52b75bb56 --- /dev/null +++ b/deploy/charts/operator-crds/templates/mcpremoteproxies-crd.yaml @@ -0,0 +1,5 @@ +{{- if .Values.crds.install.server }} +{{- $crd := .Files.Get "crds-server/toolhive.stacklok.dev_mcpremoteproxies.yaml" | fromYaml }} +{{- toYaml $crd | nindent 0 }} +{{- end }} + diff --git a/deploy/charts/operator-crds/templates/mcpservers-crd.yaml b/deploy/charts/operator-crds/templates/mcpservers-crd.yaml new file mode 100644 index 000000000..84655ebc7 --- /dev/null +++ b/deploy/charts/operator-crds/templates/mcpservers-crd.yaml @@ -0,0 +1,5 @@ +{{- if .Values.crds.install.server }} +{{- $crd := .Files.Get "crds-server/toolhive.stacklok.dev_mcpservers.yaml" | fromYaml }} +{{- toYaml $crd | nindent 0 }} +{{- end }} + diff --git a/deploy/charts/operator-crds/templates/mcptoolconfigs-crd.yaml b/deploy/charts/operator-crds/templates/mcptoolconfigs-crd.yaml new file mode 100644 index 000000000..cca87cbc0 --- /dev/null +++ b/deploy/charts/operator-crds/templates/mcptoolconfigs-crd.yaml @@ -0,0 +1,5 @@ +{{- if .Values.crds.install.server }} +{{- $crd := .Files.Get "crds-server/toolhive.stacklok.dev_mcptoolconfigs.yaml" | fromYaml }} +{{- toYaml $crd | nindent 0 }} +{{- end }} + diff --git a/deploy/charts/operator-crds/values.yaml b/deploy/charts/operator-crds/values.yaml index 9d0d49951..d2c42c64b 100644 --- a/deploy/charts/operator-crds/values.yaml +++ b/deploy/charts/operator-crds/values.yaml @@ -1,6 +1,14 @@ # Configuration for CRD installation crds: install: + # -- Install server-related CRDs (MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, and ToolConfig). + # Users who only need registry or aggregation features can set this to false to skip + # installing server management CRDs. + server: true + # -- Install registry CRD (MCPRegistry). + # Users who only need server management without registry features can set this to false + # to skip installing the registry CRD. + registry: true # -- Install Virtual MCP CRDs (VirtualMCPServer and VirtualMCPCompositeToolDefinition). # Users who only need core MCP server management can set this to false to skip # installing Virtual MCP aggregation features (saves ~54KB of CRDs). diff --git a/deploy/charts/operator/README.md b/deploy/charts/operator/README.md index 1168dfd96..47e52e984 100644 --- a/deploy/charts/operator/README.md +++ b/deploy/charts/operator/README.md @@ -60,7 +60,7 @@ The command removes all the Kubernetes components associated with the chart and | operator.autoscaling.minReplicas | int | `1` | Minimum number of replicas | | operator.autoscaling.targetCPUUtilizationPercentage | int | `80` | Target CPU utilization percentage for autoscaling | | operator.containerSecurityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsNonRoot":true,"runAsUser":1000,"seccompProfile":{"type":"RuntimeDefault"}}` | Container security context settings for the operator | -| operator.env | object | `{}` | Additional environment variables to set in the operator container. Note: Feature flags (server, registry, virtualMCP) should be set via operator.features.* helm values instead of using env variables directly. This allows for proper documentation and validation. Use this only for custom environment variables not covered by feature flags. | +| operator.env | object | `{}` | Additional environment variables to set in the operator container. | | operator.features.experimental | bool | `false` | Enable experimental features | | operator.features.registry | bool | `true` | Enable registry controller (MCPRegistry). This automatically sets ENABLE_REGISTRY environment variable. | | operator.features.server | bool | `true` | Enable server-related controllers (MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, and ToolConfig). This automatically sets ENABLE_SERVER environment variable. | @@ -126,7 +126,7 @@ A Helm chart for deploying the ToolHive Operator into Kubernetes. | operator.autoscaling.minReplicas | int | `1` | Minimum number of replicas | | operator.autoscaling.targetCPUUtilizationPercentage | int | `80` | Target CPU utilization percentage for autoscaling | | operator.containerSecurityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsNonRoot":true,"runAsUser":1000,"seccompProfile":{"type":"RuntimeDefault"}}` | Container security context settings for the operator | -| operator.env | object | `{}` | Additional environment variables to set in the operator container. Note: Feature flags (server, registry, virtualMCP) should be set via operator.features.* helm values instead of using env variables directly. This allows for proper documentation and validation. Use this only for custom environment variables not covered by feature flags. | +| operator.env | object | `{}` | Additional environment variables to set in the operator container. | | operator.features.experimental | bool | `false` | Enable experimental features | | operator.features.registry | bool | `true` | Enable registry controller (MCPRegistry). This automatically sets ENABLE_REGISTRY environment variable. | | operator.features.server | bool | `true` | Enable server-related controllers (MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, and ToolConfig). This automatically sets ENABLE_SERVER environment variable. | From 38d505657c1a527a3be4124b8dfd83dfe1816edf Mon Sep 17 00:00:00 2001 From: amirejaz Date: Wed, 26 Nov 2025 16:47:02 +0000 Subject: [PATCH 10/14] updated version and readme --- deploy/charts/operator-crds/Chart.yaml | 2 +- deploy/charts/operator-crds/README.md | 8 ++- deploy/charts/operator/README.md | 68 +------------------------- 3 files changed, 8 insertions(+), 70 deletions(-) diff --git a/deploy/charts/operator-crds/Chart.yaml b/deploy/charts/operator-crds/Chart.yaml index 706ebf286..1393137fd 100644 --- a/deploy/charts/operator-crds/Chart.yaml +++ b/deploy/charts/operator-crds/Chart.yaml @@ -2,5 +2,5 @@ apiVersion: v2 name: toolhive-operator-crds description: A Helm chart for installing the ToolHive Operator CRDs into Kubernetes. type: application -version: 0.0.64 +version: 0.0.65 appVersion: "0.0.1" diff --git a/deploy/charts/operator-crds/README.md b/deploy/charts/operator-crds/README.md index 937a23663..d3ec14348 100644 --- a/deploy/charts/operator-crds/README.md +++ b/deploy/charts/operator-crds/README.md @@ -1,6 +1,6 @@ # ToolHive Operator CRDs Helm Chart -![Version: 0.0.64](https://img.shields.io/badge/Version-0.0.64-informational?style=flat-square) +![Version: 0.0.65](https://img.shields.io/badge/Version-0.0.65-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for installing the ToolHive Operator CRDs into Kubernetes. @@ -87,6 +87,8 @@ helm upgrade -i toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive- --set crds.install.virtualMCP=false ``` +You can also combine this with disabling the registry CRD (see [Skipping Registry CRD](#skipping-registry-crd) section above) if you don't need registry features. + **Important:** When Virtual MCP CRDs are not installed, you should also disable the Virtual MCP controllers in the operator: ```shell @@ -95,12 +97,14 @@ helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-opera --set operator.features.virtualMCP=false ``` +If you also disabled the registry CRD, disable the registry controller as well (see the [Skipping Registry CRD](#skipping-registry-crd) section above). + This saves approximately 54KB of CRDs and is useful for deployments that don't require Virtual MCP aggregation features. When `operator.features.virtualMCP=false`, the operator will skip setting up the VirtualMCPServer controller, MCPGroup controller, and their associated webhooks. ## Values | Key | Type | Default | Description | -|-----|------|---------|-------------| +|-----|-------------|------|---------| | crds.install.registry | bool | `true` | Install registry CRD (MCPRegistry). Users who only need server management without registry features can set this to false to skip installing the registry CRD. | | crds.install.server | bool | `true` | Install server-related CRDs (MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, and ToolConfig). Users who only need registry or aggregation features can set this to false to skip installing server management CRDs. | | crds.install.virtualMCP | bool | `true` | Install Virtual MCP CRDs (VirtualMCPServer and VirtualMCPCompositeToolDefinition). Users who only need core MCP server management can set this to false to skip installing Virtual MCP aggregation features (saves ~54KB of CRDs). | diff --git a/deploy/charts/operator/README.md b/deploy/charts/operator/README.md index 47e52e984..d2a1ce27b 100644 --- a/deploy/charts/operator/README.md +++ b/deploy/charts/operator/README.md @@ -49,73 +49,7 @@ The command removes all the Kubernetes components associated with the chart and ## Values | Key | Type | Default | Description | -|-----|------|---------|-------------| -| fullnameOverride | string | `"toolhive-operator"` | Provide a fully-qualified name override for resources | -| nameOverride | string | `""` | Override the name of the chart | -| operator | object | `{"affinity":{},"autoscaling":{"enabled":false,"maxReplicas":100,"minReplicas":1,"targetCPUUtilizationPercentage":80},"containerSecurityContext":{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsNonRoot":true,"runAsUser":1000,"seccompProfile":{"type":"RuntimeDefault"}},"env":{},"features":{"experimental":false,"registry":true,"server":true,"virtualMCP":true},"gc":{"gogc":75,"gomeglimit":"150MiB"},"image":"ghcr.io/stacklok/toolhive/operator:v0.6.6","imagePullPolicy":"IfNotPresent","imagePullSecrets":[],"leaderElectionRole":{"binding":{"name":"toolhive-operator-leader-election-rolebinding"},"name":"toolhive-operator-leader-election-role","rules":[{"apiGroups":[""],"resources":["configmaps"],"verbs":["get","list","watch","create","update","patch","delete"]},{"apiGroups":["coordination.k8s.io"],"resources":["leases"],"verbs":["get","list","watch","create","update","patch","delete"]},{"apiGroups":[""],"resources":["events"],"verbs":["create","patch"]}]},"livenessProbe":{"httpGet":{"path":"/healthz","port":"health"},"initialDelaySeconds":15,"periodSeconds":20},"nodeSelector":{},"podAnnotations":{},"podLabels":{},"podSecurityContext":{"runAsNonRoot":true},"ports":[{"containerPort":8080,"name":"metrics","protocol":"TCP"},{"containerPort":8081,"name":"health","protocol":"TCP"}],"proxyHost":"0.0.0.0","rbac":{"allowedNamespaces":[],"scope":"cluster"},"readinessProbe":{"httpGet":{"path":"/readyz","port":"health"},"initialDelaySeconds":5,"periodSeconds":10},"replicaCount":1,"resources":{"limits":{"cpu":"500m","memory":"128Mi"},"requests":{"cpu":"10m","memory":"64Mi"}},"serviceAccount":{"annotations":{},"automountServiceAccountToken":true,"create":true,"labels":{},"name":"toolhive-operator"},"tolerations":[],"toolhiveRunnerImage":"ghcr.io/stacklok/toolhive/proxyrunner:v0.6.6","volumeMounts":[],"volumes":[]}` | All values for the operator deployment and associated resources | -| operator.affinity | object | `{}` | Affinity settings for the operator pod | -| operator.autoscaling | object | `{"enabled":false,"maxReplicas":100,"minReplicas":1,"targetCPUUtilizationPercentage":80}` | Configuration for horizontal pod autoscaling | -| operator.autoscaling.enabled | bool | `false` | Enable autoscaling for the operator | -| operator.autoscaling.maxReplicas | int | `100` | Maximum number of replicas | -| operator.autoscaling.minReplicas | int | `1` | Minimum number of replicas | -| operator.autoscaling.targetCPUUtilizationPercentage | int | `80` | Target CPU utilization percentage for autoscaling | -| operator.containerSecurityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsNonRoot":true,"runAsUser":1000,"seccompProfile":{"type":"RuntimeDefault"}}` | Container security context settings for the operator | -| operator.env | object | `{}` | Additional environment variables to set in the operator container. | -| operator.features.experimental | bool | `false` | Enable experimental features | -| operator.features.registry | bool | `true` | Enable registry controller (MCPRegistry). This automatically sets ENABLE_REGISTRY environment variable. | -| operator.features.server | bool | `true` | Enable server-related controllers (MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, and ToolConfig). This automatically sets ENABLE_SERVER environment variable. | -| operator.features.virtualMCP | bool | `true` | Enable Virtual MCP aggregation features (VirtualMCPServer, MCPGroup controllers and webhooks). Set to false to disable Virtual MCP controllers when Virtual MCP CRDs are not installed. This automatically sets ENABLE_AGGREGATION environment variable. Requires server to be enabled (server: true). | -| operator.gc | object | `{"gogc":75,"gomeglimit":"150MiB"}` | Go memory limits and garbage collection percentage for the operator container | -| operator.gc.gogc | int | `75` | Go garbage collection percentage for the operator container | -| operator.gc.gomeglimit | string | `"150MiB"` | Go memory limits for the operator container | -| operator.image | string | `"ghcr.io/stacklok/toolhive/operator:v0.6.6"` | Container image for the operator | -| operator.imagePullPolicy | string | `"IfNotPresent"` | Image pull policy for the operator container | -| operator.imagePullSecrets | list | `[]` | List of image pull secrets to use | -| operator.leaderElectionRole | object | `{"binding":{"name":"toolhive-operator-leader-election-rolebinding"},"name":"toolhive-operator-leader-election-role","rules":[{"apiGroups":[""],"resources":["configmaps"],"verbs":["get","list","watch","create","update","patch","delete"]},{"apiGroups":["coordination.k8s.io"],"resources":["leases"],"verbs":["get","list","watch","create","update","patch","delete"]},{"apiGroups":[""],"resources":["events"],"verbs":["create","patch"]}]}` | Leader election role configuration | -| operator.leaderElectionRole.binding.name | string | `"toolhive-operator-leader-election-rolebinding"` | Name of the role binding for leader election | -| operator.leaderElectionRole.name | string | `"toolhive-operator-leader-election-role"` | Name of the role for leader election | -| operator.leaderElectionRole.rules | list | `[{"apiGroups":[""],"resources":["configmaps"],"verbs":["get","list","watch","create","update","patch","delete"]},{"apiGroups":["coordination.k8s.io"],"resources":["leases"],"verbs":["get","list","watch","create","update","patch","delete"]},{"apiGroups":[""],"resources":["events"],"verbs":["create","patch"]}]` | Rules for the leader election role | -| operator.livenessProbe | object | `{"httpGet":{"path":"/healthz","port":"health"},"initialDelaySeconds":15,"periodSeconds":20}` | Liveness probe configuration for the operator | -| operator.nodeSelector | object | `{}` | Node selector for the operator pod | -| operator.podAnnotations | object | `{}` | Annotations to add to the operator pod | -| operator.podLabels | object | `{}` | Labels to add to the operator pod | -| operator.podSecurityContext | object | `{"runAsNonRoot":true}` | Pod security context settings | -| operator.ports | list | `[{"containerPort":8080,"name":"metrics","protocol":"TCP"},{"containerPort":8081,"name":"health","protocol":"TCP"}]` | List of ports to expose from the operator container | -| operator.proxyHost | string | `"0.0.0.0"` | Host for the proxy deployed by the operator | -| operator.rbac | object | `{"allowedNamespaces":[],"scope":"cluster"}` | RBAC configuration for the operator | -| operator.rbac.allowedNamespaces | list | `[]` | List of namespaces that the operator is allowed to have permissions to manage. Only used if scope is set to "namespace". | -| operator.rbac.scope | string | `"cluster"` | Scope of the RBAC configuration. - cluster: The operator will have cluster-wide permissions via ClusterRole and ClusterRoleBinding. - namespace: The operator will have permissions to manage resources in the namespaces specified in `allowedNamespaces`. The operator will have a ClusterRole and RoleBinding for each namespace in `allowedNamespaces`. | -| operator.readinessProbe | object | `{"httpGet":{"path":"/readyz","port":"health"},"initialDelaySeconds":5,"periodSeconds":10}` | Readiness probe configuration for the operator | -| operator.replicaCount | int | `1` | Number of replicas for the operator deployment | -| operator.resources | object | `{"limits":{"cpu":"500m","memory":"128Mi"},"requests":{"cpu":"10m","memory":"64Mi"}}` | Resource requests and limits for the operator container | -| operator.serviceAccount | object | `{"annotations":{},"automountServiceAccountToken":true,"create":true,"labels":{},"name":"toolhive-operator"}` | Service account configuration for the operator | -| operator.serviceAccount.annotations | object | `{}` | Annotations to add to the service account | -| operator.serviceAccount.automountServiceAccountToken | bool | `true` | Automatically mount a ServiceAccount's API credentials | -| operator.serviceAccount.create | bool | `true` | Specifies whether a service account should be created | -| operator.serviceAccount.labels | object | `{}` | Labels to add to the service account | -| operator.serviceAccount.name | string | `"toolhive-operator"` | The name of the service account to use. If not set and create is true, a name is generated. | -| operator.tolerations | list | `[]` | Tolerations for the operator pod | -| operator.toolhiveRunnerImage | string | `"ghcr.io/stacklok/toolhive/proxyrunner:v0.6.6"` | Image to use for Toolhive runners | -| operator.volumeMounts | list | `[]` | Additional volume mounts on the operator container | -| operator.volumes | list | `[]` | Additional volumes to mount on the operator pod | -| registryAPI | object | `{"image":"ghcr.io/stacklok/thv-registry-api:v0.2.0","serviceAccount":{"annotations":{},"automountServiceAccountToken":true,"labels":{},"name":"toolhive-registry-api"}}` | All values for the registry API deployment and associated resources | -| registryAPI.image | string | `"ghcr.io/stacklok/thv-registry-api:v0.2.0"` | Container image for the registry API | -| registryAPI.serviceAccount | object | `{"annotations":{},"automountServiceAccountToken":true,"labels":{},"name":"toolhive-registry-api"}` | Service account configuration for the registry API | -| registryAPI.serviceAccount.annotations | object | `{}` | Annotations to add to the registry API service account | -| registryAPI.serviceAccount.automountServiceAccountToken | bool | `true` | Automatically mount a ServiceAccount's API credentials | -| registryAPI.serviceAccount.labels | object | `{}` | Labels to add to the registry API service account | -| registryAPI.serviceAccount.name | string | `"toolhive-registry-api"` | The name of the service account to use for the registry API | - -# toolhive-operator - -![Version: 0.5.4](https://img.shields.io/badge/Version-0.5.4-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v0.6.6](https://img.shields.io/badge/AppVersion-v0.6.6-informational?style=flat-square) - -A Helm chart for deploying the ToolHive Operator into Kubernetes. - -## Values - -| Key | Type | Default | Description | -|-----|------|---------|-------------| +|-----|-------------|------|---------| | fullnameOverride | string | `"toolhive-operator"` | Provide a fully-qualified name override for resources | | nameOverride | string | `""` | Override the name of the chart | | operator | object | `{"affinity":{},"autoscaling":{"enabled":false,"maxReplicas":100,"minReplicas":1,"targetCPUUtilizationPercentage":80},"containerSecurityContext":{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsNonRoot":true,"runAsUser":1000,"seccompProfile":{"type":"RuntimeDefault"}},"env":{},"features":{"experimental":false,"registry":true,"server":true,"virtualMCP":true},"gc":{"gogc":75,"gomeglimit":"150MiB"},"image":"ghcr.io/stacklok/toolhive/operator:v0.6.6","imagePullPolicy":"IfNotPresent","imagePullSecrets":[],"leaderElectionRole":{"binding":{"name":"toolhive-operator-leader-election-rolebinding"},"name":"toolhive-operator-leader-election-role","rules":[{"apiGroups":[""],"resources":["configmaps"],"verbs":["get","list","watch","create","update","patch","delete"]},{"apiGroups":["coordination.k8s.io"],"resources":["leases"],"verbs":["get","list","watch","create","update","patch","delete"]},{"apiGroups":[""],"resources":["events"],"verbs":["create","patch"]}]},"livenessProbe":{"httpGet":{"path":"/healthz","port":"health"},"initialDelaySeconds":15,"periodSeconds":20},"nodeSelector":{},"podAnnotations":{},"podLabels":{},"podSecurityContext":{"runAsNonRoot":true},"ports":[{"containerPort":8080,"name":"metrics","protocol":"TCP"},{"containerPort":8081,"name":"health","protocol":"TCP"}],"proxyHost":"0.0.0.0","rbac":{"allowedNamespaces":[],"scope":"cluster"},"readinessProbe":{"httpGet":{"path":"/readyz","port":"health"},"initialDelaySeconds":5,"periodSeconds":10},"replicaCount":1,"resources":{"limits":{"cpu":"500m","memory":"128Mi"},"requests":{"cpu":"10m","memory":"64Mi"}},"serviceAccount":{"annotations":{},"automountServiceAccountToken":true,"create":true,"labels":{},"name":"toolhive-operator"},"tolerations":[],"toolhiveRunnerImage":"ghcr.io/stacklok/toolhive/proxyrunner:v0.6.6","volumeMounts":[],"volumes":[]}` | All values for the operator deployment and associated resources | From 8fcc2ddf94044395d378e5403c9820ca81b874b0 Mon Sep 17 00:00:00 2001 From: amirejaz Date: Wed, 26 Nov 2025 17:51:36 +0000 Subject: [PATCH 11/14] bump version --- deploy/charts/operator/Chart.yaml | 2 +- deploy/charts/operator/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy/charts/operator/Chart.yaml b/deploy/charts/operator/Chart.yaml index 7be652a2d..bbaf92889 100644 --- a/deploy/charts/operator/Chart.yaml +++ b/deploy/charts/operator/Chart.yaml @@ -2,5 +2,5 @@ apiVersion: v2 name: toolhive-operator description: A Helm chart for deploying the ToolHive Operator into Kubernetes. type: application -version: 0.5.4 +version: 0.5.5 appVersion: "v0.6.6" diff --git a/deploy/charts/operator/README.md b/deploy/charts/operator/README.md index d2a1ce27b..3edaf26be 100644 --- a/deploy/charts/operator/README.md +++ b/deploy/charts/operator/README.md @@ -1,6 +1,6 @@ # ToolHive Operator Helm Chart -![Version: 0.5.4](https://img.shields.io/badge/Version-0.5.4-informational?style=flat-square) +![Version: 0.5.5](https://img.shields.io/badge/Version-0.5.5-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying the ToolHive Operator into Kubernetes. From 1a92af694dcdd32b13b020ea4a2df56617c23f47 Mon Sep 17 00:00:00 2001 From: amirejaz Date: Wed, 26 Nov 2025 18:12:47 +0000 Subject: [PATCH 12/14] fix helm test ci check --- cmd/thv-operator/main.go | 34 +++++++++++++++---------------- deploy/charts/operator/Chart.yaml | 2 +- deploy/charts/operator/README.md | 2 +- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/cmd/thv-operator/main.go b/cmd/thv-operator/main.go index 15e05417e..ac89f2d69 100644 --- a/cmd/thv-operator/main.go +++ b/cmd/thv-operator/main.go @@ -153,23 +153,6 @@ func setupControllersAndWebhooks(mgr ctrl.Manager) error { // setupServerControllers sets up server-related controllers (MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, ToolConfig) func setupServerControllers(mgr ctrl.Manager, enableRegistry bool) error { - // Set up field indexing for MCPServer.Spec.GroupRef - // This is needed for MCPGroup controller to find MCPServers that belong to a group - if err := mgr.GetFieldIndexer().IndexField( - context.Background(), - &mcpv1alpha1.MCPServer{}, - "spec.groupRef", - func(obj client.Object) []string { - mcpServer := obj.(*mcpv1alpha1.MCPServer) - if mcpServer.Spec.GroupRef == "" { - return nil - } - return []string{mcpServer.Spec.GroupRef} - }, - ); err != nil { - return fmt.Errorf("unable to create field index for spec.groupRef: %w", err) - } - // Create a shared platform detector for all controllers sharedPlatformDetector := ctrlutil.NewSharedPlatformDetector() @@ -232,6 +215,23 @@ func setupRegistryController(mgr ctrl.Manager) error { // setupAggregationControllers sets up aggregation-related controllers and webhooks // (MCPGroup, VirtualMCPServer, and their webhooks) func setupAggregationControllers(mgr ctrl.Manager) error { + // Set up field indexing for MCPServer.Spec.GroupRef + // This is needed for MCPGroup controller to find MCPServers that belong to a group + if err := mgr.GetFieldIndexer().IndexField( + context.Background(), + &mcpv1alpha1.MCPServer{}, + "spec.groupRef", + func(obj client.Object) []string { + mcpServer := obj.(*mcpv1alpha1.MCPServer) + if mcpServer.Spec.GroupRef == "" { + return nil + } + return []string{mcpServer.Spec.GroupRef} + }, + ); err != nil { + return fmt.Errorf("unable to create field index for spec.groupRef: %w", err) + } + // Set up MCPGroup controller if err := (&controllers.MCPGroupReconciler{ Client: mgr.GetClient(), diff --git a/deploy/charts/operator/Chart.yaml b/deploy/charts/operator/Chart.yaml index bbaf92889..7be652a2d 100644 --- a/deploy/charts/operator/Chart.yaml +++ b/deploy/charts/operator/Chart.yaml @@ -2,5 +2,5 @@ apiVersion: v2 name: toolhive-operator description: A Helm chart for deploying the ToolHive Operator into Kubernetes. type: application -version: 0.5.5 +version: 0.5.4 appVersion: "v0.6.6" diff --git a/deploy/charts/operator/README.md b/deploy/charts/operator/README.md index 3edaf26be..d2a1ce27b 100644 --- a/deploy/charts/operator/README.md +++ b/deploy/charts/operator/README.md @@ -1,6 +1,6 @@ # ToolHive Operator Helm Chart -![Version: 0.5.5](https://img.shields.io/badge/Version-0.5.5-informational?style=flat-square) +![Version: 0.5.4](https://img.shields.io/badge/Version-0.5.4-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) A Helm chart for deploying the ToolHive Operator into Kubernetes. From 116db7a8e8cfb0cf81a5df3703d9d43a7baea9bf Mon Sep 17 00:00:00 2001 From: amirejaz Date: Thu, 27 Nov 2025 17:59:20 +0000 Subject: [PATCH 13/14] addressed the feedback --- .github/workflows/operator-ci.yml | 2 +- cmd/thv-operator/Taskfile.yml | 39 +------- cmd/thv-operator/main.go | 89 +++++++++++++------ .../mcp-external-auth/suite_test.go | 2 +- .../test-integration/mcp-group/suite_test.go | 3 +- .../mcp-registry/suite_test.go | 2 +- .../test-integration/mcp-server/suite_test.go | 3 +- .../test-integration/virtualmcp/suite_test.go | 3 +- deploy/charts/operator-crds/README.md | 4 +- deploy/charts/operator-crds/README.md.gotmpl | 2 +- .../templates/mcpexternalauthconfigs-crd.yaml | 4 +- .../templates/mcpgroups-crd.yaml | 2 +- .../templates/mcpregistries-crd.yaml | 2 +- .../templates/mcpremoteproxies-crd.yaml | 2 +- .../templates/mcpservers-crd.yaml | 2 +- .../templates/mcptoolconfigs-crd.yaml | 2 +- ...irtualmcpcompositetooldefinitions-crd.yaml | 2 +- .../templates/virtualmcpservers-crd.yaml | 2 +- deploy/charts/operator-crds/values.yaml | 2 +- deploy/charts/operator/README.md | 2 +- .../charts/operator/templates/deployment.yaml | 2 +- deploy/charts/operator/values-openshift.yaml | 2 +- deploy/charts/operator/values.yaml | 2 +- 23 files changed, 88 insertions(+), 89 deletions(-) diff --git a/.github/workflows/operator-ci.yml b/.github/workflows/operator-ci.yml index 97ce265d1..66d30bec4 100644 --- a/.github/workflows/operator-ci.yml +++ b/.github/workflows/operator-ci.yml @@ -95,7 +95,7 @@ jobs: - name: Check for changes id: git-check run: | - git diff --exit-code deploy/charts/operator-crds/crds deploy/charts/operator-crds/crds-server deploy/charts/operator-crds/crds-registry deploy/charts/operator-crds/crds-virtualmcp || echo "crd-changes=true" >> $GITHUB_OUTPUT + git diff --exit-code deploy/charts/operator-crds/crds deploy/charts/operator-crds/crds-* || echo "crd-changes=true" >> $GITHUB_OUTPUT git diff --exit-code deploy/charts/operator/templates || echo "operator-changes=true" >> $GITHUB_OUTPUT - name: Fail if CRDs are not up to date diff --git a/cmd/thv-operator/Taskfile.yml b/cmd/thv-operator/Taskfile.yml index 25af4bb08..fe0094765 100644 --- a/cmd/thv-operator/Taskfile.yml +++ b/cmd/thv-operator/Taskfile.yml @@ -184,45 +184,8 @@ tasks: platforms: [windows] ignore_error: true # Windows has no mkdir -p, so just ignore error if it exists - go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.17.3 - - $(go env GOPATH)/bin/controller-gen crd webhook paths="./cmd/thv-operator/..." output:crd:artifacts:config=deploy/charts/operator-crds/crds + - $(go env GOPATH)/bin/controller-gen crd webhook paths="./cmd/thv-operator/..." output:crd:artifacts:config=deploy/charts/operator-crds/crd-files - $(go env GOPATH)/bin/controller-gen rbac:roleName=toolhive-operator-manager-role paths="./cmd/thv-operator/..." output:rbac:artifacts:config=deploy/charts/operator/templates/clusterrole - # Organize CRDs into feature-specific directories - - cmd: mkdir -p deploy/charts/operator-crds/crds-server deploy/charts/operator-crds/crds-registry deploy/charts/operator-crds/crds-virtualmcp - platforms: [linux, darwin] - - cmd: cmd.exe /c mkdir deploy\charts\operator-crds\crds-server deploy\charts\operator-crds\crds-registry deploy\charts\operator-crds\crds-virtualmcp - platforms: [windows] - ignore_error: true - # Move server CRDs - - cmd: | - mv deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpservers.yaml deploy/charts/operator-crds/crds-server/ 2>/dev/null || true - mv deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml deploy/charts/operator-crds/crds-server/ 2>/dev/null || true - mv deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpremoteproxies.yaml deploy/charts/operator-crds/crds-server/ 2>/dev/null || true - mv deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcptoolconfigs.yaml deploy/charts/operator-crds/crds-server/ 2>/dev/null || true - platforms: [linux, darwin] - - cmd: | - if exist deploy\charts\operator-crds\crds\toolhive.stacklok.dev_mcpservers.yaml move deploy\charts\operator-crds\crds\toolhive.stacklok.dev_mcpservers.yaml deploy\charts\operator-crds\crds-server\ - if exist deploy\charts\operator-crds\crds\toolhive.stacklok.dev_mcpexternalauthconfigs.yaml move deploy\charts\operator-crds\crds\toolhive.stacklok.dev_mcpexternalauthconfigs.yaml deploy\charts\operator-crds\crds-server\ - if exist deploy\charts\operator-crds\crds\toolhive.stacklok.dev_mcpremoteproxies.yaml move deploy\charts\operator-crds\crds\toolhive.stacklok.dev_mcpremoteproxies.yaml deploy\charts\operator-crds\crds-server\ - if exist deploy\charts\operator-crds\crds\toolhive.stacklok.dev_mcptoolconfigs.yaml move deploy\charts\operator-crds\crds\toolhive.stacklok.dev_mcptoolconfigs.yaml deploy\charts\operator-crds\crds-server\ - platforms: [windows] - # Move registry CRD - - cmd: | - mv deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpregistries.yaml deploy/charts/operator-crds/crds-registry/ 2>/dev/null || true - platforms: [linux, darwin] - - cmd: | - if exist deploy\charts\operator-crds\crds\toolhive.stacklok.dev_mcpregistries.yaml move deploy\charts\operator-crds\crds\toolhive.stacklok.dev_mcpregistries.yaml deploy\charts\operator-crds\crds-registry\ - platforms: [windows] - # Move Virtual MCP CRDs - - cmd: | - mv deploy/charts/operator-crds/crds/toolhive.stacklok.dev_virtualmcpservers.yaml deploy/charts/operator-crds/crds-virtualmcp/ 2>/dev/null || true - mv deploy/charts/operator-crds/crds/toolhive.stacklok.dev_virtualmcpcompositetooldefinitions.yaml deploy/charts/operator-crds/crds-virtualmcp/ 2>/dev/null || true - mv deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpgroups.yaml deploy/charts/operator-crds/crds-virtualmcp/ 2>/dev/null || true - platforms: [linux, darwin] - - cmd: | - if exist deploy\charts\operator-crds\crds\toolhive.stacklok.dev_virtualmcpservers.yaml move deploy\charts\operator-crds\crds\toolhive.stacklok.dev_virtualmcpservers.yaml deploy\charts\operator-crds\crds-virtualmcp\ - if exist deploy\charts\operator-crds\crds\toolhive.stacklok.dev_virtualmcpcompositetooldefinitions.yaml move deploy\charts\operator-crds\crds\toolhive.stacklok.dev_virtualmcpcompositetooldefinitions.yaml deploy\charts\operator-crds\crds-virtualmcp\ - if exist deploy\charts\operator-crds\crds\toolhive.stacklok.dev_mcpgroups.yaml move deploy\charts\operator-crds\crds\toolhive.stacklok.dev_mcpgroups.yaml deploy\charts\operator-crds\crds-virtualmcp\ - platforms: [windows] operator-test: desc: Run tests for the operator diff --git a/cmd/thv-operator/main.go b/cmd/thv-operator/main.go index ac89f2d69..a15ccc06b 100644 --- a/cmd/thv-operator/main.go +++ b/cmd/thv-operator/main.go @@ -7,6 +7,7 @@ import ( "flag" "fmt" "os" + "strconv" "strings" // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) @@ -36,6 +37,18 @@ var ( setupLog = log.Log.WithName("setup") ) +// Feature flags for controller groups +const ( + featureServer = "ENABLE_SERVER" + featureRegistry = "ENABLE_REGISTRY" + featureVMCP = "ENABLE_VMCP" +) + +// controllerDependencies maps each controller group to its required dependencies +var controllerDependencies = map[string][]string{ + featureVMCP: {featureServer}, // Virtual MCP requires server controllers +} + func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) utilruntime.Must(mcpv1alpha1.AddToScheme(scheme)) @@ -112,13 +125,42 @@ func main() { // setupControllersAndWebhooks sets up all controllers and webhooks with the manager func setupControllersAndWebhooks(mgr ctrl.Manager) error { // Check feature flags - enableServer := isFeatureEnabled("ENABLE_SERVER", true) - enableRegistry := isFeatureEnabled("ENABLE_REGISTRY", true) - enableAggregation := isFeatureEnabled("ENABLE_AGGREGATION", true) + enableServer := isFeatureEnabled(featureServer, true) + enableRegistry := isFeatureEnabled(featureRegistry, true) + enableVMCP := isFeatureEnabled(featureVMCP, true) + + // Track enabled features for dependency checking + enabledFeatures := map[string]bool{ + featureServer: enableServer, + featureRegistry: enableRegistry, + featureVMCP: enableVMCP, + } + + // Check dependencies and log warnings for missing dependencies + for feature, deps := range controllerDependencies { + if !enabledFeatures[feature] { + continue // Skip if feature itself is disabled + } + for _, dep := range deps { + if !enabledFeatures[dep] { + setupLog.Info( + fmt.Sprintf("%s requires %s to be enabled, skipping %s controllers", feature, dep, feature), + "feature", feature, + "required_dependency", dep, + ) + enabledFeatures[feature] = false // Mark as effectively disabled + break + } + } + } + + // Create a shared platform detector for controllers that need it + // (MCPServer, MCPRemoteProxy, and VirtualMCPServer) + sharedPlatformDetector := ctrlutil.NewSharedPlatformDetector() // Set up server-related controllers - if enableServer { - if err := setupServerControllers(mgr, enableRegistry); err != nil { + if enabledFeatures[featureServer] { + if err := setupServerControllers(mgr, enableRegistry, sharedPlatformDetector); err != nil { return err } } else { @@ -126,7 +168,7 @@ func setupControllersAndWebhooks(mgr ctrl.Manager) error { } // Set up registry controller - if enableRegistry { + if enabledFeatures[featureRegistry] { if err := setupRegistryController(mgr); err != nil { return err } @@ -134,17 +176,13 @@ func setupControllersAndWebhooks(mgr ctrl.Manager) error { setupLog.Info("ENABLE_REGISTRY is disabled, skipping MCPRegistry controller") } - // Set up aggregation controllers and webhooks - if enableAggregation { - if !enableServer { - setupLog.Info("ENABLE_AGGREGATION requires ENABLE_SERVER to be enabled, skipping aggregation controllers") - } else { - if err := setupAggregationControllers(mgr); err != nil { - return err - } + // Set up Virtual MCP controllers and webhooks + if enabledFeatures[featureVMCP] { + if err := setupAggregationControllers(mgr, sharedPlatformDetector); err != nil { + return err } } else { - setupLog.Info("ENABLE_AGGREGATION is disabled, skipping Virtual MCP controllers and webhooks") + setupLog.Info("ENABLE_VMCP is disabled, skipping Virtual MCP controllers and webhooks") } //+kubebuilder:scaffold:builder @@ -152,10 +190,7 @@ func setupControllersAndWebhooks(mgr ctrl.Manager) error { } // setupServerControllers sets up server-related controllers (MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, ToolConfig) -func setupServerControllers(mgr ctrl.Manager, enableRegistry bool) error { - // Create a shared platform detector for all controllers - sharedPlatformDetector := ctrlutil.NewSharedPlatformDetector() - +func setupServerControllers(mgr ctrl.Manager, enableRegistry bool, platformDetector *ctrlutil.SharedPlatformDetector) error { // Set image validation mode based on whether registry is enabled // If ENABLE_REGISTRY is enabled, enforce registry-based image validation // Otherwise, allow all images @@ -169,7 +204,7 @@ func setupServerControllers(mgr ctrl.Manager, enableRegistry bool) error { Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorderFor("mcpserver-controller"), - PlatformDetector: sharedPlatformDetector, + PlatformDetector: platformDetector, ImageValidation: imageValidation, } if err := rec.SetupWithManager(mgr); err != nil { @@ -196,7 +231,7 @@ func setupServerControllers(mgr ctrl.Manager, enableRegistry bool) error { if err := (&controllers.MCPRemoteProxyReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), - PlatformDetector: ctrlutil.NewSharedPlatformDetector(), + PlatformDetector: platformDetector, }).SetupWithManager(mgr); err != nil { return fmt.Errorf("unable to create controller MCPRemoteProxy: %w", err) } @@ -212,9 +247,9 @@ func setupRegistryController(mgr ctrl.Manager) error { return nil } -// setupAggregationControllers sets up aggregation-related controllers and webhooks +// setupAggregationControllers sets up Virtual MCP-related controllers and webhooks // (MCPGroup, VirtualMCPServer, and their webhooks) -func setupAggregationControllers(mgr ctrl.Manager) error { +func setupAggregationControllers(mgr ctrl.Manager, platformDetector *ctrlutil.SharedPlatformDetector) error { // Set up field indexing for MCPServer.Spec.GroupRef // This is needed for MCPGroup controller to find MCPServers that belong to a group if err := mgr.GetFieldIndexer().IndexField( @@ -244,7 +279,7 @@ func setupAggregationControllers(mgr ctrl.Manager) error { Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Recorder: mgr.GetEventRecorderFor("virtualmcpserver-controller"), - PlatformDetector: ctrlutil.NewSharedPlatformDetector(), + PlatformDetector: platformDetector, }).SetupWithManager(mgr); err != nil { return fmt.Errorf("unable to create controller VirtualMCPServer: %w", err) } @@ -270,7 +305,11 @@ func isFeatureEnabled(envVar string, defaultValue bool) bool { if !found { return defaultValue } - return strings.EqualFold(value, "true") + enabled, err := strconv.ParseBool(value) + if err != nil { + return defaultValue + } + return enabled } // getDefaultNamespaces returns a map of namespaces to cache.Config for the operator to watch. diff --git a/cmd/thv-operator/test-integration/mcp-external-auth/suite_test.go b/cmd/thv-operator/test-integration/mcp-external-auth/suite_test.go index ba70856b5..3c1fe570d 100644 --- a/cmd/thv-operator/test-integration/mcp-external-auth/suite_test.go +++ b/cmd/thv-operator/test-integration/mcp-external-auth/suite_test.go @@ -58,7 +58,7 @@ var _ = BeforeSuite(func() { By("bootstrapping test environment") testEnv = &envtest.Environment{ CRDDirectoryPaths: []string{ - filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crds-server"), + filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crd-files"), }, ErrorIfCRDPathMissing: true, } diff --git a/cmd/thv-operator/test-integration/mcp-group/suite_test.go b/cmd/thv-operator/test-integration/mcp-group/suite_test.go index 1c7b13b28..f377e84a1 100644 --- a/cmd/thv-operator/test-integration/mcp-group/suite_test.go +++ b/cmd/thv-operator/test-integration/mcp-group/suite_test.go @@ -59,8 +59,7 @@ var _ = BeforeSuite(func() { By("bootstrapping test environment") testEnv = &envtest.Environment{ CRDDirectoryPaths: []string{ - filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crds-server"), - filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crds-virtualmcp"), + filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crd-files"), }, ErrorIfCRDPathMissing: true, } diff --git a/cmd/thv-operator/test-integration/mcp-registry/suite_test.go b/cmd/thv-operator/test-integration/mcp-registry/suite_test.go index 74b90f2df..7ea1f17f7 100644 --- a/cmd/thv-operator/test-integration/mcp-registry/suite_test.go +++ b/cmd/thv-operator/test-integration/mcp-registry/suite_test.go @@ -68,7 +68,7 @@ var _ = BeforeSuite(func() { testEnv = &envtest.Environment{ UseExistingCluster: &useExistingCluster, CRDDirectoryPaths: []string{ - filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crds-registry"), + filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crd-files"), }, ErrorIfCRDPathMissing: true, BinaryAssetsDirectory: kubebuilderAssets, diff --git a/cmd/thv-operator/test-integration/mcp-server/suite_test.go b/cmd/thv-operator/test-integration/mcp-server/suite_test.go index f2beb7467..b11c009f1 100644 --- a/cmd/thv-operator/test-integration/mcp-server/suite_test.go +++ b/cmd/thv-operator/test-integration/mcp-server/suite_test.go @@ -62,8 +62,7 @@ var _ = BeforeSuite(func() { By("bootstrapping test environment") testEnv = &envtest.Environment{ CRDDirectoryPaths: []string{ - filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crds-server"), - filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crds-virtualmcp"), + filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crd-files"), }, ErrorIfCRDPathMissing: true, } diff --git a/cmd/thv-operator/test-integration/virtualmcp/suite_test.go b/cmd/thv-operator/test-integration/virtualmcp/suite_test.go index 0062a5d0f..39cc4344a 100644 --- a/cmd/thv-operator/test-integration/virtualmcp/suite_test.go +++ b/cmd/thv-operator/test-integration/virtualmcp/suite_test.go @@ -62,8 +62,7 @@ var _ = BeforeSuite(func() { By("bootstrapping test environment") testEnv = &envtest.Environment{ CRDDirectoryPaths: []string{ - filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crds-server"), - filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crds-virtualmcp"), + filepath.Join("..", "..", "..", "..", "deploy", "charts", "operator-crds", "crd-files"), }, ErrorIfCRDPathMissing: true, } diff --git a/deploy/charts/operator-crds/README.md b/deploy/charts/operator-crds/README.md index d3ec14348..7b7e56810 100644 --- a/deploy/charts/operator-crds/README.md +++ b/deploy/charts/operator-crds/README.md @@ -99,7 +99,7 @@ helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-opera If you also disabled the registry CRD, disable the registry controller as well (see the [Skipping Registry CRD](#skipping-registry-crd) section above). -This saves approximately 54KB of CRDs and is useful for deployments that don't require Virtual MCP aggregation features. When `operator.features.virtualMCP=false`, the operator will skip setting up the VirtualMCPServer controller, MCPGroup controller, and their associated webhooks. +This is useful for deployments that don't require Virtual MCP aggregation features. When `operator.features.virtualMCP=false`, the operator will skip setting up the VirtualMCPServer controller, MCPGroup controller, and their associated webhooks. ## Values @@ -107,4 +107,4 @@ This saves approximately 54KB of CRDs and is useful for deployments that don't r |-----|-------------|------|---------| | crds.install.registry | bool | `true` | Install registry CRD (MCPRegistry). Users who only need server management without registry features can set this to false to skip installing the registry CRD. | | crds.install.server | bool | `true` | Install server-related CRDs (MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, and ToolConfig). Users who only need registry or aggregation features can set this to false to skip installing server management CRDs. | -| crds.install.virtualMCP | bool | `true` | Install Virtual MCP CRDs (VirtualMCPServer and VirtualMCPCompositeToolDefinition). Users who only need core MCP server management can set this to false to skip installing Virtual MCP aggregation features (saves ~54KB of CRDs). | +| crds.install.virtualMCP | bool | `true` | Install Virtual MCP CRDs (VirtualMCPServer and VirtualMCPCompositeToolDefinition). Users who only need core MCP server management can set this to false to skip installing Virtual MCP aggregation features. | diff --git a/deploy/charts/operator-crds/README.md.gotmpl b/deploy/charts/operator-crds/README.md.gotmpl index 2738ce00c..cdf55d079 100644 --- a/deploy/charts/operator-crds/README.md.gotmpl +++ b/deploy/charts/operator-crds/README.md.gotmpl @@ -107,7 +107,7 @@ helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-opera If you also disabled the registry CRD, disable the registry controller as well (see the [Skipping Registry CRD](#skipping-registry-crd) section above). -This saves approximately 54KB of CRDs and is useful for deployments that don't require Virtual MCP aggregation features. When `operator.features.virtualMCP=false`, the operator will skip setting up the VirtualMCPServer controller, MCPGroup controller, and their associated webhooks. +This is useful for deployments that don't require Virtual MCP aggregation features. When `operator.features.virtualMCP=false`, the operator will skip setting up the VirtualMCPServer controller, MCPGroup controller, and their associated webhooks. {{ template "chart.requirementsSection" . }} diff --git a/deploy/charts/operator-crds/templates/mcpexternalauthconfigs-crd.yaml b/deploy/charts/operator-crds/templates/mcpexternalauthconfigs-crd.yaml index d214cbc6c..fc98e1857 100644 --- a/deploy/charts/operator-crds/templates/mcpexternalauthconfigs-crd.yaml +++ b/deploy/charts/operator-crds/templates/mcpexternalauthconfigs-crd.yaml @@ -1,5 +1,5 @@ -{{- if .Values.crds.install.server }} -{{- $crd := .Files.Get "crds-server/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml" | fromYaml }} +{{- if .Values.crds.install.server || .Values.crds.install.virtualMCP}} +{{- $crd := .Files.Get "crd-files/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml" | fromYaml }} {{- toYaml $crd | nindent 0 }} {{- end }} diff --git a/deploy/charts/operator-crds/templates/mcpgroups-crd.yaml b/deploy/charts/operator-crds/templates/mcpgroups-crd.yaml index d81b1cd23..e7ef647ac 100644 --- a/deploy/charts/operator-crds/templates/mcpgroups-crd.yaml +++ b/deploy/charts/operator-crds/templates/mcpgroups-crd.yaml @@ -1,5 +1,5 @@ {{- if .Values.crds.install.virtualMCP }} -{{- $crd := .Files.Get "crds-virtualmcp/toolhive.stacklok.dev_mcpgroups.yaml" | fromYaml }} +{{- $crd := .Files.Get "crd-files/toolhive.stacklok.dev_mcpgroups.yaml" | fromYaml }} {{- toYaml $crd | nindent 0 }} {{- end }} diff --git a/deploy/charts/operator-crds/templates/mcpregistries-crd.yaml b/deploy/charts/operator-crds/templates/mcpregistries-crd.yaml index 9928853db..60cb77f63 100644 --- a/deploy/charts/operator-crds/templates/mcpregistries-crd.yaml +++ b/deploy/charts/operator-crds/templates/mcpregistries-crd.yaml @@ -1,5 +1,5 @@ {{- if .Values.crds.install.registry }} -{{- $crd := .Files.Get "crds-registry/toolhive.stacklok.dev_mcpregistries.yaml" | fromYaml }} +{{- $crd := .Files.Get "crd-files/toolhive.stacklok.dev_mcpregistries.yaml" | fromYaml }} {{- toYaml $crd | nindent 0 }} {{- end }} diff --git a/deploy/charts/operator-crds/templates/mcpremoteproxies-crd.yaml b/deploy/charts/operator-crds/templates/mcpremoteproxies-crd.yaml index 52b75bb56..8aa394fd5 100644 --- a/deploy/charts/operator-crds/templates/mcpremoteproxies-crd.yaml +++ b/deploy/charts/operator-crds/templates/mcpremoteproxies-crd.yaml @@ -1,5 +1,5 @@ {{- if .Values.crds.install.server }} -{{- $crd := .Files.Get "crds-server/toolhive.stacklok.dev_mcpremoteproxies.yaml" | fromYaml }} +{{- $crd := .Files.Get "crd-files/toolhive.stacklok.dev_mcpremoteproxies.yaml" | fromYaml }} {{- toYaml $crd | nindent 0 }} {{- end }} diff --git a/deploy/charts/operator-crds/templates/mcpservers-crd.yaml b/deploy/charts/operator-crds/templates/mcpservers-crd.yaml index 84655ebc7..54144af16 100644 --- a/deploy/charts/operator-crds/templates/mcpservers-crd.yaml +++ b/deploy/charts/operator-crds/templates/mcpservers-crd.yaml @@ -1,5 +1,5 @@ {{- if .Values.crds.install.server }} -{{- $crd := .Files.Get "crds-server/toolhive.stacklok.dev_mcpservers.yaml" | fromYaml }} +{{- $crd := .Files.Get "crd-files/toolhive.stacklok.dev_mcpservers.yaml" | fromYaml }} {{- toYaml $crd | nindent 0 }} {{- end }} diff --git a/deploy/charts/operator-crds/templates/mcptoolconfigs-crd.yaml b/deploy/charts/operator-crds/templates/mcptoolconfigs-crd.yaml index cca87cbc0..3e9c77ff7 100644 --- a/deploy/charts/operator-crds/templates/mcptoolconfigs-crd.yaml +++ b/deploy/charts/operator-crds/templates/mcptoolconfigs-crd.yaml @@ -1,5 +1,5 @@ {{- if .Values.crds.install.server }} -{{- $crd := .Files.Get "crds-server/toolhive.stacklok.dev_mcptoolconfigs.yaml" | fromYaml }} +{{- $crd := .Files.Get "crd-files/toolhive.stacklok.dev_mcptoolconfigs.yaml" | fromYaml }} {{- toYaml $crd | nindent 0 }} {{- end }} diff --git a/deploy/charts/operator-crds/templates/virtualmcpcompositetooldefinitions-crd.yaml b/deploy/charts/operator-crds/templates/virtualmcpcompositetooldefinitions-crd.yaml index 0470e63c1..db2643dab 100644 --- a/deploy/charts/operator-crds/templates/virtualmcpcompositetooldefinitions-crd.yaml +++ b/deploy/charts/operator-crds/templates/virtualmcpcompositetooldefinitions-crd.yaml @@ -1,4 +1,4 @@ {{- if .Values.crds.install.virtualMCP }} -{{- $crd := .Files.Get "crds-virtualmcp/toolhive.stacklok.dev_virtualmcpcompositetooldefinitions.yaml" | fromYaml }} +{{- $crd := .Files.Get "crd-files/toolhive.stacklok.dev_virtualmcpcompositetooldefinitions.yaml" | fromYaml }} {{- toYaml $crd | nindent 0 }} {{- end }} diff --git a/deploy/charts/operator-crds/templates/virtualmcpservers-crd.yaml b/deploy/charts/operator-crds/templates/virtualmcpservers-crd.yaml index 8f831ba79..06f820490 100644 --- a/deploy/charts/operator-crds/templates/virtualmcpservers-crd.yaml +++ b/deploy/charts/operator-crds/templates/virtualmcpservers-crd.yaml @@ -1,4 +1,4 @@ {{- if .Values.crds.install.virtualMCP }} -{{- $crd := .Files.Get "crds-virtualmcp/toolhive.stacklok.dev_virtualmcpservers.yaml" | fromYaml }} +{{- $crd := .Files.Get "crd-files/toolhive.stacklok.dev_virtualmcpservers.yaml" | fromYaml }} {{- toYaml $crd | nindent 0 }} {{- end }} diff --git a/deploy/charts/operator-crds/values.yaml b/deploy/charts/operator-crds/values.yaml index d2c42c64b..9b9ac64e6 100644 --- a/deploy/charts/operator-crds/values.yaml +++ b/deploy/charts/operator-crds/values.yaml @@ -11,5 +11,5 @@ crds: registry: true # -- Install Virtual MCP CRDs (VirtualMCPServer and VirtualMCPCompositeToolDefinition). # Users who only need core MCP server management can set this to false to skip - # installing Virtual MCP aggregation features (saves ~54KB of CRDs). + # installing Virtual MCP aggregation features. virtualMCP: true diff --git a/deploy/charts/operator/README.md b/deploy/charts/operator/README.md index d2a1ce27b..965ead0f7 100644 --- a/deploy/charts/operator/README.md +++ b/deploy/charts/operator/README.md @@ -64,7 +64,7 @@ The command removes all the Kubernetes components associated with the chart and | operator.features.experimental | bool | `false` | Enable experimental features | | operator.features.registry | bool | `true` | Enable registry controller (MCPRegistry). This automatically sets ENABLE_REGISTRY environment variable. | | operator.features.server | bool | `true` | Enable server-related controllers (MCPServer, MCPExternalAuthConfig, MCPRemoteProxy, and ToolConfig). This automatically sets ENABLE_SERVER environment variable. | -| operator.features.virtualMCP | bool | `true` | Enable Virtual MCP aggregation features (VirtualMCPServer, MCPGroup controllers and webhooks). Set to false to disable Virtual MCP controllers when Virtual MCP CRDs are not installed. This automatically sets ENABLE_AGGREGATION environment variable. Requires server to be enabled (server: true). | +| operator.features.virtualMCP | bool | `true` | Enable Virtual MCP aggregation features (VirtualMCPServer, MCPGroup controllers and webhooks). Set to false to disable Virtual MCP controllers when Virtual MCP CRDs are not installed. This automatically sets ENABLE_VMCP environment variable. Requires server to be enabled (server: true). | | operator.gc | object | `{"gogc":75,"gomeglimit":"150MiB"}` | Go memory limits and garbage collection percentage for the operator container | | operator.gc.gogc | int | `75` | Go garbage collection percentage for the operator container | | operator.gc.gomeglimit | string | `"150MiB"` | Go memory limits for the operator container | diff --git a/deploy/charts/operator/templates/deployment.yaml b/deploy/charts/operator/templates/deployment.yaml index 5f475d308..aa6195ad4 100644 --- a/deploy/charts/operator/templates/deployment.yaml +++ b/deploy/charts/operator/templates/deployment.yaml @@ -62,7 +62,7 @@ spec: value: {{ .Values.operator.features.server | quote }} - name: ENABLE_REGISTRY value: {{ .Values.operator.features.registry | quote }} - - name: ENABLE_AGGREGATION + - name: ENABLE_VMCP value: {{ .Values.operator.features.virtualMCP | quote }} {{- if eq .Values.operator.rbac.scope "namespace" }} - name: WATCH_NAMESPACE diff --git a/deploy/charts/operator/values-openshift.yaml b/deploy/charts/operator/values-openshift.yaml index a2b24d4e0..e7ad3e314 100644 --- a/deploy/charts/operator/values-openshift.yaml +++ b/deploy/charts/operator/values-openshift.yaml @@ -17,7 +17,7 @@ operator: registry: true # -- Enable Virtual MCP aggregation features (VirtualMCPServer, MCPGroup controllers and webhooks). # Set to false to disable Virtual MCP controllers when Virtual MCP CRDs are not installed. - # This automatically sets ENABLE_AGGREGATION environment variable. + # This automatically sets ENABLE_VMCP environment variable. # Requires server to be enabled (server: true). virtualMCP: true # -- Number of replicas for the operator deployment diff --git a/deploy/charts/operator/values.yaml b/deploy/charts/operator/values.yaml index 7f6168038..4a42b81b1 100644 --- a/deploy/charts/operator/values.yaml +++ b/deploy/charts/operator/values.yaml @@ -17,7 +17,7 @@ operator: registry: true # -- Enable Virtual MCP aggregation features (VirtualMCPServer, MCPGroup controllers and webhooks). # Set to false to disable Virtual MCP controllers when Virtual MCP CRDs are not installed. - # This automatically sets ENABLE_AGGREGATION environment variable. + # This automatically sets ENABLE_VMCP environment variable. # Requires server to be enabled (server: true). virtualMCP: true # -- Number of replicas for the operator deployment From dfc9176142f11800caf414655bcef4450c7c7f2a Mon Sep 17 00:00:00 2001 From: amirejaz Date: Thu, 27 Nov 2025 18:08:04 +0000 Subject: [PATCH 14/14] moved the crds under crd-files --- .github/workflows/operator-ci.yml | 2 +- .../toolhive.stacklok.dev_mcpexternalauthconfigs.yaml | 0 .../toolhive.stacklok.dev_mcpgroups.yaml | 0 .../toolhive.stacklok.dev_mcpregistries.yaml | 0 .../toolhive.stacklok.dev_mcpremoteproxies.yaml | 0 .../toolhive.stacklok.dev_mcpservers.yaml | 0 .../toolhive.stacklok.dev_mcptoolconfigs.yaml | 0 ...olhive.stacklok.dev_virtualmcpcompositetooldefinitions.yaml | 0 .../toolhive.stacklok.dev_virtualmcpservers.yaml | 0 .../operator-crds/templates/mcpexternalauthconfigs-crd.yaml | 3 ++- 10 files changed, 3 insertions(+), 2 deletions(-) rename deploy/charts/operator-crds/{crds-server => crd-files}/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml (100%) rename deploy/charts/operator-crds/{crds-virtualmcp => crd-files}/toolhive.stacklok.dev_mcpgroups.yaml (100%) rename deploy/charts/operator-crds/{crds-registry => crd-files}/toolhive.stacklok.dev_mcpregistries.yaml (100%) rename deploy/charts/operator-crds/{crds-server => crd-files}/toolhive.stacklok.dev_mcpremoteproxies.yaml (100%) rename deploy/charts/operator-crds/{crds-server => crd-files}/toolhive.stacklok.dev_mcpservers.yaml (100%) rename deploy/charts/operator-crds/{crds-server => crd-files}/toolhive.stacklok.dev_mcptoolconfigs.yaml (100%) rename deploy/charts/operator-crds/{crds-virtualmcp => crd-files}/toolhive.stacklok.dev_virtualmcpcompositetooldefinitions.yaml (100%) rename deploy/charts/operator-crds/{crds-virtualmcp => crd-files}/toolhive.stacklok.dev_virtualmcpservers.yaml (100%) diff --git a/.github/workflows/operator-ci.yml b/.github/workflows/operator-ci.yml index 66d30bec4..7a8afbc8b 100644 --- a/.github/workflows/operator-ci.yml +++ b/.github/workflows/operator-ci.yml @@ -95,7 +95,7 @@ jobs: - name: Check for changes id: git-check run: | - git diff --exit-code deploy/charts/operator-crds/crds deploy/charts/operator-crds/crds-* || echo "crd-changes=true" >> $GITHUB_OUTPUT + git diff --exit-code deploy/charts/operator-crds/crds deploy/charts/operator-crds/crds-files || echo "crd-changes=true" >> $GITHUB_OUTPUT git diff --exit-code deploy/charts/operator/templates || echo "operator-changes=true" >> $GITHUB_OUTPUT - name: Fail if CRDs are not up to date diff --git a/deploy/charts/operator-crds/crds-server/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml b/deploy/charts/operator-crds/crd-files/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml similarity index 100% rename from deploy/charts/operator-crds/crds-server/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml rename to deploy/charts/operator-crds/crd-files/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml diff --git a/deploy/charts/operator-crds/crds-virtualmcp/toolhive.stacklok.dev_mcpgroups.yaml b/deploy/charts/operator-crds/crd-files/toolhive.stacklok.dev_mcpgroups.yaml similarity index 100% rename from deploy/charts/operator-crds/crds-virtualmcp/toolhive.stacklok.dev_mcpgroups.yaml rename to deploy/charts/operator-crds/crd-files/toolhive.stacklok.dev_mcpgroups.yaml diff --git a/deploy/charts/operator-crds/crds-registry/toolhive.stacklok.dev_mcpregistries.yaml b/deploy/charts/operator-crds/crd-files/toolhive.stacklok.dev_mcpregistries.yaml similarity index 100% rename from deploy/charts/operator-crds/crds-registry/toolhive.stacklok.dev_mcpregistries.yaml rename to deploy/charts/operator-crds/crd-files/toolhive.stacklok.dev_mcpregistries.yaml diff --git a/deploy/charts/operator-crds/crds-server/toolhive.stacklok.dev_mcpremoteproxies.yaml b/deploy/charts/operator-crds/crd-files/toolhive.stacklok.dev_mcpremoteproxies.yaml similarity index 100% rename from deploy/charts/operator-crds/crds-server/toolhive.stacklok.dev_mcpremoteproxies.yaml rename to deploy/charts/operator-crds/crd-files/toolhive.stacklok.dev_mcpremoteproxies.yaml diff --git a/deploy/charts/operator-crds/crds-server/toolhive.stacklok.dev_mcpservers.yaml b/deploy/charts/operator-crds/crd-files/toolhive.stacklok.dev_mcpservers.yaml similarity index 100% rename from deploy/charts/operator-crds/crds-server/toolhive.stacklok.dev_mcpservers.yaml rename to deploy/charts/operator-crds/crd-files/toolhive.stacklok.dev_mcpservers.yaml diff --git a/deploy/charts/operator-crds/crds-server/toolhive.stacklok.dev_mcptoolconfigs.yaml b/deploy/charts/operator-crds/crd-files/toolhive.stacklok.dev_mcptoolconfigs.yaml similarity index 100% rename from deploy/charts/operator-crds/crds-server/toolhive.stacklok.dev_mcptoolconfigs.yaml rename to deploy/charts/operator-crds/crd-files/toolhive.stacklok.dev_mcptoolconfigs.yaml diff --git a/deploy/charts/operator-crds/crds-virtualmcp/toolhive.stacklok.dev_virtualmcpcompositetooldefinitions.yaml b/deploy/charts/operator-crds/crd-files/toolhive.stacklok.dev_virtualmcpcompositetooldefinitions.yaml similarity index 100% rename from deploy/charts/operator-crds/crds-virtualmcp/toolhive.stacklok.dev_virtualmcpcompositetooldefinitions.yaml rename to deploy/charts/operator-crds/crd-files/toolhive.stacklok.dev_virtualmcpcompositetooldefinitions.yaml diff --git a/deploy/charts/operator-crds/crds-virtualmcp/toolhive.stacklok.dev_virtualmcpservers.yaml b/deploy/charts/operator-crds/crd-files/toolhive.stacklok.dev_virtualmcpservers.yaml similarity index 100% rename from deploy/charts/operator-crds/crds-virtualmcp/toolhive.stacklok.dev_virtualmcpservers.yaml rename to deploy/charts/operator-crds/crd-files/toolhive.stacklok.dev_virtualmcpservers.yaml diff --git a/deploy/charts/operator-crds/templates/mcpexternalauthconfigs-crd.yaml b/deploy/charts/operator-crds/templates/mcpexternalauthconfigs-crd.yaml index fc98e1857..b72797334 100644 --- a/deploy/charts/operator-crds/templates/mcpexternalauthconfigs-crd.yaml +++ b/deploy/charts/operator-crds/templates/mcpexternalauthconfigs-crd.yaml @@ -1,4 +1,5 @@ -{{- if .Values.crds.install.server || .Values.crds.install.virtualMCP}} +{{- if or .Values.crds.install.server .Values.crds.install.virtualMCP }} +{{- /* MCPExternalAuthConfig is needed by both MCPServer and VirtualMCPServer */}} {{- $crd := .Files.Get "crd-files/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml" | fromYaml }} {{- toYaml $crd | nindent 0 }} {{- end }}