diff --git a/cmd/main.go b/cmd/main.go index e103574..b338bb0 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -203,8 +203,9 @@ func main() { } if err = (&controller.MCPServerReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Plugins: nil, // Plugins are for 3rd party extensions to open source. }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "MCPServer") os.Exit(1) diff --git a/pkg/controller/mcpserver_controller.go b/pkg/controller/mcpserver_controller.go index bee7922..80788f0 100644 --- a/pkg/controller/mcpserver_controller.go +++ b/pkg/controller/mcpserver_controller.go @@ -22,7 +22,7 @@ import ( "time" - "github.com/kagent-dev/kmcp/pkg/controller/internal/transportadapter" + "github.com/kagent-dev/kmcp/pkg/controller/transportadapter" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -40,7 +40,8 @@ import ( // MCPServerReconciler reconciles a MCPServer object type MCPServerReconciler struct { client.Client - Scheme *runtime.Scheme + Scheme *runtime.Scheme + Plugins []transportadapter.TranslatorPlugin } // +kubebuilder:rbac:groups=kagent.dev,resources=mcpservers,verbs=get;list;watch;create;update;patch;delete @@ -69,8 +70,8 @@ func (r *MCPServerReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( return ctrl.Result{}, client.IgnoreNotFound(err) } - t := transportadapter.NewTransportAdapterTranslator(r.Scheme) - outputs, err := t.TranslateTransportAdapterOutputs(mcpServer) + t := transportadapter.NewTransportAdapterTranslator(r.Scheme, r.Plugins) + outputs, err := t.TranslateTransportAdapterOutputs(ctx, mcpServer) if err != nil { log.FromContext(ctx).Error(err, "Failed to translate MCPServer outputs") r.reconcileStatus(ctx, mcpServer, err) diff --git a/pkg/controller/internal/transportadapter/transportadapter_translator.go b/pkg/controller/transportadapter/transportadapter_translator.go similarity index 92% rename from pkg/controller/internal/transportadapter/transportadapter_translator.go rename to pkg/controller/transportadapter/transportadapter_translator.go index 6be009b..bde5f59 100644 --- a/pkg/controller/internal/transportadapter/transportadapter_translator.go +++ b/pkg/controller/transportadapter/transportadapter_translator.go @@ -1,11 +1,13 @@ package transportadapter import ( + "context" "crypto/sha256" "encoding/hex" "fmt" "sort" + "go.uber.org/multierr" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -25,20 +27,32 @@ const ( // Translator is the interface for translating MCPServer objects to TransportAdapter objects. type Translator interface { - TranslateTransportAdapterOutputs(server *v1alpha1.MCPServer) ([]client.Object, error) + TranslateTransportAdapterOutputs( + ctx context.Context, + server *v1alpha1.MCPServer, + ) ([]client.Object, error) } +type TranslatorPlugin func( + ctx context.Context, + server *v1alpha1.MCPServer, + objects []client.Object, +) ([]client.Object, error) + type transportAdapterTranslator struct { - scheme *runtime.Scheme + scheme *runtime.Scheme + plugins []TranslatorPlugin } -func NewTransportAdapterTranslator(scheme *runtime.Scheme) Translator { +func NewTransportAdapterTranslator(scheme *runtime.Scheme, plugins []TranslatorPlugin) Translator { return &transportAdapterTranslator{ - scheme: scheme, + scheme: scheme, + plugins: plugins, } } func (t *transportAdapterTranslator) TranslateTransportAdapterOutputs( + ctx context.Context, server *v1alpha1.MCPServer, ) ([]client.Object, error) { serviceAccount, err := t.translateTransportAdapterServiceAccount(server) @@ -57,12 +71,12 @@ func (t *transportAdapterTranslator) TranslateTransportAdapterOutputs( if err != nil { return nil, fmt.Errorf("failed to translate TransportAdapter config map: %w", err) } - return []client.Object{ + return t.runPlugins(ctx, server, []client.Object{ serviceAccount, deployment, service, configMap, - }, nil + }) } func (t *transportAdapterTranslator) translateTransportAdapterDeployment( @@ -359,7 +373,9 @@ func convertEnvVars(env map[string]string) []corev1.EnvVar { return envVars } -func (t *transportAdapterTranslator) translateTransportAdapterService(server *v1alpha1.MCPServer) (*corev1.Service, error) { +func (t *transportAdapterTranslator) translateTransportAdapterService( + server *v1alpha1.MCPServer, +) (*corev1.Service, error) { port := server.Spec.Deployment.Port if port == 0 { return nil, fmt.Errorf("deployment port must be specified for MCPServer %s", server.Name) @@ -392,7 +408,9 @@ func (t *transportAdapterTranslator) translateTransportAdapterService(server *v1 return service, controllerutil.SetOwnerReference(server, service, t.scheme) } -func (t *transportAdapterTranslator) translateTransportAdapterConfigAsYAML(server *v1alpha1.MCPServer) (string, error) { +func (t *transportAdapterTranslator) translateTransportAdapterConfigAsYAML( + server *v1alpha1.MCPServer, +) (string, error) { config, err := t.translateTransportAdapterConfig(server) if err != nil { return "", fmt.Errorf("failed to translate MCP server config: %w", err) @@ -406,7 +424,9 @@ func (t *transportAdapterTranslator) translateTransportAdapterConfigAsYAML(serve return string(configYaml), nil } -func (t *transportAdapterTranslator) translateTransportAdapterConfigMap(server *v1alpha1.MCPServer) (*corev1.ConfigMap, error) { +func (t *transportAdapterTranslator) translateTransportAdapterConfigMap( + server *v1alpha1.MCPServer, +) (*corev1.ConfigMap, error) { configYaml, err := t.translateTransportAdapterConfigAsYAML(server) if err != nil { return nil, fmt.Errorf("failed to marshal MCP server config to YAML: %w", err) @@ -498,3 +518,22 @@ func (t *transportAdapterTranslator) translateTransportAdapterConfig(server *v1a return config, nil } + +func (t *transportAdapterTranslator) runPlugins( + ctx context.Context, + server *v1alpha1.MCPServer, + objects []client.Object, +) ([]client.Object, error) { + var errs error + if len(t.plugins) > 0 { + for _, plugin := range t.plugins { + out, err := plugin(ctx, server, objects) + if err != nil { + errs = multierr.Append(errs, fmt.Errorf("plugin %T failed: %w", plugin, err)) + } + objects = out + } + } + + return objects, errs +} diff --git a/pkg/controller/internal/transportadapter/transportadapter_types.go b/pkg/controller/transportadapter/transportadapter_types.go similarity index 100% rename from pkg/controller/internal/transportadapter/transportadapter_types.go rename to pkg/controller/transportadapter/transportadapter_types.go