Skip to content
Open
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
1 change: 1 addition & 0 deletions internal/database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
6 changes: 5 additions & 1 deletion internal/database/postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -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'")
}

Expand Down
8 changes: 5 additions & 3 deletions internal/service/registry_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
29 changes: 29 additions & 0 deletions internal/service/registry_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.UpdateAllVersionsStatus(ctx, "com.example/deprecated-server", &StatusChangeRequest{NewStatus: model.StatusDeprecated})
require.NoError(t, err, "failed to deprecate server")

tests := []struct {
name string
serverDetail apiv0.ServerJSON
Expand Down Expand Up @@ -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 {
Expand Down
Loading