From 5cc696585366b26d1166e4f2fda556ee4e3bf586 Mon Sep 17 00:00:00 2001 From: Anvesh Thakur Date: Mon, 6 Apr 2026 13:29:50 +0530 Subject: [PATCH 1/4] Replace custom EnvVar with corev1.EnvVar --- cmd/thv-operator/api/v1alpha1/mcpserver_types.go | 4 ++-- .../api/v1alpha1/zz_generated.deepcopy.go | 6 +++--- .../controllers/mcpremoteproxy_deployment_test.go | 2 +- .../mcpserver_resource_overrides_test.go | 14 +++++++------- .../controllers/mcpserver_runconfig.go | 2 +- .../controllers/mcpserver_runconfig_test.go | 12 ++++++------ .../mcpserver_controller_integration_test.go | 10 +--------- .../mcpserver_runconfig_integration_test.go | 6 +++--- docs/operator/crd-api.md | 6 ++---- pkg/export/k8s.go | 5 +++-- test/e2e/thv-operator/virtualmcp/helpers.go | 6 +++--- .../virtualmcp/virtualmcp_auth_discovery_test.go | 4 ++-- .../virtualmcp/virtualmcp_circuit_breaker_test.go | 4 ++-- .../virtualmcp/virtualmcp_external_auth_test.go | 4 ++-- .../virtualmcp_optimizer_multibackend_test.go | 2 +- .../virtualmcp/virtualmcp_telemetry_test.go | 2 +- .../virtualmcp/virtualmcp_yardstick_base_test.go | 5 +++-- 17 files changed, 43 insertions(+), 51 deletions(-) diff --git a/cmd/thv-operator/api/v1alpha1/mcpserver_types.go b/cmd/thv-operator/api/v1alpha1/mcpserver_types.go index cc58bb43da..52ab1cdf13 100644 --- a/cmd/thv-operator/api/v1alpha1/mcpserver_types.go +++ b/cmd/thv-operator/api/v1alpha1/mcpserver_types.go @@ -193,7 +193,7 @@ type MCPServerSpec struct { // Env are environment variables to set in the MCP server container // +optional - Env []EnvVar `json:"env,omitempty"` + Env []corev1.EnvVar `json:"env,omitempty"` // Volumes are volumes to mount in the MCP server container // +optional @@ -348,7 +348,7 @@ type ProxyDeploymentOverrides struct { // These affect the toolhive proxy itself, not the MCP server it manages // Use TOOLHIVE_DEBUG=true to enable debug logging in the proxy // +optional - Env []EnvVar `json:"env,omitempty"` + Env []corev1.EnvVar `json:"env,omitempty"` // ImagePullSecrets allows specifying image pull secrets for the proxy runner // These are applied to both the Deployment and the ServiceAccount diff --git a/cmd/thv-operator/api/v1alpha1/zz_generated.deepcopy.go b/cmd/thv-operator/api/v1alpha1/zz_generated.deepcopy.go index b8f06dc149..016e629010 100644 --- a/cmd/thv-operator/api/v1alpha1/zz_generated.deepcopy.go +++ b/cmd/thv-operator/api/v1alpha1/zz_generated.deepcopy.go @@ -22,7 +22,7 @@ package v1alpha1 import ( corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -1618,7 +1618,7 @@ func (in *MCPServerSpec) DeepCopyInto(out *MCPServerSpec) { } if in.Env != nil { in, out := &in.Env, &out.Env - *out = make([]EnvVar, len(*in)) + *out = make([]corev1.EnvVar, len(*in)) copy(*out, *in) } if in.Volumes != nil { @@ -2382,7 +2382,7 @@ func (in *ProxyDeploymentOverrides) DeepCopyInto(out *ProxyDeploymentOverrides) } if in.Env != nil { in, out := &in.Env, &out.Env - *out = make([]EnvVar, len(*in)) + *out = make([]corev1.EnvVar, len(*in)) copy(*out, *in) } if in.ImagePullSecrets != nil { diff --git a/cmd/thv-operator/controllers/mcpremoteproxy_deployment_test.go b/cmd/thv-operator/controllers/mcpremoteproxy_deployment_test.go index f4105195c6..482b19cc40 100644 --- a/cmd/thv-operator/controllers/mcpremoteproxy_deployment_test.go +++ b/cmd/thv-operator/controllers/mcpremoteproxy_deployment_test.go @@ -136,7 +136,7 @@ func TestDeploymentForMCPRemoteProxy(t *testing.T) { "custom-annotation": "custom-annotation-value", }, }, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "CUSTOM_ENV", Value: "custom-value"}, {Name: "TOOLHIVE_DEBUG", Value: "true"}, }, diff --git a/cmd/thv-operator/controllers/mcpserver_resource_overrides_test.go b/cmd/thv-operator/controllers/mcpserver_resource_overrides_test.go index ba6e516752..9f8d1fb61b 100644 --- a/cmd/thv-operator/controllers/mcpserver_resource_overrides_test.go +++ b/cmd/thv-operator/controllers/mcpserver_resource_overrides_test.go @@ -155,7 +155,7 @@ func TestResourceOverrides(t *testing.T) { "environment": "test", }, }, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "HTTP_PROXY", Value: "http://proxy.example.com:8080", @@ -203,7 +203,7 @@ func TestResourceOverrides(t *testing.T) { ProxyPort: 8080, ResourceOverrides: &mcpv1alpha1.ResourceOverrides{ ProxyDeployment: &mcpv1alpha1.ProxyDeploymentOverrides{ - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TOOLHIVE_DEBUG", Value: "true"}, }, }, @@ -249,7 +249,7 @@ func TestResourceOverrides(t *testing.T) { "version": "v1.2.3", }, }, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "LOG_LEVEL", Value: "debug", @@ -483,7 +483,7 @@ func TestDeploymentNeedsUpdateProxyEnv(t *testing.T) { ProxyPort: 8080, ResourceOverrides: &mcpv1alpha1.ResourceOverrides{ ProxyDeployment: &mcpv1alpha1.ProxyDeploymentOverrides{ - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "HTTP_PROXY", Value: "http://proxy.example.com:8080"}, {Name: "NO_PROXY", Value: "localhost,127.0.0.1"}, }, @@ -509,7 +509,7 @@ func TestDeploymentNeedsUpdateProxyEnv(t *testing.T) { ProxyPort: 8080, ResourceOverrides: &mcpv1alpha1.ResourceOverrides{ ProxyDeployment: &mcpv1alpha1.ProxyDeploymentOverrides{ - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "HTTP_PROXY", Value: "http://new-proxy.example.com:8080"}, {Name: "NO_PROXY", Value: "localhost,127.0.0.1"}, }, @@ -535,7 +535,7 @@ func TestDeploymentNeedsUpdateProxyEnv(t *testing.T) { ProxyPort: 8080, ResourceOverrides: &mcpv1alpha1.ResourceOverrides{ ProxyDeployment: &mcpv1alpha1.ProxyDeploymentOverrides{ - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "HTTP_PROXY", Value: "http://proxy.example.com:8080"}, {Name: "NO_PROXY", Value: "localhost,127.0.0.1"}, {Name: "CUSTOM_ENV", Value: "custom-value"}, @@ -562,7 +562,7 @@ func TestDeploymentNeedsUpdateProxyEnv(t *testing.T) { ProxyPort: 8080, ResourceOverrides: &mcpv1alpha1.ResourceOverrides{ ProxyDeployment: &mcpv1alpha1.ProxyDeploymentOverrides{ - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "HTTP_PROXY", Value: "http://proxy.example.com:8080"}, }, }, diff --git a/cmd/thv-operator/controllers/mcpserver_runconfig.go b/cmd/thv-operator/controllers/mcpserver_runconfig.go index 45dad52740..80de266065 100644 --- a/cmd/thv-operator/controllers/mcpserver_runconfig.go +++ b/cmd/thv-operator/controllers/mcpserver_runconfig.go @@ -551,7 +551,7 @@ func (*MCPServerReconciler) validateToolsFilter(config *runner.RunConfig) error } // convertEnvVarsFromMCPServer converts MCPServer environment variables to builder format -func convertEnvVarsFromMCPServer(envs []mcpv1alpha1.EnvVar) map[string]string { +func convertEnvVarsFromMCPServer(envs []corev1.EnvVar) map[string]string { if len(envs) == 0 { return nil } diff --git a/cmd/thv-operator/controllers/mcpserver_runconfig_test.go b/cmd/thv-operator/controllers/mcpserver_runconfig_test.go index 8b1220c32c..e65f20acc3 100644 --- a/cmd/thv-operator/controllers/mcpserver_runconfig_test.go +++ b/cmd/thv-operator/controllers/mcpserver_runconfig_test.go @@ -41,7 +41,7 @@ func createRunConfigTestScheme() *runtime.Scheme { return testScheme } -func createTestMCPServerWithConfig(name, namespace, image string, envVars []mcpv1alpha1.EnvVar) *mcpv1alpha1.MCPServer { +func createTestMCPServerWithConfig(name, namespace, image string, envVars []corev1.EnvVar) *mcpv1alpha1.MCPServer { return &mcpv1alpha1.MCPServer{ ObjectMeta: metav1.ObjectMeta{ Name: name, @@ -96,7 +96,7 @@ func TestCreateRunConfigFromMCPServer(t *testing.T) { Image: "env-image:latest", Transport: "sse", ProxyPort: 9090, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "VAR1", Value: "value1"}, {Name: "VAR2", Value: "value2"}, }, @@ -322,7 +322,7 @@ func TestCreateRunConfigFromMCPServer(t *testing.T) { McpPort: 8080, ProxyMode: "streamable-http", Args: []string{"--comprehensive", "--test"}, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "ENV1", Value: "value1"}, {Name: "ENV2", Value: "value2"}, {Name: "EMPTY_VALUE", Value: ""}, @@ -611,7 +611,7 @@ func TestDeterministicConfigMapGeneration(t *testing.T) { ProxyPort: 9090, McpPort: 8080, Args: []string{"--arg1", "--arg2", "--complex-flag=value"}, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "VAR_C", Value: "value_c"}, {Name: "VAR_A", Value: "value_a"}, {Name: "VAR_B", Value: "value_b"}, @@ -1324,7 +1324,7 @@ func TestEnsureRunConfigConfigMapCompleteFlow(t *testing.T) { } // Step 1: Create initial MCPServer and ConfigMap - mcpServer := createTestMCPServerWithConfig("flow-server", "flow-ns", "test:v1", []mcpv1alpha1.EnvVar{ + mcpServer := createTestMCPServerWithConfig("flow-server", "flow-ns", "test:v1", []corev1.EnvVar{ {Name: "ENV1", Value: "value1"}, }) @@ -1355,7 +1355,7 @@ func TestEnsureRunConfigConfigMapCompleteFlow(t *testing.T) { // The checksum will automatically change when content changes mcpServer.Spec.Image = "test:v2" - mcpServer.Spec.Env = []mcpv1alpha1.EnvVar{ + mcpServer.Spec.Env = []corev1.EnvVar{ {Name: "ENV1", Value: "value1"}, {Name: "ENV2", Value: "value2"}, } diff --git a/cmd/thv-operator/test-integration/mcp-server/mcpserver_controller_integration_test.go b/cmd/thv-operator/test-integration/mcp-server/mcpserver_controller_integration_test.go index c5731dec2d..e1766ffca5 100644 --- a/cmd/thv-operator/test-integration/mcp-server/mcpserver_controller_integration_test.go +++ b/cmd/thv-operator/test-integration/mcp-server/mcpserver_controller_integration_test.go @@ -69,7 +69,7 @@ var _ = Describe("MCPServer Controller Integration Tests", func() { ProxyPort: 8080, McpPort: 8080, Args: []string{"--verbose"}, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "DEBUG", Value: "true", @@ -113,7 +113,6 @@ var _ = Describe("MCPServer Controller Integration Tests", func() { }) It("Should create a Deployment with proper configuration", func() { - // Wait for Deployment to be created deployment := &appsv1.Deployment{} Eventually(func() error { @@ -229,11 +228,9 @@ var _ = Describe("MCPServer Controller Integration Tests", func() { Expect(container.ReadinessProbe.ProbeHandler.HTTPGet.Port).To(Equal(intstr.FromString("http"))) Expect(container.ReadinessProbe.InitialDelaySeconds).To(Equal(int32(5))) Expect(container.ReadinessProbe.PeriodSeconds).To(Equal(int32(5))) - }) It("Should create the RunConfig ConfigMap", func() { - // Wait for Service to be created (using the correct naming pattern) configMap := &corev1.ConfigMap{} configMapName := mcpServerName + "-runconfig" @@ -253,7 +250,6 @@ var _ = Describe("MCPServer Controller Integration Tests", func() { }) It("Should create a Service for the MCPServer Proxy", func() { - // Wait for Service to be created (using the correct naming pattern) service := &corev1.Service{} serviceName := "mcp-" + mcpServerName + "-proxy" @@ -271,11 +267,9 @@ var _ = Describe("MCPServer Controller Integration Tests", func() { Expect(service.Spec.Type).To(Equal(corev1.ServiceTypeClusterIP)) Expect(service.Spec.Ports).To(HaveLen(1)) Expect(service.Spec.Ports[0].Port).To(Equal(int32(8080))) - }) It("Should create RBAC resources when ServiceAccount is not specified", func() { - // Wait for ServiceAccount to be created serviceAccountName := mcpServerName + "-proxy-runner" serviceAccount := &corev1.ServiceAccount{} @@ -320,7 +314,6 @@ var _ = Describe("MCPServer Controller Integration Tests", func() { Expect(roleBinding.Subjects).To(HaveLen(1)) Expect(roleBinding.Subjects[0].Name).To(Equal(serviceAccountName)) Expect(roleBinding.RoleRef.Name).To(Equal(serviceAccountName)) - }) It("Should set ObservedGeneration in status after reconciliation", func() { @@ -359,7 +352,6 @@ var _ = Describe("MCPServer Controller Integration Tests", func() { }) It("Should update Deployment when MCPServer spec changes", func() { - // Wait for Deployment to be created deployment := &appsv1.Deployment{} Eventually(func() error { diff --git a/cmd/thv-operator/test-integration/mcp-server/mcpserver_runconfig_integration_test.go b/cmd/thv-operator/test-integration/mcp-server/mcpserver_runconfig_integration_test.go index 223e7fa395..d17f3acee2 100644 --- a/cmd/thv-operator/test-integration/mcp-server/mcpserver_runconfig_integration_test.go +++ b/cmd/thv-operator/test-integration/mcp-server/mcpserver_runconfig_integration_test.go @@ -65,7 +65,7 @@ var _ = Describe("RunConfig ConfigMap Integration Tests", func() { ProxyPort: 8080, McpPort: 8081, Args: []string{"--verbose", "--debug"}, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "DEBUG", Value: "true", @@ -300,7 +300,7 @@ var _ = Describe("RunConfig ConfigMap Integration Tests", func() { // Update multiple fields mcpServer.Spec.Image = "example/mcp-server:v2.0.0" mcpServer.Spec.ProxyPort = 9090 - mcpServer.Spec.Env = append(mcpServer.Spec.Env, mcpv1alpha1.EnvVar{ + mcpServer.Spec.Env = append(mcpServer.Spec.Env, corev1.EnvVar{ Name: "NEW_VAR", Value: "new_value", }) @@ -777,7 +777,7 @@ var _ = Describe("RunConfig ConfigMap Integration Tests", func() { ProxyPort: 9090, McpPort: 8080, Args: []string{"--arg1", "--arg2", "--arg3"}, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "VAR_C", Value: "value_c"}, {Name: "VAR_A", Value: "value_a"}, {Name: "VAR_B", Value: "value_b"}, diff --git a/docs/operator/crd-api.md b/docs/operator/crd-api.md index 31f474a270..7b064241e8 100644 --- a/docs/operator/crd-api.md +++ b/docs/operator/crd-api.md @@ -1196,8 +1196,6 @@ EnvVar represents an environment variable in a container _Appears in:_ - [api.v1alpha1.EmbeddingServerSpec](#apiv1alpha1embeddingserverspec) -- [api.v1alpha1.MCPServerSpec](#apiv1alpha1mcpserverspec) -- [api.v1alpha1.ProxyDeploymentOverrides](#apiv1alpha1proxydeploymentoverrides) | Field | Description | Default | Validation | | --- | --- | --- | --- | @@ -2223,7 +2221,7 @@ _Appears in:_ | `proxyPort` _integer_ | ProxyPort is the port to expose the proxy runner on | 8080 | Maximum: 65535
Minimum: 1
| | `mcpPort` _integer_ | McpPort is the port that MCP server listens to | | Maximum: 65535
Minimum: 1
Optional: \{\}
| | `args` _string array_ | Args are additional arguments to pass to the MCP server | | Optional: \{\}
| -| `env` _[api.v1alpha1.EnvVar](#apiv1alpha1envvar) array_ | Env are environment variables to set in the MCP server container | | Optional: \{\}
| +| `env` _[EnvVar](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#envvar-v1-core) array_ | Env are environment variables to set in the MCP server container | | Optional: \{\}
| | `volumes` _[api.v1alpha1.Volume](#apiv1alpha1volume) array_ | Volumes are volumes to mount in the MCP server container | | Optional: \{\}
| | `resources` _[api.v1alpha1.ResourceRequirements](#apiv1alpha1resourcerequirements)_ | Resources defines the resource requirements for the MCP server container | | Optional: \{\}
| | `secrets` _[api.v1alpha1.SecretRef](#apiv1alpha1secretref) array_ | Secrets are references to secrets to mount in the MCP server container | | Optional: \{\}
| @@ -2772,7 +2770,7 @@ _Appears in:_ | `annotations` _object (keys:string, values:string)_ | Annotations to add or override on the resource | | Optional: \{\}
| | `labels` _object (keys:string, values:string)_ | Labels to add or override on the resource | | Optional: \{\}
| | `podTemplateMetadataOverrides` _[api.v1alpha1.ResourceMetadataOverrides](#apiv1alpha1resourcemetadataoverrides)_ | | | | -| `env` _[api.v1alpha1.EnvVar](#apiv1alpha1envvar) array_ | Env are environment variables to set in the proxy container (thv run process)
These affect the toolhive proxy itself, not the MCP server it manages
Use TOOLHIVE_DEBUG=true to enable debug logging in the proxy | | Optional: \{\}
| +| `env` _[EnvVar](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#envvar-v1-core) array_ | Env are environment variables to set in the proxy container (thv run process)
These affect the toolhive proxy itself, not the MCP server it manages
Use TOOLHIVE_DEBUG=true to enable debug logging in the proxy | | Optional: \{\}
| | `imagePullSecrets` _[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#localobjectreference-v1-core) array_ | ImagePullSecrets allows specifying image pull secrets for the proxy runner
These are applied to both the Deployment and the ServiceAccount | | Optional: \{\}
| diff --git a/pkg/export/k8s.go b/pkg/export/k8s.go index 5bfe928441..80ddf217dc 100644 --- a/pkg/export/k8s.go +++ b/pkg/export/k8s.go @@ -10,6 +10,7 @@ import ( "io" "strings" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/yaml" @@ -94,9 +95,9 @@ func runConfigToMCPServer(config *runner.RunConfig) (*v1alpha1.MCPServer, error) // Convert environment variables if len(config.EnvVars) > 0 { - mcpServer.Spec.Env = make([]v1alpha1.EnvVar, 0, len(config.EnvVars)) + mcpServer.Spec.Env = make([]corev1.EnvVar, 0, len(config.EnvVars)) for key, value := range config.EnvVars { - mcpServer.Spec.Env = append(mcpServer.Spec.Env, v1alpha1.EnvVar{ + mcpServer.Spec.Env = append(mcpServer.Spec.Env, corev1.EnvVar{ Name: key, Value: value, }) diff --git a/test/e2e/thv-operator/virtualmcp/helpers.go b/test/e2e/thv-operator/virtualmcp/helpers.go index 337e232f7e..eb741088d4 100644 --- a/test/e2e/thv-operator/virtualmcp/helpers.go +++ b/test/e2e/thv-operator/virtualmcp/helpers.go @@ -772,7 +772,7 @@ func CreateMCPServerAndWait( ProxyPort: 8080, McpPort: 8080, Resources: defaultMCPServerResources(), - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, }, @@ -806,7 +806,7 @@ type BackendConfig struct { Transport string // defaults to "streamable-http" if empty ExternalAuthConfigRef *mcpv1alpha1.ExternalAuthConfigRef Secrets []mcpv1alpha1.SecretRef - Env []mcpv1alpha1.EnvVar // additional env vars beyond TRANSPORT + Env []corev1.EnvVar // additional env vars beyond TRANSPORT // Resources overrides the default resource requests/limits. When nil, // defaultMCPServerResources() is used to ensure containers are scheduled // with reasonable resource guarantees and do not compete excessively. @@ -864,7 +864,7 @@ func CreateMultipleMCPServersInParallel( ExternalAuthConfigRef: backends[idx].ExternalAuthConfigRef, Secrets: backends[idx].Secrets, Resources: resources, - Env: append([]mcpv1alpha1.EnvVar{ + Env: append([]corev1.EnvVar{ {Name: "TRANSPORT", Value: backendTransport}, }, backends[idx].Env...), }, diff --git a/test/e2e/thv-operator/virtualmcp/virtualmcp_auth_discovery_test.go b/test/e2e/thv-operator/virtualmcp/virtualmcp_auth_discovery_test.go index 2206336d92..1d20530b3b 100644 --- a/test/e2e/thv-operator/virtualmcp/virtualmcp_auth_discovery_test.go +++ b/test/e2e/thv-operator/virtualmcp/virtualmcp_auth_discovery_test.go @@ -1454,7 +1454,7 @@ var _ = Describe("Auth Config Error Handling", Ordered, func() { ExternalAuthConfigRef: &mcpv1alpha1.ExternalAuthConfigRef{ Name: workingAuthConfigName, }, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, }, @@ -1477,7 +1477,7 @@ var _ = Describe("Auth Config Error Handling", Ordered, func() { ExternalAuthConfigRef: &mcpv1alpha1.ExternalAuthConfigRef{ Name: missingAuthConfigName, }, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, }, diff --git a/test/e2e/thv-operator/virtualmcp/virtualmcp_circuit_breaker_test.go b/test/e2e/thv-operator/virtualmcp/virtualmcp_circuit_breaker_test.go index ce34ae8882..2440401298 100644 --- a/test/e2e/thv-operator/virtualmcp/virtualmcp_circuit_breaker_test.go +++ b/test/e2e/thv-operator/virtualmcp/virtualmcp_circuit_breaker_test.go @@ -57,7 +57,7 @@ var _ = Describe("VirtualMCPServer Circuit Breaker Lifecycle", Ordered, func() { Transport: "streamable-http", ProxyPort: 8080, McpPort: 8080, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, }, @@ -76,7 +76,7 @@ var _ = Describe("VirtualMCPServer Circuit Breaker Lifecycle", Ordered, func() { Transport: "streamable-http", ProxyPort: 8080, McpPort: 8080, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, }, diff --git a/test/e2e/thv-operator/virtualmcp/virtualmcp_external_auth_test.go b/test/e2e/thv-operator/virtualmcp/virtualmcp_external_auth_test.go index fb5f4cbfb5..0ddd1e3e2d 100644 --- a/test/e2e/thv-operator/virtualmcp/virtualmcp_external_auth_test.go +++ b/test/e2e/thv-operator/virtualmcp/virtualmcp_external_auth_test.go @@ -835,7 +835,7 @@ var _ = Describe("VirtualMCPServer Health Check with HeaderInjection Auth", Orde Transport: "streamable-http", ProxyPort: 8080, McpPort: 8080, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, ExternalAuthConfigRef: &mcpv1alpha1.ExternalAuthConfigRef{ @@ -1051,7 +1051,7 @@ var _ = Describe("VirtualMCPServer Health Check with TokenExchange Auth", Ordere Transport: "streamable-http", ProxyPort: 8080, McpPort: 8080, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, ExternalAuthConfigRef: &mcpv1alpha1.ExternalAuthConfigRef{ diff --git a/test/e2e/thv-operator/virtualmcp/virtualmcp_optimizer_multibackend_test.go b/test/e2e/thv-operator/virtualmcp/virtualmcp_optimizer_multibackend_test.go index 5b2f72a09f..b5bd9d3d08 100644 --- a/test/e2e/thv-operator/virtualmcp/virtualmcp_optimizer_multibackend_test.go +++ b/test/e2e/thv-operator/virtualmcp/virtualmcp_optimizer_multibackend_test.go @@ -91,7 +91,7 @@ var _ = Describe("VirtualMCPServer Optimizer Multi-Backend", Ordered, func() { Name: backend5Name, Namespace: testNamespace, GroupRef: mcpGroupName, Image: images.TerraformMCPServerImage, // 9 tools Transport: "streamable-http", - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT_MODE", Value: "streamable-http"}, {Name: "TRANSPORT_HOST", Value: "0.0.0.0"}, }, diff --git a/test/e2e/thv-operator/virtualmcp/virtualmcp_telemetry_test.go b/test/e2e/thv-operator/virtualmcp/virtualmcp_telemetry_test.go index 4f8b5f377d..2766f884c1 100644 --- a/test/e2e/thv-operator/virtualmcp/virtualmcp_telemetry_test.go +++ b/test/e2e/thv-operator/virtualmcp/virtualmcp_telemetry_test.go @@ -47,7 +47,7 @@ var _ = Describe("VirtualMCPServer Telemetry Config", Ordered, func() { Transport: "streamable-http", ProxyPort: 8080, McpPort: 8080, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, }, diff --git a/test/e2e/thv-operator/virtualmcp/virtualmcp_yardstick_base_test.go b/test/e2e/thv-operator/virtualmcp/virtualmcp_yardstick_base_test.go index 7c375bd1c8..b238f2d441 100644 --- a/test/e2e/thv-operator/virtualmcp/virtualmcp_yardstick_base_test.go +++ b/test/e2e/thv-operator/virtualmcp/virtualmcp_yardstick_base_test.go @@ -13,6 +13,7 @@ import ( "github.com/mark3labs/mcp-go/mcp" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -51,7 +52,7 @@ var _ = Describe("VirtualMCPServer Yardstick Base", Ordered, func() { Transport: "streamable-http", ProxyPort: 8080, McpPort: 8080, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, }, @@ -69,7 +70,7 @@ var _ = Describe("VirtualMCPServer Yardstick Base", Ordered, func() { Transport: "streamable-http", ProxyPort: 8080, McpPort: 8080, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, }, From b29cd5ee9ddccab2540eeacc3f77e347206935bb Mon Sep 17 00:00:00 2001 From: Anvesh Thakur Date: Tue, 7 Apr 2026 10:44:33 +0530 Subject: [PATCH 2/4] updates controller code --- .../controllers/mcpserver_controller.go | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/cmd/thv-operator/controllers/mcpserver_controller.go b/cmd/thv-operator/controllers/mcpserver_controller.go index 6cf0dc4336..7c91767a08 100644 --- a/cmd/thv-operator/controllers/mcpserver_controller.go +++ b/cmd/thv-operator/controllers/mcpserver_controller.go @@ -1139,12 +1139,7 @@ func (r *MCPServerReconciler) deploymentForMCPServer( // Add user-specified proxy environment variables from ResourceOverrides if m.Spec.ResourceOverrides != nil && m.Spec.ResourceOverrides.ProxyDeployment != nil { - for _, envVar := range m.Spec.ResourceOverrides.ProxyDeployment.Env { - env = append(env, corev1.EnvVar{ - Name: envVar.Name, - Value: envVar.Value, - }) - } + env = append(env, m.Spec.ResourceOverrides.ProxyDeployment.Env...) } // Add volume mounts for user-defined volumes @@ -1731,12 +1726,7 @@ func (r *MCPServerReconciler) deploymentNeedsUpdate( // Add user-specified environment variables if mcpServer.Spec.ResourceOverrides != nil && mcpServer.Spec.ResourceOverrides.ProxyDeployment != nil { - for _, envVar := range mcpServer.Spec.ResourceOverrides.ProxyDeployment.Env { - expectedProxyEnv = append(expectedProxyEnv, corev1.EnvVar{ - Name: envVar.Name, - Value: envVar.Value, - }) - } + expectedProxyEnv = append(expectedProxyEnv, mcpServer.Spec.ResourceOverrides.ProxyDeployment.Env...) } // Add default environment variables that are always injected expectedProxyEnv = ctrlutil.EnsureRequiredEnvVars(ctx, expectedProxyEnv) From 7a502e8fb765701bb84860fb70847b4a0ae89d6b Mon Sep 17 00:00:00 2001 From: Anvesh Thakur Date: Mon, 6 Apr 2026 13:29:50 +0530 Subject: [PATCH 3/4] Replace custom EnvVar with corev1.EnvVar --- cmd/thv-operator/api/v1alpha1/mcpserver_types.go | 4 ++-- .../api/v1alpha1/zz_generated.deepcopy.go | 6 +++--- .../controllers/mcpremoteproxy_deployment_test.go | 2 +- .../mcpserver_resource_overrides_test.go | 14 +++++++------- .../controllers/mcpserver_runconfig.go | 2 +- .../controllers/mcpserver_runconfig_test.go | 12 ++++++------ .../mcpserver_controller_integration_test.go | 10 +--------- .../mcpserver_runconfig_integration_test.go | 6 +++--- docs/operator/crd-api.md | 6 ++---- pkg/export/k8s.go | 5 +++-- test/e2e/thv-operator/virtualmcp/helpers.go | 6 +++--- .../virtualmcp/virtualmcp_auth_discovery_test.go | 4 ++-- .../virtualmcp/virtualmcp_circuit_breaker_test.go | 4 ++-- .../virtualmcp/virtualmcp_external_auth_test.go | 4 ++-- .../virtualmcp_optimizer_multibackend_test.go | 2 +- .../virtualmcp/virtualmcp_telemetry_test.go | 2 +- .../virtualmcp/virtualmcp_yardstick_base_test.go | 5 +++-- 17 files changed, 43 insertions(+), 51 deletions(-) diff --git a/cmd/thv-operator/api/v1alpha1/mcpserver_types.go b/cmd/thv-operator/api/v1alpha1/mcpserver_types.go index dd665f4250..f78d3761a7 100644 --- a/cmd/thv-operator/api/v1alpha1/mcpserver_types.go +++ b/cmd/thv-operator/api/v1alpha1/mcpserver_types.go @@ -197,7 +197,7 @@ type MCPServerSpec struct { // +listType=map // +listMapKey=name // +optional - Env []EnvVar `json:"env,omitempty"` + Env []corev1.EnvVar `json:"env,omitempty"` // Volumes are volumes to mount in the MCP server container // +listType=map @@ -363,7 +363,7 @@ type ProxyDeploymentOverrides struct { // +listType=map // +listMapKey=name // +optional - Env []EnvVar `json:"env,omitempty"` + Env []corev1.EnvVar `json:"env,omitempty"` // ImagePullSecrets allows specifying image pull secrets for the proxy runner // These are applied to both the Deployment and the ServiceAccount diff --git a/cmd/thv-operator/api/v1alpha1/zz_generated.deepcopy.go b/cmd/thv-operator/api/v1alpha1/zz_generated.deepcopy.go index 83408b7eb8..bb03f654ae 100644 --- a/cmd/thv-operator/api/v1alpha1/zz_generated.deepcopy.go +++ b/cmd/thv-operator/api/v1alpha1/zz_generated.deepcopy.go @@ -23,7 +23,7 @@ package v1alpha1 import ( corev1 "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -1831,7 +1831,7 @@ func (in *MCPServerSpec) DeepCopyInto(out *MCPServerSpec) { } if in.Env != nil { in, out := &in.Env, &out.Env - *out = make([]EnvVar, len(*in)) + *out = make([]corev1.EnvVar, len(*in)) copy(*out, *in) } if in.Volumes != nil { @@ -2605,7 +2605,7 @@ func (in *ProxyDeploymentOverrides) DeepCopyInto(out *ProxyDeploymentOverrides) } if in.Env != nil { in, out := &in.Env, &out.Env - *out = make([]EnvVar, len(*in)) + *out = make([]corev1.EnvVar, len(*in)) copy(*out, *in) } if in.ImagePullSecrets != nil { diff --git a/cmd/thv-operator/controllers/mcpremoteproxy_deployment_test.go b/cmd/thv-operator/controllers/mcpremoteproxy_deployment_test.go index f4105195c6..482b19cc40 100644 --- a/cmd/thv-operator/controllers/mcpremoteproxy_deployment_test.go +++ b/cmd/thv-operator/controllers/mcpremoteproxy_deployment_test.go @@ -136,7 +136,7 @@ func TestDeploymentForMCPRemoteProxy(t *testing.T) { "custom-annotation": "custom-annotation-value", }, }, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "CUSTOM_ENV", Value: "custom-value"}, {Name: "TOOLHIVE_DEBUG", Value: "true"}, }, diff --git a/cmd/thv-operator/controllers/mcpserver_resource_overrides_test.go b/cmd/thv-operator/controllers/mcpserver_resource_overrides_test.go index ba6e516752..9f8d1fb61b 100644 --- a/cmd/thv-operator/controllers/mcpserver_resource_overrides_test.go +++ b/cmd/thv-operator/controllers/mcpserver_resource_overrides_test.go @@ -155,7 +155,7 @@ func TestResourceOverrides(t *testing.T) { "environment": "test", }, }, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "HTTP_PROXY", Value: "http://proxy.example.com:8080", @@ -203,7 +203,7 @@ func TestResourceOverrides(t *testing.T) { ProxyPort: 8080, ResourceOverrides: &mcpv1alpha1.ResourceOverrides{ ProxyDeployment: &mcpv1alpha1.ProxyDeploymentOverrides{ - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TOOLHIVE_DEBUG", Value: "true"}, }, }, @@ -249,7 +249,7 @@ func TestResourceOverrides(t *testing.T) { "version": "v1.2.3", }, }, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "LOG_LEVEL", Value: "debug", @@ -483,7 +483,7 @@ func TestDeploymentNeedsUpdateProxyEnv(t *testing.T) { ProxyPort: 8080, ResourceOverrides: &mcpv1alpha1.ResourceOverrides{ ProxyDeployment: &mcpv1alpha1.ProxyDeploymentOverrides{ - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "HTTP_PROXY", Value: "http://proxy.example.com:8080"}, {Name: "NO_PROXY", Value: "localhost,127.0.0.1"}, }, @@ -509,7 +509,7 @@ func TestDeploymentNeedsUpdateProxyEnv(t *testing.T) { ProxyPort: 8080, ResourceOverrides: &mcpv1alpha1.ResourceOverrides{ ProxyDeployment: &mcpv1alpha1.ProxyDeploymentOverrides{ - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "HTTP_PROXY", Value: "http://new-proxy.example.com:8080"}, {Name: "NO_PROXY", Value: "localhost,127.0.0.1"}, }, @@ -535,7 +535,7 @@ func TestDeploymentNeedsUpdateProxyEnv(t *testing.T) { ProxyPort: 8080, ResourceOverrides: &mcpv1alpha1.ResourceOverrides{ ProxyDeployment: &mcpv1alpha1.ProxyDeploymentOverrides{ - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "HTTP_PROXY", Value: "http://proxy.example.com:8080"}, {Name: "NO_PROXY", Value: "localhost,127.0.0.1"}, {Name: "CUSTOM_ENV", Value: "custom-value"}, @@ -562,7 +562,7 @@ func TestDeploymentNeedsUpdateProxyEnv(t *testing.T) { ProxyPort: 8080, ResourceOverrides: &mcpv1alpha1.ResourceOverrides{ ProxyDeployment: &mcpv1alpha1.ProxyDeploymentOverrides{ - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "HTTP_PROXY", Value: "http://proxy.example.com:8080"}, }, }, diff --git a/cmd/thv-operator/controllers/mcpserver_runconfig.go b/cmd/thv-operator/controllers/mcpserver_runconfig.go index 03fd6dc8d3..ae8c565509 100644 --- a/cmd/thv-operator/controllers/mcpserver_runconfig.go +++ b/cmd/thv-operator/controllers/mcpserver_runconfig.go @@ -558,7 +558,7 @@ func (*MCPServerReconciler) validateToolsFilter(config *runner.RunConfig) error } // convertEnvVarsFromMCPServer converts MCPServer environment variables to builder format -func convertEnvVarsFromMCPServer(envs []mcpv1alpha1.EnvVar) map[string]string { +func convertEnvVarsFromMCPServer(envs []corev1.EnvVar) map[string]string { if len(envs) == 0 { return nil } diff --git a/cmd/thv-operator/controllers/mcpserver_runconfig_test.go b/cmd/thv-operator/controllers/mcpserver_runconfig_test.go index 02492257de..bf588310fe 100644 --- a/cmd/thv-operator/controllers/mcpserver_runconfig_test.go +++ b/cmd/thv-operator/controllers/mcpserver_runconfig_test.go @@ -41,7 +41,7 @@ func createRunConfigTestScheme() *runtime.Scheme { return testScheme } -func createTestMCPServerWithConfig(name, namespace, image string, envVars []mcpv1alpha1.EnvVar) *mcpv1alpha1.MCPServer { +func createTestMCPServerWithConfig(name, namespace, image string, envVars []corev1.EnvVar) *mcpv1alpha1.MCPServer { return &mcpv1alpha1.MCPServer{ ObjectMeta: metav1.ObjectMeta{ Name: name, @@ -96,7 +96,7 @@ func TestCreateRunConfigFromMCPServer(t *testing.T) { Image: "env-image:latest", Transport: "sse", ProxyPort: 9090, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "VAR1", Value: "value1"}, {Name: "VAR2", Value: "value2"}, }, @@ -322,7 +322,7 @@ func TestCreateRunConfigFromMCPServer(t *testing.T) { McpPort: 8080, ProxyMode: "streamable-http", Args: []string{"--comprehensive", "--test"}, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "ENV1", Value: "value1"}, {Name: "ENV2", Value: "value2"}, {Name: "EMPTY_VALUE", Value: ""}, @@ -611,7 +611,7 @@ func TestDeterministicConfigMapGeneration(t *testing.T) { ProxyPort: 9090, McpPort: 8080, Args: []string{"--arg1", "--arg2", "--complex-flag=value"}, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "VAR_C", Value: "value_c"}, {Name: "VAR_A", Value: "value_a"}, {Name: "VAR_B", Value: "value_b"}, @@ -1324,7 +1324,7 @@ func TestEnsureRunConfigConfigMapCompleteFlow(t *testing.T) { } // Step 1: Create initial MCPServer and ConfigMap - mcpServer := createTestMCPServerWithConfig("flow-server", "flow-ns", "test:v1", []mcpv1alpha1.EnvVar{ + mcpServer := createTestMCPServerWithConfig("flow-server", "flow-ns", "test:v1", []corev1.EnvVar{ {Name: "ENV1", Value: "value1"}, }) @@ -1355,7 +1355,7 @@ func TestEnsureRunConfigConfigMapCompleteFlow(t *testing.T) { // The checksum will automatically change when content changes mcpServer.Spec.Image = "test:v2" - mcpServer.Spec.Env = []mcpv1alpha1.EnvVar{ + mcpServer.Spec.Env = []corev1.EnvVar{ {Name: "ENV1", Value: "value1"}, {Name: "ENV2", Value: "value2"}, } diff --git a/cmd/thv-operator/test-integration/mcp-server/mcpserver_controller_integration_test.go b/cmd/thv-operator/test-integration/mcp-server/mcpserver_controller_integration_test.go index c5731dec2d..e1766ffca5 100644 --- a/cmd/thv-operator/test-integration/mcp-server/mcpserver_controller_integration_test.go +++ b/cmd/thv-operator/test-integration/mcp-server/mcpserver_controller_integration_test.go @@ -69,7 +69,7 @@ var _ = Describe("MCPServer Controller Integration Tests", func() { ProxyPort: 8080, McpPort: 8080, Args: []string{"--verbose"}, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "DEBUG", Value: "true", @@ -113,7 +113,6 @@ var _ = Describe("MCPServer Controller Integration Tests", func() { }) It("Should create a Deployment with proper configuration", func() { - // Wait for Deployment to be created deployment := &appsv1.Deployment{} Eventually(func() error { @@ -229,11 +228,9 @@ var _ = Describe("MCPServer Controller Integration Tests", func() { Expect(container.ReadinessProbe.ProbeHandler.HTTPGet.Port).To(Equal(intstr.FromString("http"))) Expect(container.ReadinessProbe.InitialDelaySeconds).To(Equal(int32(5))) Expect(container.ReadinessProbe.PeriodSeconds).To(Equal(int32(5))) - }) It("Should create the RunConfig ConfigMap", func() { - // Wait for Service to be created (using the correct naming pattern) configMap := &corev1.ConfigMap{} configMapName := mcpServerName + "-runconfig" @@ -253,7 +250,6 @@ var _ = Describe("MCPServer Controller Integration Tests", func() { }) It("Should create a Service for the MCPServer Proxy", func() { - // Wait for Service to be created (using the correct naming pattern) service := &corev1.Service{} serviceName := "mcp-" + mcpServerName + "-proxy" @@ -271,11 +267,9 @@ var _ = Describe("MCPServer Controller Integration Tests", func() { Expect(service.Spec.Type).To(Equal(corev1.ServiceTypeClusterIP)) Expect(service.Spec.Ports).To(HaveLen(1)) Expect(service.Spec.Ports[0].Port).To(Equal(int32(8080))) - }) It("Should create RBAC resources when ServiceAccount is not specified", func() { - // Wait for ServiceAccount to be created serviceAccountName := mcpServerName + "-proxy-runner" serviceAccount := &corev1.ServiceAccount{} @@ -320,7 +314,6 @@ var _ = Describe("MCPServer Controller Integration Tests", func() { Expect(roleBinding.Subjects).To(HaveLen(1)) Expect(roleBinding.Subjects[0].Name).To(Equal(serviceAccountName)) Expect(roleBinding.RoleRef.Name).To(Equal(serviceAccountName)) - }) It("Should set ObservedGeneration in status after reconciliation", func() { @@ -359,7 +352,6 @@ var _ = Describe("MCPServer Controller Integration Tests", func() { }) It("Should update Deployment when MCPServer spec changes", func() { - // Wait for Deployment to be created deployment := &appsv1.Deployment{} Eventually(func() error { diff --git a/cmd/thv-operator/test-integration/mcp-server/mcpserver_runconfig_integration_test.go b/cmd/thv-operator/test-integration/mcp-server/mcpserver_runconfig_integration_test.go index 223e7fa395..d17f3acee2 100644 --- a/cmd/thv-operator/test-integration/mcp-server/mcpserver_runconfig_integration_test.go +++ b/cmd/thv-operator/test-integration/mcp-server/mcpserver_runconfig_integration_test.go @@ -65,7 +65,7 @@ var _ = Describe("RunConfig ConfigMap Integration Tests", func() { ProxyPort: 8080, McpPort: 8081, Args: []string{"--verbose", "--debug"}, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ { Name: "DEBUG", Value: "true", @@ -300,7 +300,7 @@ var _ = Describe("RunConfig ConfigMap Integration Tests", func() { // Update multiple fields mcpServer.Spec.Image = "example/mcp-server:v2.0.0" mcpServer.Spec.ProxyPort = 9090 - mcpServer.Spec.Env = append(mcpServer.Spec.Env, mcpv1alpha1.EnvVar{ + mcpServer.Spec.Env = append(mcpServer.Spec.Env, corev1.EnvVar{ Name: "NEW_VAR", Value: "new_value", }) @@ -777,7 +777,7 @@ var _ = Describe("RunConfig ConfigMap Integration Tests", func() { ProxyPort: 9090, McpPort: 8080, Args: []string{"--arg1", "--arg2", "--arg3"}, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "VAR_C", Value: "value_c"}, {Name: "VAR_A", Value: "value_a"}, {Name: "VAR_B", Value: "value_b"}, diff --git a/docs/operator/crd-api.md b/docs/operator/crd-api.md index b9f5a49f18..31888ec148 100644 --- a/docs/operator/crd-api.md +++ b/docs/operator/crd-api.md @@ -1158,8 +1158,6 @@ EnvVar represents an environment variable in a container _Appears in:_ - [api.v1alpha1.EmbeddingServerSpec](#apiv1alpha1embeddingserverspec) -- [api.v1alpha1.MCPServerSpec](#apiv1alpha1mcpserverspec) -- [api.v1alpha1.ProxyDeploymentOverrides](#apiv1alpha1proxydeploymentoverrides) | Field | Description | Default | Validation | | --- | --- | --- | --- | @@ -2349,7 +2347,7 @@ _Appears in:_ | `proxyPort` _integer_ | ProxyPort is the port to expose the proxy runner on | 8080 | Maximum: 65535
Minimum: 1
| | `mcpPort` _integer_ | McpPort is the port that MCP server listens to | | Maximum: 65535
Minimum: 1
Optional: \{\}
| | `args` _string array_ | Args are additional arguments to pass to the MCP server | | Optional: \{\}
| -| `env` _[api.v1alpha1.EnvVar](#apiv1alpha1envvar) array_ | Env are environment variables to set in the MCP server container | | Optional: \{\}
| +| `env` _[EnvVar](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#envvar-v1-core) array_ | Env are environment variables to set in the MCP server container | | Optional: \{\}
| | `volumes` _[api.v1alpha1.Volume](#apiv1alpha1volume) array_ | Volumes are volumes to mount in the MCP server container | | Optional: \{\}
| | `resources` _[api.v1alpha1.ResourceRequirements](#apiv1alpha1resourcerequirements)_ | Resources defines the resource requirements for the MCP server container | | Optional: \{\}
| | `secrets` _[api.v1alpha1.SecretRef](#apiv1alpha1secretref) array_ | Secrets are references to secrets to mount in the MCP server container | | Optional: \{\}
| @@ -2897,7 +2895,7 @@ _Appears in:_ | `annotations` _object (keys:string, values:string)_ | Annotations to add or override on the resource | | Optional: \{\}
| | `labels` _object (keys:string, values:string)_ | Labels to add or override on the resource | | Optional: \{\}
| | `podTemplateMetadataOverrides` _[api.v1alpha1.ResourceMetadataOverrides](#apiv1alpha1resourcemetadataoverrides)_ | | | | -| `env` _[api.v1alpha1.EnvVar](#apiv1alpha1envvar) array_ | Env are environment variables to set in the proxy container (thv run process)
These affect the toolhive proxy itself, not the MCP server it manages
Use TOOLHIVE_DEBUG=true to enable debug logging in the proxy | | Optional: \{\}
| +| `env` _[EnvVar](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#envvar-v1-core) array_ | Env are environment variables to set in the proxy container (thv run process)
These affect the toolhive proxy itself, not the MCP server it manages
Use TOOLHIVE_DEBUG=true to enable debug logging in the proxy | | Optional: \{\}
| | `imagePullSecrets` _[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#localobjectreference-v1-core) array_ | ImagePullSecrets allows specifying image pull secrets for the proxy runner
These are applied to both the Deployment and the ServiceAccount | | Optional: \{\}
| diff --git a/pkg/export/k8s.go b/pkg/export/k8s.go index 5bfe928441..80ddf217dc 100644 --- a/pkg/export/k8s.go +++ b/pkg/export/k8s.go @@ -10,6 +10,7 @@ import ( "io" "strings" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/yaml" @@ -94,9 +95,9 @@ func runConfigToMCPServer(config *runner.RunConfig) (*v1alpha1.MCPServer, error) // Convert environment variables if len(config.EnvVars) > 0 { - mcpServer.Spec.Env = make([]v1alpha1.EnvVar, 0, len(config.EnvVars)) + mcpServer.Spec.Env = make([]corev1.EnvVar, 0, len(config.EnvVars)) for key, value := range config.EnvVars { - mcpServer.Spec.Env = append(mcpServer.Spec.Env, v1alpha1.EnvVar{ + mcpServer.Spec.Env = append(mcpServer.Spec.Env, corev1.EnvVar{ Name: key, Value: value, }) diff --git a/test/e2e/thv-operator/virtualmcp/helpers.go b/test/e2e/thv-operator/virtualmcp/helpers.go index 7f3e1c6dba..83e80ee63b 100644 --- a/test/e2e/thv-operator/virtualmcp/helpers.go +++ b/test/e2e/thv-operator/virtualmcp/helpers.go @@ -673,7 +673,7 @@ func CreateMCPServerAndWait( ProxyPort: 8080, McpPort: 8080, Resources: defaultMCPServerResources(), - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, }, @@ -707,7 +707,7 @@ type BackendConfig struct { Transport string // defaults to "streamable-http" if empty ExternalAuthConfigRef *mcpv1alpha1.ExternalAuthConfigRef Secrets []mcpv1alpha1.SecretRef - Env []mcpv1alpha1.EnvVar // additional env vars beyond TRANSPORT + Env []corev1.EnvVar // additional env vars beyond TRANSPORT // Resources overrides the default resource requests/limits. When nil, // defaultMCPServerResources() is used to ensure containers are scheduled // with reasonable resource guarantees and do not compete excessively. @@ -765,7 +765,7 @@ func CreateMultipleMCPServersInParallel( ExternalAuthConfigRef: backends[idx].ExternalAuthConfigRef, Secrets: backends[idx].Secrets, Resources: resources, - Env: append([]mcpv1alpha1.EnvVar{ + Env: append([]corev1.EnvVar{ {Name: "TRANSPORT", Value: backendTransport}, }, backends[idx].Env...), }, diff --git a/test/e2e/thv-operator/virtualmcp/virtualmcp_auth_discovery_test.go b/test/e2e/thv-operator/virtualmcp/virtualmcp_auth_discovery_test.go index 8c74fe95cb..b1cdd873c4 100644 --- a/test/e2e/thv-operator/virtualmcp/virtualmcp_auth_discovery_test.go +++ b/test/e2e/thv-operator/virtualmcp/virtualmcp_auth_discovery_test.go @@ -1454,7 +1454,7 @@ var _ = Describe("Auth Config Error Handling", Ordered, func() { ExternalAuthConfigRef: &mcpv1alpha1.ExternalAuthConfigRef{ Name: workingAuthConfigName, }, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, }, @@ -1477,7 +1477,7 @@ var _ = Describe("Auth Config Error Handling", Ordered, func() { ExternalAuthConfigRef: &mcpv1alpha1.ExternalAuthConfigRef{ Name: missingAuthConfigName, }, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, }, diff --git a/test/e2e/thv-operator/virtualmcp/virtualmcp_circuit_breaker_test.go b/test/e2e/thv-operator/virtualmcp/virtualmcp_circuit_breaker_test.go index 94b427ab6b..5d8bb5fe32 100644 --- a/test/e2e/thv-operator/virtualmcp/virtualmcp_circuit_breaker_test.go +++ b/test/e2e/thv-operator/virtualmcp/virtualmcp_circuit_breaker_test.go @@ -57,7 +57,7 @@ var _ = Describe("VirtualMCPServer Circuit Breaker Lifecycle", Ordered, func() { Transport: "streamable-http", ProxyPort: 8080, McpPort: 8080, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, }, @@ -76,7 +76,7 @@ var _ = Describe("VirtualMCPServer Circuit Breaker Lifecycle", Ordered, func() { Transport: "streamable-http", ProxyPort: 8080, McpPort: 8080, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, }, diff --git a/test/e2e/thv-operator/virtualmcp/virtualmcp_external_auth_test.go b/test/e2e/thv-operator/virtualmcp/virtualmcp_external_auth_test.go index e05c2f6c18..1a97e9d7f9 100644 --- a/test/e2e/thv-operator/virtualmcp/virtualmcp_external_auth_test.go +++ b/test/e2e/thv-operator/virtualmcp/virtualmcp_external_auth_test.go @@ -835,7 +835,7 @@ var _ = Describe("VirtualMCPServer Health Check with HeaderInjection Auth", Orde Transport: "streamable-http", ProxyPort: 8080, McpPort: 8080, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, ExternalAuthConfigRef: &mcpv1alpha1.ExternalAuthConfigRef{ @@ -1051,7 +1051,7 @@ var _ = Describe("VirtualMCPServer Health Check with TokenExchange Auth", Ordere Transport: "streamable-http", ProxyPort: 8080, McpPort: 8080, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, ExternalAuthConfigRef: &mcpv1alpha1.ExternalAuthConfigRef{ diff --git a/test/e2e/thv-operator/virtualmcp/virtualmcp_optimizer_multibackend_test.go b/test/e2e/thv-operator/virtualmcp/virtualmcp_optimizer_multibackend_test.go index 5b2f72a09f..b5bd9d3d08 100644 --- a/test/e2e/thv-operator/virtualmcp/virtualmcp_optimizer_multibackend_test.go +++ b/test/e2e/thv-operator/virtualmcp/virtualmcp_optimizer_multibackend_test.go @@ -91,7 +91,7 @@ var _ = Describe("VirtualMCPServer Optimizer Multi-Backend", Ordered, func() { Name: backend5Name, Namespace: testNamespace, GroupRef: mcpGroupName, Image: images.TerraformMCPServerImage, // 9 tools Transport: "streamable-http", - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT_MODE", Value: "streamable-http"}, {Name: "TRANSPORT_HOST", Value: "0.0.0.0"}, }, diff --git a/test/e2e/thv-operator/virtualmcp/virtualmcp_telemetry_test.go b/test/e2e/thv-operator/virtualmcp/virtualmcp_telemetry_test.go index 42c3e59936..8519fdd8bf 100644 --- a/test/e2e/thv-operator/virtualmcp/virtualmcp_telemetry_test.go +++ b/test/e2e/thv-operator/virtualmcp/virtualmcp_telemetry_test.go @@ -47,7 +47,7 @@ var _ = Describe("VirtualMCPServer Telemetry Config", Ordered, func() { Transport: "streamable-http", ProxyPort: 8080, McpPort: 8080, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, }, diff --git a/test/e2e/thv-operator/virtualmcp/virtualmcp_yardstick_base_test.go b/test/e2e/thv-operator/virtualmcp/virtualmcp_yardstick_base_test.go index dee37216d4..63f43c40d7 100644 --- a/test/e2e/thv-operator/virtualmcp/virtualmcp_yardstick_base_test.go +++ b/test/e2e/thv-operator/virtualmcp/virtualmcp_yardstick_base_test.go @@ -13,6 +13,7 @@ import ( "github.com/mark3labs/mcp-go/mcp" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -51,7 +52,7 @@ var _ = Describe("VirtualMCPServer Yardstick Base", Ordered, func() { Transport: "streamable-http", ProxyPort: 8080, McpPort: 8080, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, }, @@ -69,7 +70,7 @@ var _ = Describe("VirtualMCPServer Yardstick Base", Ordered, func() { Transport: "streamable-http", ProxyPort: 8080, McpPort: 8080, - Env: []mcpv1alpha1.EnvVar{ + Env: []corev1.EnvVar{ {Name: "TRANSPORT", Value: "streamable-http"}, }, }, From c1388bcb4f2858afa31d35abd4d5ed898d6c8d88 Mon Sep 17 00:00:00 2001 From: Anvesh Thakur Date: Tue, 7 Apr 2026 10:44:33 +0530 Subject: [PATCH 4/4] updates controller code --- .../controllers/mcpserver_controller.go | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/cmd/thv-operator/controllers/mcpserver_controller.go b/cmd/thv-operator/controllers/mcpserver_controller.go index 29ddff9b2d..76d7fc56c0 100644 --- a/cmd/thv-operator/controllers/mcpserver_controller.go +++ b/cmd/thv-operator/controllers/mcpserver_controller.go @@ -1140,12 +1140,7 @@ func (r *MCPServerReconciler) deploymentForMCPServer( // Add user-specified proxy environment variables from ResourceOverrides if m.Spec.ResourceOverrides != nil && m.Spec.ResourceOverrides.ProxyDeployment != nil { - for _, envVar := range m.Spec.ResourceOverrides.ProxyDeployment.Env { - env = append(env, corev1.EnvVar{ - Name: envVar.Name, - Value: envVar.Value, - }) - } + env = append(env, m.Spec.ResourceOverrides.ProxyDeployment.Env...) } // Add volume mounts for user-defined volumes @@ -1753,12 +1748,7 @@ func (r *MCPServerReconciler) deploymentNeedsUpdate( // Add user-specified environment variables if mcpServer.Spec.ResourceOverrides != nil && mcpServer.Spec.ResourceOverrides.ProxyDeployment != nil { - for _, envVar := range mcpServer.Spec.ResourceOverrides.ProxyDeployment.Env { - expectedProxyEnv = append(expectedProxyEnv, corev1.EnvVar{ - Name: envVar.Name, - Value: envVar.Value, - }) - } + expectedProxyEnv = append(expectedProxyEnv, mcpServer.Spec.ResourceOverrides.ProxyDeployment.Env...) } // Add default environment variables that are always injected expectedProxyEnv = ctrlutil.EnsureRequiredEnvVars(ctx, expectedProxyEnv)