diff --git a/registry/converters/registry_converters.go b/registry/converters/registry_converters.go index b2b8339..7039a86 100644 --- a/registry/converters/registry_converters.go +++ b/registry/converters/registry_converters.go @@ -47,7 +47,6 @@ func NewUpstreamRegistryFromToolhiveRegistry(toolhiveReg *registry.Registry) (*r }, Data: registry.UpstreamData{ Servers: servers, - Groups: []registry.UpstreamGroup{}, }, }, nil } diff --git a/registry/types/data/upstream-registry.schema.json b/registry/types/data/upstream-registry.schema.json index f96b3b3..a794cc7 100644 --- a/registry/types/data/upstream-registry.schema.json +++ b/registry/types/data/upstream-registry.schema.json @@ -48,35 +48,6 @@ "$ref": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json" } }, - "groups": { - "type": "array", - "description": "Groups of related MCP servers", - "items": { - "type": "object", - "required": [ - "name", - "description", - "servers" - ], - "properties": { - "name": { - "type": "string", - "description": "Unique identifier for the group" - }, - "description": { - "type": "string", - "description": "Description of the group's purpose" - }, - "servers": { - "type": "array", - "description": "Array of servers in this group", - "items": { - "$ref": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json" - } - } - } - } - }, "skills": { "type": "array", "description": "Array of skills in the registry", diff --git a/registry/types/schema_validation.go b/registry/types/schema_validation.go index 2975717..d55bbee 100644 --- a/registry/types/schema_validation.go +++ b/registry/types/schema_validation.go @@ -195,31 +195,13 @@ func validateRegistryExtensions(registryData []byte) error { var errs []string if servers, ok := data["servers"].([]any); ok { - errs = append(errs, validateServerList(servers, "")...) - } - if groups, ok := data["groups"].([]any); ok { - errs = append(errs, validateGroupServers(groups)...) + errs = append(errs, validateServerList(servers)...) } return formatExtensionErrors(errs) } -func validateGroupServers(groups []any) []string { - var errs []string - for _, group := range groups { - groupMap, ok := group.(map[string]any) - if !ok { - continue - } - groupName, _ := groupMap["name"].(string) - if groupServers, ok := groupMap["servers"].([]any); ok { - errs = append(errs, validateServerList(groupServers, groupName)...) - } - } - return errs -} - -func validateServerList(servers []any, groupName string) []string { +func validateServerList(servers []any) []string { var errs []string for i, server := range servers { serverMap, ok := server.(map[string]any) @@ -227,9 +209,6 @@ func validateServerList(servers []any, groupName string) []string { continue } serverName := getServerName(serverMap, i) - if groupName != "" { - serverName = fmt.Sprintf("group[%s].%s", groupName, serverName) - } if err := validateServerExtensions(serverMap, serverName); err != nil { errs = append(errs, err.Error()) } diff --git a/registry/types/schema_validation_test.go b/registry/types/schema_validation_test.go index 528af62..805c8a6 100644 --- a/registry/types/schema_validation_test.go +++ b/registry/types/schema_validation_test.go @@ -28,21 +28,6 @@ func TestValidateUpstreamRegistryBytes(t *testing.T) { }{ { name: "valid registry with all fields", - data: `{ - "$schema": "https://raw.githubusercontent.com/stacklok/toolhive-core/main/registry/types/data/upstream-registry.schema.json", - "version": "1.0.0", - "meta": { - "last_updated": "2024-01-15T10:30:00Z" - }, - "data": { - "servers": [], - "groups": [] - } - }`, - wantErr: false, - }, - { - name: "valid registry without groups (optional)", data: `{ "$schema": "https://raw.githubusercontent.com/stacklok/toolhive-core/main/registry/types/data/upstream-registry.schema.json", "version": "1.0.0", @@ -55,27 +40,6 @@ func TestValidateUpstreamRegistryBytes(t *testing.T) { }`, wantErr: false, }, - { - name: "valid registry with group", - data: `{ - "$schema": "https://raw.githubusercontent.com/stacklok/toolhive-core/main/registry/types/data/upstream-registry.schema.json", - "version": "1.0.0", - "meta": { - "last_updated": "2024-01-15T10:30:00Z" - }, - "data": { - "servers": [], - "groups": [ - { - "name": "test-group", - "description": "Test group", - "servers": [] - } - ] - } - }`, - wantErr: false, - }, { name: "missing meta", data: `{ @@ -150,25 +114,6 @@ func TestValidateUpstreamRegistryBytes(t *testing.T) { wantErr: true, errorContains: "date-time", }, - { - name: "missing required group fields", - data: `{ - "version": "1.0.0", - "meta": { - "last_updated": "2024-01-15T10:30:00Z" - }, - "data": { - "servers": [], - "groups": [ - { - "name": "incomplete-group" - } - ] - } - }`, - wantErr: true, - errorContains: errKeyDescription, - }, } for _, tt := range tests { @@ -207,8 +152,7 @@ func TestValidateUpstreamRegistry_RealWorld(t *testing.T) { "version": "1.0.0", "title": "Test Server" } - ], - "groups": [] + ] } }` @@ -736,77 +680,6 @@ func TestValidateUpstreamRegistry_WithExtensions(t *testing.T) { wantErr: true, errorContains: errKeyTier, }, - { - name: "valid registry with extensions in groups", - data: `{ - "version": "1.0.0", - "meta": { - "last_updated": "2024-01-15T10:30:00Z" - }, - "data": { - "servers": [], - "groups": [ - { - "name": "test-group", - "description": "A test group", - "servers": [ - { - "name": "io.github.stacklok/grouped-server", - "description": "A grouped server", - "version": "1.0.0", - "_meta": { - "io.modelcontextprotocol.registry/publisher-provided": { - "io.github.stacklok": { - "ghcr.io/test/grouped:v1.0.0": { - "status": "active" - } - } - } - } - } - ] - } - ] - } - }`, - wantErr: false, - }, - { - name: "invalid extensions in group server", - data: `{ - "version": "1.0.0", - "meta": { - "last_updated": "2024-01-15T10:30:00Z" - }, - "data": { - "servers": [], - "groups": [ - { - "name": "test-group", - "description": "A test group", - "servers": [ - { - "name": "io.github.stacklok/grouped-server", - "description": "A grouped server", - "version": "1.0.0", - "_meta": { - "io.modelcontextprotocol.registry/publisher-provided": { - "io.github.stacklok": { - "ghcr.io/test/grouped:v1.0.0": { - "status": "invalid-status" - } - } - } - } - } - ] - } - ] - } - }`, - wantErr: true, - errorContains: errKeyStatus, - }, } for _, tt := range tests { @@ -1332,24 +1205,6 @@ func TestUpstreamRegistrySchemaVersionSync(t *testing.T) { schemaPath, schemaDate, expectedDate, expectedDate, expectedDate) } - // Also check groups schema if present - groupServerItems, ok := walkJSONObjects(schema, "properties", "data", "properties", "groups", "items", "properties", "servers", "items") - if ok { - groupRefURL, ok := groupServerItems["$ref"].(string) - if ok { - groupMatches := re.FindStringSubmatch(groupRefURL) - if len(groupMatches) == 2 { - groupSchemaDate := groupMatches[1] - if groupSchemaDate != expectedDate { - t.Errorf("Groups schema version mismatch!\n"+ - " Groups $ref date: %s\n"+ - " Expected: %s\n\n"+ - "To fix: Update data.properties.groups.items.properties.servers.items.$ref", - groupSchemaDate, expectedDate) - } - } - } - } } // findExternalRefs recursively walks a parsed JSON value and collects all diff --git a/registry/types/upstream_registry.go b/registry/types/upstream_registry.go index 3c6ec89..256a4f2 100644 --- a/registry/types/upstream_registry.go +++ b/registry/types/upstream_registry.go @@ -12,7 +12,7 @@ const UpstreamRegistrySchemaURL = "https://raw.githubusercontent.com/stacklok/to "registry/types/data/upstream-registry.schema.json" // UpstreamRegistry is the unified registry format that stores servers in upstream -// ServerJSON format with proper meta/data separation and groups support. +// ServerJSON format with proper meta/data separation. type UpstreamRegistry struct { // Schema is the JSON schema URL for validation Schema string `json:"$schema" yaml:"$schema"` @@ -33,26 +33,11 @@ type UpstreamMeta struct { LastUpdated string `json:"last_updated" yaml:"last_updated"` } -// UpstreamData contains the actual registry content (servers, groups, and skills) +// UpstreamData contains the actual registry content (servers and skills) type UpstreamData struct { // Servers contains the server definitions in upstream MCP format Servers []upstreamv0.ServerJSON `json:"servers" yaml:"servers"` - // Groups contains grouped collections of servers (optional, for future use) - Groups []UpstreamGroup `json:"groups,omitempty" yaml:"groups,omitempty"` - // Skills contains the skill definitions Skills []Skill `json:"skills,omitempty" yaml:"skills,omitempty"` } - -// UpstreamGroup represents a named collection of related MCP servers -type UpstreamGroup struct { - // Name is the unique identifier for the group - Name string `json:"name" yaml:"name"` - - // Description explains the purpose of this group - Description string `json:"description" yaml:"description"` - - // Servers contains the server definitions in this group - Servers []upstreamv0.ServerJSON `json:"servers" yaml:"servers"` -} diff --git a/registry/types/upstream_registry_test.go b/registry/types/upstream_registry_test.go index 1e5bf81..1d89d7b 100644 --- a/registry/types/upstream_registry_test.go +++ b/registry/types/upstream_registry_test.go @@ -24,7 +24,6 @@ func TestUpstreamRegistry_JSONSerialization(t *testing.T) { }, Data: UpstreamData{ Servers: []upstreamv0.ServerJSON{}, - Groups: []UpstreamGroup{}, }, } @@ -54,7 +53,6 @@ func TestUpstreamRegistry_YAMLSerialization(t *testing.T) { }, Data: UpstreamData{ Servers: []upstreamv0.ServerJSON{}, - Groups: []UpstreamGroup{}, }, } @@ -72,36 +70,6 @@ func TestUpstreamRegistry_YAMLSerialization(t *testing.T) { assert.Equal(t, registry.Meta.LastUpdated, decoded.Meta.LastUpdated) } -func TestUpstreamRegistry_WithGroups(t *testing.T) { - t.Parallel() - registry := &UpstreamRegistry{ - Schema: UpstreamRegistrySchemaURL, - Version: testVersion, - Meta: UpstreamMeta{ - LastUpdated: time.Now().Format(time.RFC3339), - }, - Data: UpstreamData{ - Servers: []upstreamv0.ServerJSON{}, - Groups: []UpstreamGroup{ - { - Name: "test-group", - Description: "Test group for testing", - Servers: []upstreamv0.ServerJSON{}, - }, - }, - }, - } - - jsonData, err := json.Marshal(registry) - require.NoError(t, err) - - var decoded UpstreamRegistry - err = json.Unmarshal(jsonData, &decoded) - require.NoError(t, err) - assert.Len(t, decoded.Data.Groups, 1) - assert.Equal(t, "test-group", decoded.Data.Groups[0].Name) -} - func TestUpstreamRegistry_SchemaField(t *testing.T) { t.Parallel() @@ -113,7 +81,6 @@ func TestUpstreamRegistry_SchemaField(t *testing.T) { }, Data: UpstreamData{ Servers: []upstreamv0.ServerJSON{}, - Groups: []UpstreamGroup{}, }, } @@ -155,7 +122,7 @@ func TestRegistryMeta_TimeFormat(t *testing.T) { func TestRegistryData_EmptyOptionalFields(t *testing.T) { t.Parallel() - // Test that groups and skills can be omitted (omitempty) + // Test that skills can be omitted (omitempty) data := UpstreamData{ Servers: []upstreamv0.ServerJSON{}, } @@ -163,18 +130,15 @@ func TestRegistryData_EmptyOptionalFields(t *testing.T) { jsonData, err := json.Marshal(data) require.NoError(t, err) - // Groups and skills should not appear in JSON when nil (omitempty behavior) - assert.NotContains(t, string(jsonData), "groups") + // Skills should not appear in JSON when nil (omitempty behavior) assert.NotContains(t, string(jsonData), "skills") - // Test with empty slices - also omitted due to omitempty - data.Groups = []UpstreamGroup{} + // Test with empty slice - also omitted due to omitempty data.Skills = []Skill{} jsonData, err = json.Marshal(data) require.NoError(t, err) // Empty arrays are also omitted with omitempty - assert.NotContains(t, string(jsonData), "groups") assert.NotContains(t, string(jsonData), "skills") } @@ -219,30 +183,3 @@ func TestUpstreamRegistry_WithSkills(t *testing.T) { require.Len(t, decoded.Data.Skills[0].Packages, 1) assert.Equal(t, "oci", decoded.Data.Skills[0].Packages[0].RegistryType) } - -func TestRegistryGroup_Structure(t *testing.T) { - t.Parallel() - - group := UpstreamGroup{ - Name: "test-group", - Description: "A test group for testing purposes", - Servers: []upstreamv0.ServerJSON{ - { - Name: "io.test/server1", - Description: "Test server 1", - Version: testVersion, - }, - }, - } - - jsonData, err := json.Marshal(group) - require.NoError(t, err) - - var decoded UpstreamGroup - err = json.Unmarshal(jsonData, &decoded) - require.NoError(t, err) - assert.Equal(t, group.Name, decoded.Name) - assert.Equal(t, group.Description, decoded.Description) - assert.Len(t, decoded.Servers, 1) - assert.Equal(t, "io.test/server1", decoded.Servers[0].Name) -}