@@ -22,6 +22,7 @@ import (
2222 "github.com/stacklok/toolhive/pkg/container/templates"
2323 "github.com/stacklok/toolhive/pkg/groups"
2424 "github.com/stacklok/toolhive/pkg/networking"
25+ "github.com/stacklok/toolhive/pkg/registry"
2526 "github.com/stacklok/toolhive/pkg/runner"
2627 "github.com/stacklok/toolhive/pkg/runner/retriever"
2728 "github.com/stacklok/toolhive/pkg/secrets"
@@ -142,6 +143,16 @@ func (s *WorkloadService) UpdateWorkloadFromRequest(ctx context.Context, name st
142143func (s * WorkloadService ) BuildFullRunConfig (
143144 ctx context.Context , req * createRequest , existingPort int ,
144145) (* runner.RunConfig , error ) {
146+ // If registry+server specified, resolve from registry and fill defaults
147+ if req .Registry != "" && req .Server != "" {
148+ if err := resolveRegistryServer (req ); err != nil {
149+ return nil , fmt .Errorf ("failed to resolve server from registry: %w" , err )
150+ }
151+ }
152+ if (req .Registry != "" && req .Server == "" ) || (req .Registry == "" && req .Server != "" ) {
153+ return nil , fmt .Errorf ("both registry and server must be specified together" )
154+ }
155+
145156 // Default proxy mode to streamable-http if not specified (SSE is deprecated)
146157 if ! types .IsValidProxyMode (req .ProxyMode ) {
147158 if req .ProxyMode == "" {
@@ -557,3 +568,74 @@ func (s *WorkloadService) GetWorkloadNamesFromRequest(ctx context.Context, req b
557568
558569 return workloadNames , nil
559570}
571+
572+ // resolveRegistryServer resolves a server from the registry and fills in
573+ // default values on the request. User-provided fields are not overwritten.
574+ func resolveRegistryServer (req * createRequest ) error {
575+ provider , err := registry .GetDefaultProviderWithConfig (
576+ config .NewProvider (),
577+ registry .WithInteractive (false ),
578+ )
579+ if err != nil {
580+ return fmt .Errorf ("failed to get registry provider: %w" , err )
581+ }
582+
583+ metadata , err := provider .GetServer (req .Server )
584+ if err != nil {
585+ return fmt .Errorf ("server %q not found in registry: %w" , req .Server , err )
586+ }
587+
588+ applyRegistryDefaults (req , metadata )
589+ return nil
590+ }
591+
592+ func applyRegistryDefaults (req * createRequest , metadata regtypes.ServerMetadata ) {
593+ if req .Transport == "" {
594+ req .Transport = metadata .GetTransport ()
595+ }
596+ if req .Name == "" {
597+ req .Name = metadata .GetName ()
598+ }
599+
600+ switch md := metadata .(type ) {
601+ case * regtypes.ImageMetadata :
602+ applyImageDefaults (req , md )
603+ case * regtypes.RemoteServerMetadata :
604+ applyRemoteDefaults (req , md )
605+ }
606+ }
607+
608+ func applyImageDefaults (req * createRequest , md * regtypes.ImageMetadata ) {
609+ if req .Image == "" {
610+ req .Image = md .Image
611+ }
612+ if req .TargetPort == 0 && md .TargetPort != 0 {
613+ req .TargetPort = md .TargetPort
614+ }
615+ if len (req .CmdArguments ) == 0 && len (md .Args ) > 0 {
616+ req .CmdArguments = md .Args
617+ }
618+ if req .PermissionProfile == nil && md .Permissions != nil {
619+ req .PermissionProfile = md .Permissions
620+ }
621+ // Merge env vars: registry defaults first, user overrides take precedence
622+ if req .EnvVars == nil {
623+ req .EnvVars = make (map [string ]string )
624+ }
625+ for _ , ev := range md .EnvVars {
626+ if ev .Default != "" {
627+ if _ , userSet := req .EnvVars [ev .Name ]; ! userSet {
628+ req .EnvVars [ev .Name ] = ev .Default
629+ }
630+ }
631+ }
632+ }
633+
634+ func applyRemoteDefaults (req * createRequest , md * regtypes.RemoteServerMetadata ) {
635+ if req .URL == "" {
636+ req .URL = md .URL
637+ }
638+ if len (req .Headers ) == 0 && len (md .Headers ) > 0 {
639+ req .Headers = md .Headers
640+ }
641+ }
0 commit comments