Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cmd/thv-operator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,8 @@ kubectl describe mcpserver <name>
|---------------------|----------------------------------------------------|----------|---------|
| `image` | Container image for the MCP server | Yes | - |
| `transport` | Transport method (stdio, streamable-http or sse) | No | stdio |
| `port` | Port to expose the MCP server on | No | 8080 |
| `targetPort` | Port that MCP server listens to | No | - |
| `proxyPort` | Port to expose the MCP server on | No | 8080 |
| `mcpPort` | Port that MCP server listens to | No | - |
| `args` | Additional arguments to pass to the MCP server | No | - |
| `env` | Environment variables to set in the container | No | - |
| `volumes` | Volumes to mount in the container | No | - |
Expand Down
11 changes: 8 additions & 3 deletions cmd/thv-operator/api/v1alpha1/mcpremoteproxy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,12 @@ func (m *MCPRemoteProxy) GetOIDCConfig() *OIDCConfigRef {
return &m.Spec.OIDCConfig
}

// GetPort returns the port for the MCPRemoteProxy
func (m *MCPRemoteProxy) GetPort() int32 {
return m.Spec.Port
// GetProxyPort returns the proxy port of the MCPRemoteProxy
func (m *MCPRemoteProxy) GetProxyPort() int32 {
if m.Spec.Port > 0 {
return m.Spec.Port
}

// default to 8080 if no port is specified
return 8080
}
42 changes: 39 additions & 3 deletions cmd/thv-operator/api/v1alpha1/mcpserver_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,28 @@ type MCPServerSpec struct {
// +kubebuilder:validation:Minimum=1
// +kubebuilder:validation:Maximum=65535
// +kubebuilder:default=8080
// Deprecated: Use ProxyPort instead
Port int32 `json:"port,omitempty"`

// TargetPort is the port that MCP server listens to
// +kubebuilder:validation:Minimum=1
// +kubebuilder:validation:Maximum=65535
// +optional
// Deprecated: Use McpPort instead
TargetPort int32 `json:"targetPort,omitempty"`

// ProxyPort is the port to expose the proxy runner on
// +kubebuilder:validation:Minimum=1
// +kubebuilder:validation:Maximum=65535
// +kubebuilder:default=8080
ProxyPort int32 `json:"proxyPort,omitempty"`

// McpPort is the port that MCP server listens to
// +kubebuilder:validation:Minimum=1
// +kubebuilder:validation:Maximum=65535
// +optional
McpPort int32 `json:"mcpPort,omitempty"`

// Args are additional arguments to pass to the MCP server
// +optional
Args []string `json:"args,omitempty"`
Expand Down Expand Up @@ -725,9 +739,31 @@ func (m *MCPServer) GetOIDCConfig() *OIDCConfigRef {
return m.Spec.OIDCConfig
}

// GetPort returns the port of the MCPServer
func (m *MCPServer) GetPort() int32 {
return m.Spec.Port
// GetProxyPort returns the proxy port of the MCPServer
func (m *MCPServer) GetProxyPort() int32 {
if m.Spec.ProxyPort > 0 {
return m.Spec.ProxyPort
}

// the below is deprecated and will be removed in a future version
// we need to keep it here to avoid breaking changes
if m.Spec.Port > 0 {
return m.Spec.Port
}

// default to 8080 if no port is specified
return 8080
}

// GetMcpPort returns the MCP port of the MCPServer
func (m *MCPServer) GetMcpPort() int32 {
if m.Spec.McpPort > 0 {
return m.Spec.McpPort
}

// the below is deprecated and will be removed in a future version
// we need to keep it here to avoid breaking changes
return m.Spec.TargetPort
}

func init() {
Expand Down
6 changes: 3 additions & 3 deletions cmd/thv-operator/controllers/mcpremoteproxy_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ func (r *MCPRemoteProxyReconciler) ensureService(
func (r *MCPRemoteProxyReconciler) ensureServiceURL(ctx context.Context, proxy *mcpv1alpha1.MCPRemoteProxy) error {
if proxy.Status.URL == "" {
// Note: createProxyServiceURL uses the remote-prefixed service name
proxy.Status.URL = createProxyServiceURL(proxy.Name, proxy.Namespace, proxy.Spec.Port)
proxy.Status.URL = createProxyServiceURL(proxy.Name, proxy.Namespace, int32(proxy.GetProxyPort()))
return r.Status().Update(ctx, proxy)
}
return nil
Expand Down Expand Up @@ -631,7 +631,7 @@ func (r *MCPRemoteProxyReconciler) containerNeedsUpdate(
}

// Check if port has changed
if len(container.Ports) > 0 && container.Ports[0].ContainerPort != proxy.Spec.Port {
if len(container.Ports) > 0 && container.Ports[0].ContainerPort != int32(proxy.GetProxyPort()) {
return true
}

Expand Down Expand Up @@ -727,7 +727,7 @@ func (r *MCPRemoteProxyReconciler) podTemplateMetadataNeedsUpdate(
// serviceNeedsUpdate checks if the service needs to be updated
func (*MCPRemoteProxyReconciler) serviceNeedsUpdate(service *corev1.Service, proxy *mcpv1alpha1.MCPRemoteProxy) bool {
// Check if port has changed
if len(service.Spec.Ports) > 0 && service.Spec.Ports[0].Port != proxy.Spec.Port {
if len(service.Spec.Ports) > 0 && service.Spec.Ports[0].Port != int32(proxy.GetProxyPort()) {
return true
}

Expand Down
6 changes: 3 additions & 3 deletions cmd/thv-operator/controllers/mcpremoteproxy_deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ func (r *MCPRemoteProxyReconciler) buildSecurityContexts(
// buildContainerPorts builds container port configuration
func (*MCPRemoteProxyReconciler) buildContainerPorts(proxy *mcpv1alpha1.MCPRemoteProxy) []corev1.ContainerPort {
return []corev1.ContainerPort{{
ContainerPort: proxy.Spec.Port,
ContainerPort: int32(proxy.GetProxyPort()),
Name: "http",
Protocol: corev1.ProtocolTCP,
}}
Expand All @@ -279,8 +279,8 @@ func (r *MCPRemoteProxyReconciler) serviceForMCPRemoteProxy(
Spec: corev1.ServiceSpec{
Selector: ls,
Ports: []corev1.ServicePort{{
Port: proxy.Spec.Port,
TargetPort: intstr.FromInt(int(proxy.Spec.Port)),
Port: int32(proxy.GetProxyPort()),
TargetPort: intstr.FromInt(int(proxy.GetProxyPort())),
Protocol: corev1.ProtocolTCP,
Name: "http",
}},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ func TestMCPRemoteProxyStatusProgression(t *testing.T) {

// Verify status URL was set
assert.NotEmpty(t, updatedProxy.Status.URL)
expectedURL := createProxyServiceURL(proxy.Name, proxy.Namespace, proxy.Spec.Port)
expectedURL := createProxyServiceURL(proxy.Name, proxy.Namespace, int32(proxy.GetProxyPort()))
assert.Equal(t, expectedURL, updatedProxy.Status.URL)
}

Expand Down
7 changes: 1 addition & 6 deletions cmd/thv-operator/controllers/mcpremoteproxy_runconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,6 @@ func (r *MCPRemoteProxyReconciler) createRunConfigFromMCPRemoteProxy(
proxyHost = envHost
}

port := 8080
if proxy.Spec.Port != 0 {
port = int(proxy.Spec.Port)
}

// Get tool configuration from MCPToolConfig if referenced
var toolsFilter []string
var toolsOverride map[string]runner.ToolOverride
Expand Down Expand Up @@ -162,7 +157,7 @@ func (r *MCPRemoteProxyReconciler) createRunConfigFromMCPRemoteProxy(
// Key: Set RemoteURL instead of Image
runner.WithRemoteURL(proxy.Spec.RemoteURL),
// Use user-specified transport (sse or streamable-http, both use HTTPTransport internally)
runner.WithTransportAndPorts(transport, port, 0),
runner.WithTransportAndPorts(transport, int(proxy.GetProxyPort()), 0),
runner.WithHost(proxyHost),
runner.WithTrustProxyHeaders(proxy.Spec.TrustProxyHeaders),
runner.WithToolsFilter(toolsFilter),
Expand Down
22 changes: 11 additions & 11 deletions cmd/thv-operator/controllers/mcpserver_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,7 @@ func (r *MCPServerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (

// Update the MCPServer status with the service URL
if mcpServer.Status.URL == "" {
mcpServer.Status.URL = ctrlutil.CreateProxyServiceURL(mcpServer.Name, mcpServer.Namespace, mcpServer.Spec.Port)
mcpServer.Status.URL = ctrlutil.CreateProxyServiceURL(mcpServer.Name, mcpServer.Namespace, mcpServer.Spec.ProxyPort)
err = r.Status().Update(ctx, mcpServer)
if err != nil {
ctxLogger.Error(err, "Failed to update MCPServer status")
Expand Down Expand Up @@ -1151,7 +1151,7 @@ func (r *MCPServerReconciler) deploymentForMCPServer(
VolumeMounts: volumeMounts,
Resources: resources,
Ports: []corev1.ContainerPort{{
ContainerPort: m.Spec.Port,
ContainerPort: m.GetProxyPort(),
Name: "http",
Protocol: corev1.ProtocolTCP,
}},
Expand Down Expand Up @@ -1228,8 +1228,8 @@ func (r *MCPServerReconciler) serviceForMCPServer(ctx context.Context, m *mcpv1a
Spec: corev1.ServiceSpec{
Selector: ls, // Keep original labels for selector
Ports: []corev1.ServicePort{{
Port: m.Spec.Port,
TargetPort: intstr.FromInt(int(m.Spec.Port)),
Port: m.GetProxyPort(),
TargetPort: intstr.FromInt(int(m.GetProxyPort())),
Protocol: corev1.ProtocolTCP,
Name: "http",
}},
Expand Down Expand Up @@ -1383,7 +1383,7 @@ func (r *MCPServerReconciler) deploymentNeedsUpdate(
}

// Check if the port has changed
portArg := fmt.Sprintf("--proxy-port=%d", mcpServer.Spec.Port)
portArg := fmt.Sprintf("--proxy-port=%d", mcpServer.Spec.ProxyPort)
found = false
for _, arg := range container.Args {
if arg == portArg {
Expand Down Expand Up @@ -1414,7 +1414,7 @@ func (r *MCPServerReconciler) deploymentNeedsUpdate(
}

// Check if the container port has changed
if len(container.Ports) > 0 && container.Ports[0].ContainerPort != mcpServer.Spec.Port {
if len(container.Ports) > 0 && container.Ports[0].ContainerPort != mcpServer.Spec.ProxyPort {
return true
}

Expand Down Expand Up @@ -1535,12 +1535,12 @@ func (r *MCPServerReconciler) deploymentNeedsUpdate(
return true
}

// Check if the targetPort has changed
if mcpServer.Spec.TargetPort != 0 {
targetPortArg := fmt.Sprintf("--target-port=%d", mcpServer.Spec.TargetPort)
// Check if the mcpPort has changed
if mcpServer.Spec.McpPort != 0 {
mcpPortArg := fmt.Sprintf("--target-port=%d", mcpServer.Spec.McpPort)
found := false
for _, arg := range container.Args {
if arg == targetPortArg {
if arg == mcpPortArg {
found = true
break
}
Expand Down Expand Up @@ -1625,7 +1625,7 @@ func (r *MCPServerReconciler) deploymentNeedsUpdate(
// serviceNeedsUpdate checks if the service needs to be updated
func serviceNeedsUpdate(service *corev1.Service, mcpServer *mcpv1alpha1.MCPServer) bool {
// Check if the service port has changed
if len(service.Spec.Ports) > 0 && service.Spec.Ports[0].Port != mcpServer.Spec.Port {
if len(service.Spec.Ports) > 0 && service.Spec.Ports[0].Port != mcpServer.Spec.ProxyPort {
return true
}

Expand Down
6 changes: 3 additions & 3 deletions cmd/thv-operator/controllers/mcpserver_platform_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func TestMCPServerReconciler_DeploymentForMCPServer_Kubernetes(t *testing.T) {
Spec: mcpv1alpha1.MCPServerSpec{
Image: "test-image:latest",
Transport: "stdio",
Port: 8080,
ProxyPort: 8080,
},
}

Expand Down Expand Up @@ -169,7 +169,7 @@ func TestMCPServerReconciler_DeploymentForMCPServer_OpenShift(t *testing.T) {
Spec: mcpv1alpha1.MCPServerSpec{
Image: "test-image:latest",
Transport: "stdio",
Port: 8080,
ProxyPort: 8080,
},
}

Expand Down Expand Up @@ -247,7 +247,7 @@ func TestMCPServerReconciler_DeploymentForMCPServer_PlatformDetectionError(t *te
Spec: mcpv1alpha1.MCPServerSpec{
Image: "test-image:latest",
Transport: "stdio",
Port: 8080,
ProxyPort: 8080,
},
}

Expand Down
11 changes: 6 additions & 5 deletions cmd/thv-operator/controllers/mcpserver_pod_template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func TestDeploymentForMCPServerWithPodTemplateSpec(t *testing.T) {
Spec: mcpv1alpha1.MCPServerSpec{
Image: "test-image:latest",
Transport: "stdio",
Port: 8080,
ProxyPort: 8080,
PodTemplateSpec: podTemplateSpecToRawExtension(t, podTemplateSpec),
},
}
Expand Down Expand Up @@ -162,7 +162,7 @@ func TestDeploymentForMCPServerSecretsProviderEnv(t *testing.T) {
Spec: mcpv1alpha1.MCPServerSpec{
Image: "test-image:latest",
Transport: "stdio",
Port: 8080,
ProxyPort: 8080,
},
}

Expand Down Expand Up @@ -193,7 +193,7 @@ func TestDeploymentForMCPServerWithSecrets(t *testing.T) {
Spec: mcpv1alpha1.MCPServerSpec{
Image: "test-image:latest",
Transport: "stdio",
Port: 8080,
ProxyPort: 8080,
ServiceAccount: &customSA,
Secrets: []mcpv1alpha1.SecretRef{
{
Expand Down Expand Up @@ -290,6 +290,7 @@ func TestDeploymentForMCPServerWithSecrets(t *testing.T) {
assert.NotContains(t, arg, "--secret=", "No secret CLI arguments should be present")
}
}

func TestProxyRunnerSecurityContext(t *testing.T) {
t.Parallel()

Expand All @@ -302,7 +303,7 @@ func TestProxyRunnerSecurityContext(t *testing.T) {
Spec: mcpv1alpha1.MCPServerSpec{
Image: "test-image:latest",
Transport: "stdio",
Port: 8080,
ProxyPort: 8080,
},
}

Expand Down Expand Up @@ -349,7 +350,7 @@ func TestProxyRunnerStructuredLogsEnvVar(t *testing.T) {
Spec: mcpv1alpha1.MCPServerSpec{
Image: "test-image:latest",
Transport: "stdio",
Port: 8080,
ProxyPort: 8080,
},
}

Expand Down
2 changes: 1 addition & 1 deletion cmd/thv-operator/controllers/mcpserver_rbac_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ func createTestMCPServer(name, namespace string) *mcpv1alpha1.MCPServer {
Spec: mcpv1alpha1.MCPServerSpec{
Image: "test-image:latest",
Transport: "stdio",
Port: 8080,
ProxyPort: 8080,
},
}
}
Expand Down
Loading
Loading