Skip to content
Draft
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
99 changes: 40 additions & 59 deletions apiclient/types/mcpserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,17 @@ type ContainerizedRuntimeConfig struct {

// RemoteRuntimeConfig represents configuration for remote runtime (External MCP servers)
type RemoteRuntimeConfig struct {
URL string `json:"url"` // Required: Full URL to remote MCP server
Headers []MCPHeader `json:"headers,omitempty"` // Optional
IsTemplate bool `json:"isTemplate"` // Optional: Whether the URL is a template
RemoteCatalogConfig `json:",inline"`
URL string `json:"url"` // Required: Full URL to remote MCP server
IsTemplate bool `json:"isTemplate"` // Optional: Whether the URL is a template
}

// RemoteCatalogConfig represents template configuration for remote servers in catalog entries
type RemoteCatalogConfig struct {
FixedURL string `json:"fixedURL,omitempty"` // Fixed URL for all instances
URLTemplate string `json:"urlTemplate,omitempty"` // URL template for user URLs
Hostname string `json:"hostname,omitempty"` // Required hostname for user URLs
Headers []MCPHeader `json:"headers,omitempty"` // Optional
FixedURL string `json:"fixedURL,omitempty"`
URLTemplate string `json:"urlTemplate,omitempty"`
Hostname string `json:"hostname,omitempty"` // Required hostname for user URLs
Headers []MCPHeader `json:"headers,omitempty"`
}

// CompositeCatalogConfig represents configuration for composite servers in catalog entries.
Expand All @@ -68,7 +68,16 @@ type CatalogComponentServer struct {
MCPServerID string `json:"mcpServerID,omitempty"`
Manifest MCPServerCatalogEntryManifest `json:"manifest,omitempty"`
ToolOverrides []ToolOverride `json:"toolOverrides,omitempty"`
Disabled bool `json:"disabled,omitempty"`
}

// ComponentID returns the ID of the component server.
// It's used to uniquely identify a component server in a composite server.
func (c CatalogComponentServer) ComponentID() string {
if c.CatalogEntryID != "" {
return c.CatalogEntryID
}

return c.MCPServerID
}

type CompositeRuntimeConfig struct {
Expand All @@ -85,6 +94,14 @@ type ComponentServer struct {
Disabled bool `json:"disabled,omitempty"`
}

func (c ComponentServer) ComponentID() string {
if c.CatalogEntryID != "" {
return c.CatalogEntryID
}

return c.MCPServerID
}

type MCPServerCatalogEntry struct {
Metadata
Manifest MCPServerCatalogEntryManifest `json:"manifest"`
Expand Down Expand Up @@ -328,7 +345,7 @@ func (e RuntimeValidationError) Error() string {

// MapCatalogEntryToServer converts an MCPServerCatalogEntryManifest to an MCPServerManifest
// For remote runtime, userURL is used when the catalog entry has a hostname constraint
func MapCatalogEntryToServer(catalogEntry MCPServerCatalogEntryManifest, userURL string) (MCPServerManifest, error) {
func MapCatalogEntryToServer(catalogEntry MCPServerCatalogEntryManifest, userURL string, disableHostnameValidation bool) (MCPServerManifest, error) {
serverManifest := MCPServerManifest{
// Copy common fields
Metadata: catalogEntry.Metadata,
Expand Down Expand Up @@ -394,22 +411,25 @@ func MapCatalogEntryToServer(catalogEntry MCPServerCatalogEntryManifest, userURL
}
}

remoteConfig := &RemoteRuntimeConfig{}

remoteConfig := &RemoteRuntimeConfig{
RemoteCatalogConfig: *catalogEntry.RemoteConfig,
}
if catalogEntry.RemoteConfig.FixedURL != "" {
// Use the fixed URL from catalog entry
remoteConfig.URL = catalogEntry.RemoteConfig.FixedURL
} else if catalogEntry.RemoteConfig.Hostname != "" {
// Validate that userURL uses the required hostname
if userURL == "" {
return serverManifest, RuntimeValidationError{
Runtime: RuntimeRemote,
Field: "URL",
Message: "user URL is required when catalog entry specifies hostname constraint",
if !disableHostnameValidation {
if userURL == "" {
return serverManifest, RuntimeValidationError{
Runtime: RuntimeRemote,
Field: "URL",
Message: "user URL is required when catalog entry specifies hostname constraint",
}
}
if err := ValidateURLHostname(userURL, catalogEntry.RemoteConfig.Hostname); err != nil {
return serverManifest, err
}
}
if err := ValidateURLHostname(userURL, catalogEntry.RemoteConfig.Hostname); err != nil {
return serverManifest, err
}
remoteConfig.URL = userURL
} else if catalogEntry.RemoteConfig.URLTemplate != "" {
Expand All @@ -422,48 +442,9 @@ func MapCatalogEntryToServer(catalogEntry MCPServerCatalogEntryManifest, userURL
}
}

// Copy headers from catalog entry
remoteConfig.Headers = catalogEntry.RemoteConfig.Headers
// Copy the runtime config back to the server manifest
serverManifest.RemoteConfig = remoteConfig

case RuntimeComposite:
if catalogEntry.CompositeConfig == nil {
return serverManifest, RuntimeValidationError{
Runtime: RuntimeComposite,
Field: "compositeConfig",
Message: "composite configuration is required for composite runtime",
}
}

// Convert CatalogComponentServer to ComponentServer
componentServers := make([]ComponentServer, len(catalogEntry.CompositeConfig.ComponentServers))
for i, catalogComponent := range catalogEntry.CompositeConfig.ComponentServers {
componentServer := ComponentServer{
CatalogEntryID: catalogComponent.CatalogEntryID,
MCPServerID: catalogComponent.MCPServerID,
ToolOverrides: catalogComponent.ToolOverrides,
Disabled: false,
}

if catalogComponent.CatalogEntryID != "" {
componentServerManifest, err := MapCatalogEntryToServer(catalogComponent.Manifest, "")
if err != nil {
return serverManifest, RuntimeValidationError{
Runtime: RuntimeComposite,
Field: fmt.Sprintf("compositeConfig.componentServers[%d]", i),
Message: fmt.Sprintf("failed to convert component manifest: %v", err),
}
}
componentServer.Manifest = componentServerManifest
}

componentServers[i] = componentServer
}

serverManifest.CompositeConfig = &CompositeRuntimeConfig{
ComponentServers: componentServers,
}

default:
return serverManifest, RuntimeValidationError{
Runtime: catalogEntry.Runtime,
Expand Down
14 changes: 7 additions & 7 deletions apiclient/types/mcpserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func TestMapCatalogEntryToServer_UVX(t *testing.T) {
},
}

result, err := MapCatalogEntryToServer(catalogEntry, "")
result, err := MapCatalogEntryToServer(catalogEntry, "", false)
if err != nil {
t.Fatalf("Expected no error, got: %v", err)
}
Expand Down Expand Up @@ -48,7 +48,7 @@ func TestMapCatalogEntryToServer_NPX(t *testing.T) {
},
}

result, err := MapCatalogEntryToServer(catalogEntry, "")
result, err := MapCatalogEntryToServer(catalogEntry, "", false)
if err != nil {
t.Fatalf("Expected no error, got: %v", err)
}
Expand Down Expand Up @@ -78,7 +78,7 @@ func TestMapCatalogEntryToServer_Containerized(t *testing.T) {
},
}

result, err := MapCatalogEntryToServer(catalogEntry, "")
result, err := MapCatalogEntryToServer(catalogEntry, "", false)
if err != nil {
t.Fatalf("Expected no error, got: %v", err)
}
Expand Down Expand Up @@ -114,7 +114,7 @@ func TestMapCatalogEntryToServer_RemoteFixedURL(t *testing.T) {
},
}

result, err := MapCatalogEntryToServer(catalogEntry, "")
result, err := MapCatalogEntryToServer(catalogEntry, "", false)
if err != nil {
t.Fatalf("Expected no error, got: %v", err)
}
Expand Down Expand Up @@ -143,7 +143,7 @@ func TestMapCatalogEntryToServer_RemoteHostname(t *testing.T) {
}

userURL := "https://api.example.com/custom/path"
result, err := MapCatalogEntryToServer(catalogEntry, userURL)
result, err := MapCatalogEntryToServer(catalogEntry, userURL, false)
if err != nil {
t.Fatalf("Expected no error, got: %v", err)
}
Expand Down Expand Up @@ -172,7 +172,7 @@ func TestMapCatalogEntryToServer_RemoteHostnameMismatch(t *testing.T) {
}

userURL := "https://wrong.example.com/custom/path"
_, err := MapCatalogEntryToServer(catalogEntry, userURL)
_, err := MapCatalogEntryToServer(catalogEntry, userURL, false)
if err == nil {
t.Fatal("Expected error for hostname mismatch")
}
Expand All @@ -199,7 +199,7 @@ func TestMapCatalogEntryToServer_MissingConfig(t *testing.T) {
// Missing UVXConfig
}

_, err := MapCatalogEntryToServer(catalogEntry, "")
_, err := MapCatalogEntryToServer(catalogEntry, "", false)
if err == nil {
t.Fatal("Expected error for missing config")
}
Expand Down
6 changes: 1 addition & 5 deletions apiclient/types/zz_generated.deepcopy.go

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

Loading