@@ -21,8 +21,46 @@ func ServerJSONToImageMetadata(serverJSON *upstream.ServerJSON) (*registry.Image
2121 return nil , fmt .Errorf ("serverJSON cannot be nil" )
2222 }
2323
24+ pkg , err := extractSingleOCIPackage (serverJSON )
25+ if err != nil {
26+ return nil , err
27+ }
28+
29+ imageMetadata := & registry.ImageMetadata {
30+ BaseServerMetadata : registry.BaseServerMetadata {
31+ Name : serverJSON .Title ,
32+ Description : serverJSON .Description ,
33+ Transport : pkg .Transport .Type ,
34+ },
35+ Image : pkg .Identifier , // OCI packages store full image ref in Identifier
36+ }
37+
38+ // Set repository URL
39+ if serverJSON .Repository != nil && serverJSON .Repository .URL != "" {
40+ imageMetadata .RepositoryURL = serverJSON .Repository .URL
41+ }
42+
43+ // Convert environment variables
44+ imageMetadata .EnvVars = convertEnvironmentVariables (pkg .EnvironmentVariables )
45+
46+ // Extract target port from transport URL if present
47+ imageMetadata .TargetPort = extractTargetPort (pkg .Transport .URL , serverJSON .Name )
48+
49+ // Convert PackageArguments to simple Args (priority: structured arguments first)
50+ if len (pkg .PackageArguments ) > 0 {
51+ imageMetadata .Args = flattenPackageArguments (pkg .PackageArguments )
52+ }
53+
54+ // Extract publisher-provided extensions (including Args fallback)
55+ extractImageExtensions (serverJSON , imageMetadata )
56+
57+ return imageMetadata , nil
58+ }
59+
60+ // extractSingleOCIPackage validates and extracts the single OCI package from ServerJSON
61+ func extractSingleOCIPackage (serverJSON * upstream.ServerJSON ) (model.Package , error ) {
2462 if len (serverJSON .Packages ) == 0 {
25- return nil , fmt .Errorf ("server '%s' has no packages (not a container-based server)" , serverJSON .Name )
63+ return model. Package {} , fmt .Errorf ("server '%s' has no packages (not a container-based server)" , serverJSON .Name )
2664 }
2765
2866 // Filter for OCI packages only
@@ -36,70 +74,60 @@ func ServerJSONToImageMetadata(serverJSON *upstream.ServerJSON) (*registry.Image
3674 }
3775
3876 if len (ociPackages ) == 0 {
39- return nil , fmt .Errorf ("server '%s' has no OCI packages (found: %v)" , serverJSON .Name , packageTypes )
77+ return model. Package {} , fmt .Errorf ("server '%s' has no OCI packages (found: %v)" , serverJSON .Name , packageTypes )
4078 }
4179
4280 if len (ociPackages ) > 1 {
43- return nil , fmt .Errorf ("server '%s' has %d OCI packages, expected exactly 1" , serverJSON .Name , len (ociPackages ))
81+ return model. Package {} , fmt .Errorf ("server '%s' has %d OCI packages, expected exactly 1" , serverJSON .Name , len (ociPackages ))
4482 }
4583
46- pkg := ociPackages [0 ]
84+ return ociPackages [0 ], nil
85+ }
4786
48- imageMetadata := & registry.ImageMetadata {
49- BaseServerMetadata : registry.BaseServerMetadata {
50- Name : serverJSON .Title ,
51- Description : serverJSON .Description ,
52- Transport : pkg .Transport .Type ,
53- },
54- Image : pkg .Identifier , // OCI packages store full image ref in Identifier
87+ // convertEnvironmentVariables converts model.KeyValueInput to registry.EnvVar
88+ func convertEnvironmentVariables (envVars []model.KeyValueInput ) []* registry.EnvVar {
89+ if len (envVars ) == 0 {
90+ return nil
5591 }
5692
57- // Set repository URL
58- if serverJSON .Repository .URL != "" {
59- imageMetadata .RepositoryURL = serverJSON .Repository .URL
93+ result := make ([]* registry.EnvVar , 0 , len (envVars ))
94+ for _ , envVar := range envVars {
95+ result = append (result , & registry.EnvVar {
96+ Name : envVar .Name ,
97+ Description : envVar .Description ,
98+ Required : envVar .IsRequired ,
99+ Secret : envVar .IsSecret ,
100+ Default : envVar .Default ,
101+ })
60102 }
103+ return result
104+ }
61105
62- // Convert environment variables
63- if len (pkg .EnvironmentVariables ) > 0 {
64- imageMetadata .EnvVars = make ([]* registry.EnvVar , 0 , len (pkg .EnvironmentVariables ))
65- for _ , envVar := range pkg .EnvironmentVariables {
66- imageMetadata .EnvVars = append (imageMetadata .EnvVars , & registry.EnvVar {
67- Name : envVar .Name ,
68- Description : envVar .Description ,
69- Required : envVar .IsRequired ,
70- Secret : envVar .IsSecret ,
71- Default : envVar .Default ,
72- })
73- }
106+ // extractTargetPort extracts the port number from a transport URL
107+ func extractTargetPort (transportURL , serverName string ) int {
108+ if transportURL == "" {
109+ return 0
74110 }
75111
76- // Extract target port from transport URL if present
77- if pkg .Transport .URL != "" {
78- // Parse URL like "http://localhost:8080"
79- parsedURL , err := url .Parse (pkg .Transport .URL )
80- if err != nil {
81- // Log parse error to aid debugging but don't fail conversion
82- fmt .Printf ("⚠️ Failed to parse transport URL '%s' for server '%s': %v\n " ,
83- pkg .Transport .URL , serverJSON .Name , err )
84- } else if parsedURL .Port () != "" {
85- if port , err := strconv .Atoi (parsedURL .Port ()); err == nil {
86- imageMetadata .TargetPort = port
87- } else {
88- fmt .Printf ("⚠️ Failed to parse port from URL '%s' for server '%s': %v\n " ,
89- pkg .Transport .URL , serverJSON .Name , err )
90- }
91- }
112+ parsedURL , err := url .Parse (transportURL )
113+ if err != nil {
114+ fmt .Printf ("⚠️ Failed to parse transport URL '%s' for server '%s': %v\n " ,
115+ transportURL , serverName , err )
116+ return 0
92117 }
93118
94- // Convert PackageArguments to simple Args (priority: structured arguments first)
95- if len (pkg .PackageArguments ) > 0 {
96- imageMetadata .Args = flattenPackageArguments (pkg .PackageArguments )
119+ if parsedURL .Port () == "" {
120+ return 0
97121 }
98122
99- // Extract publisher-provided extensions (including Args fallback)
100- extractImageExtensions (serverJSON , imageMetadata )
123+ port , err := strconv .Atoi (parsedURL .Port ())
124+ if err != nil {
125+ fmt .Printf ("⚠️ Failed to parse port from URL '%s' for server '%s': %v\n " ,
126+ transportURL , serverName , err )
127+ return 0
128+ }
101129
102- return imageMetadata , nil
130+ return port
103131}
104132
105133// ServerJSONToRemoteServerMetadata converts an upstream ServerJSON (with remotes) to toolhive RemoteServerMetadata
@@ -125,7 +153,7 @@ func ServerJSONToRemoteServerMetadata(serverJSON *upstream.ServerJSON) (*registr
125153 }
126154
127155 // Set repository URL
128- if serverJSON .Repository .URL != "" {
156+ if serverJSON .Repository != nil && serverJSON . Repository .URL != "" {
129157 remoteMetadata .RepositoryURL = serverJSON .Repository .URL
130158 }
131159
0 commit comments