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
7 changes: 5 additions & 2 deletions cmd/thv/app/run_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -347,12 +347,15 @@ func BuildRunnerConfig(
return nil, fmt.Errorf("failed to parse environment variables: %w", err)
}

// Resolve registry source URLs when the server was discovered via registry lookup.
// Resolve registry source URLs and server name when the server was discovered via registry lookup.
regAPIURL, regURL := runner.ResolveRegistrySourceURLs(serverMetadata, appConfig)
regServerName := runner.ResolveRegistryServerName(serverMetadata)

// Build the runner config
return buildRunnerConfig(ctx, runFlags, cmdArgs, debugMode, validatedHost, rt, imageURL, serverMetadata,
envVars, envVarValidator, oidcConfig, telemetryConfig, runner.WithRegistrySourceURLs(regAPIURL, regURL))
envVars, envVarValidator, oidcConfig, telemetryConfig,
runner.WithRegistrySourceURLs(regAPIURL, regURL),
runner.WithRegistryServerName(regServerName))
}

// setupOIDCConfiguration sets up OIDC configuration and validates URLs
Expand Down
4 changes: 4 additions & 0 deletions docs/server/docs.go

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

4 changes: 4 additions & 0 deletions docs/server/swagger.json

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

5 changes: 5 additions & 0 deletions docs/server/swagger.yaml

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

4 changes: 3 additions & 1 deletion pkg/api/v1/workload_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,8 +252,9 @@ func (s *WorkloadService) BuildFullRunConfig(
}
}

// Resolve registry source URLs when the server was discovered via registry lookup.
// Resolve registry source URLs and server name when the server was discovered via registry lookup.
regAPIURL, regURL := runner.ResolveRegistrySourceURLs(serverMetadata, s.appConfig)
regServerName := runner.ResolveRegistryServerName(serverMetadata)

options := []runner.RunConfigBuilderOption{
runner.WithRuntime(s.containerRuntime),
Expand Down Expand Up @@ -283,6 +284,7 @@ func (s *WorkloadService) BuildFullRunConfig(
runner.WithToolsOverride(toolsOverride),
runner.WithTelemetryConfigFromFlags("", false, false, false, "", 0.0, nil, false, nil, false),
runner.WithRegistrySourceURLs(regAPIURL, regURL),
runner.WithRegistryServerName(regServerName),
}

// Add header forward configuration if specified
Expand Down
4 changes: 3 additions & 1 deletion pkg/mcp/server/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ func TestBuildServerConfig(t *testing.T) {
expectError: false, // Actually succeeds and tests the type assertion line
},
{
name: "registry source URLs are recorded on config",
name: "registry source URLs and server name are recorded on config",
imageURL: "test/image:latest",
imageMetadata: &regtypes.ImageMetadata{
BaseServerMetadata: regtypes.BaseServerMetadata{
Expand All @@ -321,12 +321,14 @@ func TestBuildServerConfig(t *testing.T) {
},
extraOpts: []runner.RunConfigBuilderOption{
runner.WithRegistrySourceURLs("https://api.example.com", "https://registry.example.com"),
runner.WithRegistryServerName("fetch"),
},
expectError: false,
checkConfig: func(t *testing.T, rc *runner.RunConfig) {
t.Helper()
assert.Equal(t, "https://api.example.com", rc.RegistryAPIURL)
assert.Equal(t, "https://registry.example.com", rc.RegistryURL)
assert.Equal(t, "fetch", rc.RegistryServerName)
},
},
}
Expand Down
6 changes: 4 additions & 2 deletions pkg/mcp/server/run_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,9 @@ func (h *Handler) RunServer(ctx context.Context, request mcp.CallToolRequest) (*
return mcp.NewToolResultError(fmt.Sprintf("Failed to get MCP server: %v", err)), nil
}

// Resolve registry source URLs when the server was discovered via registry lookup.
// Resolve registry source URLs and server name when the server was discovered via registry lookup.
regAPIURL, regURL := runner.ResolveRegistrySourceURLs(serverMetadata, h.configProvider.GetConfig())
regServerName := runner.ResolveRegistryServerName(serverMetadata)

// Build run configuration.
// Use type assertion with nil check to guard against typed nil pointers.
Expand All @@ -61,7 +62,8 @@ func (h *Handler) RunServer(ctx context.Context, request mcp.CallToolRequest) (*
}

runConfig, err := buildServerConfig(ctx, args, imageURL, imageMetadata,
runner.WithRegistrySourceURLs(regAPIURL, regURL))
runner.WithRegistrySourceURLs(regAPIURL, regURL),
runner.WithRegistryServerName(regServerName))
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("Failed to build run configuration: %v", err)), nil
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/runner/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ type RunConfig struct {
// Empty when the server was not discovered via registry lookup.
RegistryURL string `json:"registry_url,omitempty" yaml:"registry_url,omitempty"`

// RegistryServerName is the registry entry name used to look up this server's metadata.
// Empty when the server was not discovered via registry lookup.
RegistryServerName string `json:"registry_server_name,omitempty" yaml:"registry_server_name,omitempty"`

// RemoteAuthConfig contains OAuth configuration for remote MCP servers
RemoteAuthConfig *remote.Config `json:"remote_auth_config,omitempty" yaml:"remote_auth_config,omitempty"`

Expand Down
18 changes: 18 additions & 0 deletions pkg/runner/config_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,24 @@ func ResolveRegistrySourceURLs(serverMetadata regtypes.ServerMetadata, appConfig
return appConfig.RegistryApiUrl, appConfig.RegistryUrl
}

// WithRegistryServerName records the registry entry name used to look up this server's metadata.
func WithRegistryServerName(name string) RunConfigBuilderOption {
return func(b *runConfigBuilder) error {
b.config.RegistryServerName = name
return nil
}
}

// ResolveRegistryServerName returns the registry entry name from server metadata
// when the server was discovered via registry lookup (non-nil metadata).
// Returns empty string when metadata is nil (direct image reference or protocol scheme).
func ResolveRegistryServerName(serverMetadata regtypes.ServerMetadata) string {
if serverMetadata == nil {
return ""
}
return serverMetadata.GetName()
}

// WithRegistryProxyPort sets the proxy port from registry metadata.
// This is used as a fallback when the CLI --proxy-port flag is not set.
func WithRegistryProxyPort(port int) RunConfigBuilderOption {
Expand Down
77 changes: 77 additions & 0 deletions pkg/runner/config_builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1419,3 +1419,80 @@ func TestResolveRegistrySourceURLs(t *testing.T) {
})
}
}

func TestWithRegistryServerName(t *testing.T) {
t.Parallel()

tests := []struct {
name string
input string
expected string
}{
{
name: "name set",
input: "my-server",
expected: "my-server",
},
{
name: "empty name",
input: "",
expected: "",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

builder := &runConfigBuilder{config: NewRunConfig()}
opt := WithRegistryServerName(tt.input)
err := opt(builder)

require.NoError(t, err)
assert.Equal(t, tt.expected, builder.config.RegistryServerName)
})
}
}

func TestResolveRegistryServerName(t *testing.T) {
t.Parallel()

tests := []struct {
name string
serverMetadata regtypes.ServerMetadata
expected string
}{
{
name: "nil metadata returns empty string",
serverMetadata: nil,
expected: "",
},
{
name: "metadata with name set",
serverMetadata: &regtypes.ImageMetadata{
BaseServerMetadata: regtypes.BaseServerMetadata{
Name: "fetch",
},
},
expected: "fetch",
},
{
name: "metadata with empty name",
serverMetadata: &regtypes.ImageMetadata{
BaseServerMetadata: regtypes.BaseServerMetadata{
Name: "",
},
},
expected: "",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

result := ResolveRegistryServerName(tt.serverMetadata)
assert.Equal(t, tt.expected, result)
})
}
}
Loading