From 7e4e3ce394d2f5a718c8ddae08d14cf0c22281c9 Mon Sep 17 00:00:00 2001 From: Nova Date: Mon, 8 Jun 2026 09:43:29 +0000 Subject: [PATCH 1/2] fix: skip deprecated/deleted servers in URL uniqueness check (#1193) validateNoDuplicateRemoteURLs was blocking URL reuse when the existing server was deprecated or deleted. Added a Status filter to ServerFilter to allow callers to specify which statuses to match, and updated the validation logic to only check active servers. Changes: - Add Status field to ServerFilter struct - Update postgres query builder to handle Status filter - Filter by active status in validateNoDuplicateRemoteURLs - Add test case for deprecated server URL reuse --- internal/database/database.go | 1 + internal/database/postgres.go | 6 ++++- internal/service/registry_service.go | 8 ++++--- internal/service/registry_service_test.go | 29 +++++++++++++++++++++++ 4 files changed, 40 insertions(+), 4 deletions(-) diff --git a/internal/database/database.go b/internal/database/database.go index b4a797fa..7d81abde 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -28,6 +28,7 @@ type ServerFilter struct { SubstringName *string // for substring search on name Version *string // for exact version matching IsLatest *bool // for filtering latest versions only + Status *string // for exact status filtering (e.g. "active") IncludeDeleted *bool // for including deleted packages in results (default: exclude) } diff --git a/internal/database/postgres.go b/internal/database/postgres.go index b0b2d983..49c1c741 100644 --- a/internal/database/postgres.go +++ b/internal/database/postgres.go @@ -137,7 +137,11 @@ func buildFilterConditions(filter *ServerFilter, argIndex int) ([]string, []any, args = append(args, *filter.IsLatest) argIndex++ } - if filter.IncludeDeleted == nil || !*filter.IncludeDeleted { + if filter.Status != nil { + conditions = append(conditions, fmt.Sprintf("status = $%d", argIndex)) + args = append(args, *filter.Status) + argIndex++ + } else if filter.IncludeDeleted == nil || !*filter.IncludeDeleted { conditions = append(conditions, "status != 'deleted'") } diff --git a/internal/service/registry_service.go b/internal/service/registry_service.go index 16a91000..96934a8d 100644 --- a/internal/service/registry_service.go +++ b/internal/service/registry_service.go @@ -300,10 +300,12 @@ func pickLatestVersion(versions []*apiv0.ServerResponse, allowDeleted bool) *api // validateNoDuplicateRemoteURLs checks that no other server is using the same remote URLs func (s *registryServiceImpl) validateNoDuplicateRemoteURLs(ctx context.Context, tx pgx.Tx, serverDetail apiv0.ServerJSON) error { - // Check each remote URL in the new server for conflicts + // Check each remote URL in the new server for conflicts. + // Only check active servers — deleted and deprecated servers should not block URL reuse. for _, remote := range serverDetail.Remotes { - // Use filter to find servers with this remote URL - filter := &database.ServerFilter{RemoteURL: &remote.URL} + // Use filter to find active servers with this remote URL + activeStatus := string(model.StatusActive) + filter := &database.ServerFilter{RemoteURL: &remote.URL, Status: &activeStatus} conflictingServers, _, err := s.db.ListServers(ctx, tx, filter, "", 1000) if err != nil { diff --git a/internal/service/registry_service_test.go b/internal/service/registry_service_test.go index 05ec8477..f83b0163 100644 --- a/internal/service/registry_service_test.go +++ b/internal/service/registry_service_test.go @@ -52,6 +52,22 @@ func TestValidateNoDuplicateRemoteURLs(t *testing.T) { require.NoError(t, err, "failed to create server: %v", err) } + // Create a deprecated server with a remote URL to test that deprecated servers + // do not block URL reuse (issue #1193). + deprecatedServer := &apiv0.ServerJSON{ + Schema: model.CurrentSchemaURL, + Name: "com.example/deprecated-server", + Description: "A deprecated server", + Version: "1.0.0", + Remotes: []model.Transport{ + {Type: "streamable-http", URL: "https://api.deprecated.example.com/mcp"}, + }, + } + _, err := service.CreateServer(ctx, deprecatedServer) + require.NoError(t, err, "failed to create deprecated server") + _, err = service.SetAllVersionsStatus(ctx, "com.example/deprecated-server", model.StatusDeprecated, nil) + require.NoError(t, err, "failed to deprecate server") + tests := []struct { name string serverDetail apiv0.ServerJSON @@ -110,6 +126,19 @@ func TestValidateNoDuplicateRemoteURLs(t *testing.T) { }, expectError: false, }, + { + name: "duplicate URL from deprecated server - should pass (issue #1193)", + serverDetail: apiv0.ServerJSON{ + Schema: model.CurrentSchemaURL, + Name: "com.example/new-server-reuse-deprecated-url", + Description: "A new server reusing a deprecated server's URL", + Version: "1.0.0", + Remotes: []model.Transport{ + {Type: "streamable-http", URL: "https://api.deprecated.example.com/mcp"}, // Same URL as deprecated server + }, + }, + expectError: false, + }, } for _, tt := range tests { From a1b6a31e8e439976274d88313f43e344f9a54b39 Mon Sep 17 00:00:00 2001 From: Nova Date: Fri, 12 Jun 2026 09:58:18 +0000 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20UpdateAllVersionsStatus=20signature?= =?UTF-8?q?=20change=20(SetAllVersionsStatus=20=E2=86=92=20UpdateAllVersio?= =?UTF-8?q?nsStatus=20with=20StatusChangeRequest)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/service/registry_service_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/registry_service_test.go b/internal/service/registry_service_test.go index f83b0163..3b1e7ae0 100644 --- a/internal/service/registry_service_test.go +++ b/internal/service/registry_service_test.go @@ -65,7 +65,7 @@ func TestValidateNoDuplicateRemoteURLs(t *testing.T) { } _, err := service.CreateServer(ctx, deprecatedServer) require.NoError(t, err, "failed to create deprecated server") - _, err = service.SetAllVersionsStatus(ctx, "com.example/deprecated-server", model.StatusDeprecated, nil) + _, err = service.UpdateAllVersionsStatus(ctx, "com.example/deprecated-server", &StatusChangeRequest{NewStatus: model.StatusDeprecated}) require.NoError(t, err, "failed to deprecate server") tests := []struct {