Skip to content

CRD type changes and manifest regeneration for MCPServer and VirtualMCPServer scaling #4206

@yrobla

Description

@yrobla

Description

Add the SessionStorageConfig shared struct and scaling fields to both CRDs, then regenerate deepcopy and CRD manifests. Specifically:

  1. MCPServer types (mcpserver_types.go): Add SessionStorageConfig struct, Replicas *int32, BackendReplicas *int32, and SessionStorage *SessionStorageConfig to MCPServerSpec.
  2. VirtualMCPServer types (virtualmcpserver_types.go): Add Replicas *int32 and SessionStorage *SessionStorageConfig to VirtualMCPServerSpec, reusing the SessionStorageConfig struct defined above.
  3. Regeneration: Run task operator-generate and task operator-manifests to regenerate zz_generated.deepcopy.go and the CRD YAML files.

These changes are the foundation for horizontal scaling support in the ToolHive operator (THV-0047). All downstream reconciler and deployment-builder tasks depend on this compilable, up-to-date API surface.

Note: Originally split into three tasks (TASK-001 #267, TASK-002 #269, TASK-003 #275). Merged because all three are always shipped in a single PR — the type changes and regeneration are inseparable from a compilation standpoint.

Parent epic: stacklok/stacklok-epics#264

Dependencies: None (root task)
Blocks: TASK-002 (MCPServer Reconciler), TASK-003 (MCPServer RunConfig Injection), TASK-004 (VirtualMCPServer Deployment Builder), TASK-005 (VirtualMCPServer ConfigMap Injection), TASK-006 (Unit Tests)

Acceptance Criteria

MCPServer types (mcpserver_types.go)

  • SessionStorageConfig struct is defined with fields: Provider (required, enum memory|redis), Address (optional), DB (optional int32, default 0), KeyPrefix (optional), and PasswordRef (optional *SecretKeyRef)
  • CEL validation rule on SessionStorageConfig enforces that address is required when provider == "redis"
  • MCPServerSpec includes Replicas *int32, BackendReplicas *int32, and SessionStorage *SessionStorageConfig with // +optional and omitempty tags
  • PasswordRef reuses the existing SecretKeyRef type — not redeclared

VirtualMCPServer types (virtualmcpserver_types.go)

  • VirtualMCPServerSpec includes Replicas *int32 and SessionStorage *SessionStorageConfig with // +optional and omitempty tags
  • SessionStorageConfig is referenced from mcpserver_types.go — not redeclared in this file

Regeneration

  • task operator-generate runs without error and regenerates zz_generated.deepcopy.go with DeepCopyInto for SessionStorageConfig and updated deepcopy for both specs
  • task operator-manifests regenerates CRD YAMLs in deploy/charts/operator-crds/files/crds/ including the CEL validation rule and enum constraints
  • go build ./cmd/thv-operator/... passes after regeneration
  • go test ./cmd/thv-operator/... passes after regeneration
  • All generated files committed in the same PR
  • Code reviewed and approved

Technical Approach

Step 1 — MCPServer types

Add SessionStorageConfig struct and three new fields to MCPServerSpec in mcpserver_types.go. Follow the +optional pattern for pointer fields (OIDCConfig, AuthzConfig). Use +kubebuilder:validation:Enum=memory;redis on Provider and a struct-level +kubebuilder:validation:XValidation rule for the address-required-for-redis constraint.

// +kubebuilder:validation:XValidation:rule="self.provider == 'redis' ? has(self.address) : true",message="address is required when provider is redis"
type SessionStorageConfig struct {
    // +kubebuilder:validation:Enum=memory;redis
    // +kubebuilder:validation:Required
    Provider string `json:"provider"`
    // +optional
    Address string `json:"address,omitempty"`
    // +kubebuilder:default=0
    // +optional
    DB int32 `json:"db,omitempty"`
    // +optional
    KeyPrefix string `json:"keyPrefix,omitempty"`
    // +optional
    PasswordRef *SecretKeyRef `json:"passwordRef,omitempty"`
}

New MCPServerSpec fields:

// +optional
Replicas *int32 `json:"replicas,omitempty"`
// +optional
BackendReplicas *int32 `json:"backendReplicas,omitempty"`
// +optional
SessionStorage *SessionStorageConfig `json:"sessionStorage,omitempty"`

Step 2 — VirtualMCPServer types

Add two new fields to VirtualMCPServerSpec in virtualmcpserver_types.go after EmbeddingServerRef:

// +optional
Replicas *int32 `json:"replicas,omitempty"`
// +optional
SessionStorage *SessionStorageConfig `json:"sessionStorage,omitempty"`

Step 3 — Regeneration

task operator-generate   # regenerates zz_generated.deepcopy.go
task operator-manifests  # regenerates CRD YAMLs + Helm wrappers
go build ./cmd/thv-operator/...
go test -ldflags=-extldflags=-Wl,-w ./cmd/thv-operator/... 2>&1 | grep -v '/test-integration'

Do not run task crdref-gen (documentation generation, out of scope).

Code Pointers

  • cmd/thv-operator/api/v1alpha1/mcpserver_types.go — Add SessionStorageConfig struct and three new MCPServerSpec fields
  • cmd/thv-operator/api/v1alpha1/virtualmcpserver_types.go — Add two new fields to VirtualMCPServerSpec
  • cmd/thv-operator/api/v1alpha1/mcpexternalauthconfig_types.go — Source of SecretKeyRef (lines 561–569) and CEL validation patterns (lines 44–49)
  • cmd/thv-operator/api/v1alpha1/zz_generated.deepcopy.go — Regenerated; do not edit by hand

Out of Scope

  • Reconciler logic changes — TASK-002 through TASK-005
  • Unit tests for reconciler behavior — TASK-006
  • runner.RunConfig changes — TASK-003

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestkubernetesItems related to KubernetesoperatorscalabilityItems related to scalabilityvmcpVirtual MCP Server related issues

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions