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
19 changes: 19 additions & 0 deletions api/v1alpha1/mcpserver_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,25 @@ type MCPServerDeployment struct {
// This allows for custom volume configurations beyond just secrets and configmaps.
// +optional
Volumes []corev1.Volume `json:"volumes,omitempty"`

// InitContainer defines the configuration for the init container that copies
// the transport adapter binary. This is used for stdio transport type.
// +optional
InitContainer *InitContainerConfig `json:"initContainer,omitempty"`
}

// InitContainerConfig defines the configuration for the init container.
type InitContainerConfig struct {
// Image defines the full image reference for the init container.
// If specified, this overrides the default transport adapter image.
// Example: "myregistry.com/agentgateway/agentgateway:0.9.0-musl"
// +optional
Image string `json:"image,omitempty"`

// ImagePullPolicy defines the pull policy for the init container image.
// +optional
// +kubebuilder:validation:Enum=Always;Never;IfNotPresent
ImagePullPolicy corev1.PullPolicy `json:"imagePullPolicy,omitempty"`
}

// +kubebuilder:object:root=true
Expand Down
22 changes: 21 additions & 1 deletion api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions config/crd/bases/kagent.dev_mcpservers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,26 @@ spec:
description: Image defines the container image to to deploy the
MCP server.
type: string
initContainer:
description: |-
InitContainer defines the configuration for the init container that copies
the transport adapter binary. This is used for stdio transport type.
properties:
image:
description: |-
Image defines the full image reference for the init container.
If specified, this overrides the default transport adapter image.
Example: "myregistry.com/agentgateway/agentgateway:0.9.0-musl"
type: string
imagePullPolicy:
description: ImagePullPolicy defines the pull policy for the
init container image.
enum:
- Always
- Never
- IfNotPresent
type: string
type: object
port:
default: 3000
description: Port defines the port on which the MCP server will
Expand Down
52 changes: 46 additions & 6 deletions pkg/controller/transportadapter/transportadapter_translator.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/hex"
"fmt"
"os"
"regexp"
"sort"

"go.uber.org/multierr"
Expand All @@ -23,9 +24,14 @@ import (
)

const (
defaultTransportAdapterContainerImage = "ghcr.io/agentgateway/agentgateway:0.9.0-musl"
transportAdapterRepository = "ghcr.io/agentgateway/agentgateway"
defaultTransportAdapterVersion = "0.9.0"
)

// versionRegex validates that version strings contain only allowed characters
// (alphanumeric, dots, hyphens) to prevent potential image injection attacks
var versionRegex = regexp.MustCompile(`^[a-zA-Z0-9.\-]+$`)

// Translator is the interface for translating MCPServer objects to TransportAdapter objects.
type Translator interface {
TranslateTransportAdapterOutputs(
Expand Down Expand Up @@ -103,10 +109,19 @@ func (t *transportAdapterTranslator) translateTransportAdapterDeployment(
// Create volume mounts from the MCPServer spec
volumeMounts := t.createVolumeMounts(server.Spec.Deployment)

transportAdapterContainerImage := defaultTransportAdapterContainerImage
transportAdapterVersion := os.Getenv("TRANSPORT_ADAPTER_VERSION")
if transportAdapterVersion != "" {
transportAdapterContainerImage = fmt.Sprintf("ghcr.io/agentgateway/agentgateway:%s-musl", transportAdapterVersion)
// Determine the init container image and pull policy to use
// Start with the default transport adapter image
transportAdapterContainerImage := getTransportAdapterImage()

initContainerPullPolicy := corev1.PullIfNotPresent

// Override with custom image if specified in the MCPServer spec
if server.Spec.Deployment.InitContainer != nil && server.Spec.Deployment.InitContainer.Image != "" {
transportAdapterContainerImage = server.Spec.Deployment.InitContainer.Image
initContainerPullPolicy = server.Spec.Deployment.InitContainer.ImagePullPolicy
if initContainerPullPolicy == "" {
initContainerPullPolicy = corev1.PullIfNotPresent
}
}

var template corev1.PodSpec
Expand All @@ -118,7 +133,7 @@ func (t *transportAdapterTranslator) translateTransportAdapterDeployment(
InitContainers: []corev1.Container{{
Name: "copy-binary",
Image: transportAdapterContainerImage,
ImagePullPolicy: corev1.PullIfNotPresent,
ImagePullPolicy: initContainerPullPolicy,
Command: []string{},
Args: []string{
"--copy-self",
Expand Down Expand Up @@ -544,3 +559,28 @@ func (t *transportAdapterTranslator) runPlugins(

return objects, errs
}

// validateVersion validates that a version string contains only allowed characters
// to prevent potential image injection attacks
func validateVersion(version string) error {
if !versionRegex.MatchString(version) {
return fmt.Errorf("invalid version format: %s (only alphanumeric characters, dots, and hyphens are allowed)", version)
}
return nil
}

// getTransportAdapterImage returns the transport adapter container image,
// using the environment variable if provided and valid, otherwise using the default
func getTransportAdapterImage() string {
transportAdapterVersion := os.Getenv("TRANSPORT_ADAPTER_VERSION")
if transportAdapterVersion == "" {
return fmt.Sprintf("%s:%s-musl", transportAdapterRepository, defaultTransportAdapterVersion)
}

if err := validateVersion(transportAdapterVersion); err != nil {
klog.Warningf("Invalid TRANSPORT_ADAPTER_VERSION: %v, fallback to %s", err, defaultTransportAdapterVersion)
return fmt.Sprintf("%s:%s-musl", transportAdapterRepository, defaultTransportAdapterVersion)
}

return fmt.Sprintf("%s:%s-musl", transportAdapterRepository, transportAdapterVersion)
}
Loading