Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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.GetProxyPort())
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.GetProxyPort())
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.GetProxyPort() {
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.GetProxyPort() {
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