Fix _meta field format for MCP spec compliance (#367)

## Summary

Updates the `_meta` field structure to be compliant with the MCP
specification by using proper `prefix/name` format for vendor
extensions.

### Changes

- **Registry metadata**: `io.modelcontextprotocol.registry` →
`io.modelcontextprotocol.registry/official`
- **Publisher metadata**: `publisher` →
`io.modelcontextprotocol.registry/publisher-provided`
- **Database migration**: Updates indexes to use new field paths
- **Updated all code**: Service layer, validators, tests, and seed data

### Format Before
```json
{
  "_meta": {
    "publisher": { "license": "MIT" },
    "io.modelcontextprotocol.registry": { "id": "...", "is_latest": true }
  }
}
```

### Format After
```json
{
  "_meta": {
    "io.modelcontextprotocol.registry/publisher-provided": { "license": "MIT" },
    "io.modelcontextprotocol.registry/official": { "id": "...", "is_latest": true }
  }
}
```

This resolves the naming collision with the planned top-level
`publisher` field and ensures compliance with MCP specification
requirements.

Fixes #356
This commit is contained in:
adam jones 2025-09-08 11:40:04 -07:00 • committed by GitHub
commit ef4607720d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -133,8 +133,10 @@ func publishToRegistry(expected *apiv0.ServerJSON, line int) error {
id, err := runPublisher(p)
if err != nil {
log.Printf(" ⛔ Failed to get server ID: %v", err)
return err
}
log.Printf(" 📋 Got server ID: %s", id)
return verifyPublishedServer(id, expected)
}
@ -159,6 +161,9 @@ func runPublisher(filePath string) (string, error) {
return "", fmt.Errorf("failed to get server name from file: %w", err)
}
// Add a small delay to ensure database consistency
time.Sleep(100 * time.Millisecond)
// Find the server in the registry by name
return findServerIDByName(serverName)
}
@ -216,16 +221,24 @@ func findServerIDByName(serverName string) (string, error) {
}
// Find the server with matching name
var foundServers []string
for _, server := range serverList.Servers {
if server.Name == serverName && server.Meta.IOModelContextProtocolRegistry.IsLatest {
return server.Meta.IOModelContextProtocolRegistry.ID, nil
if server.Name == serverName {
foundServers = append(foundServers, fmt.Sprintf("ID:%s IsLatest:%t", server.Meta.Official.ID, server.Meta.Official.IsLatest))
if server.Meta.Official.IsLatest {
return server.Meta.Official.ID, nil
}
}
}
return "", fmt.Errorf("could not find server with name %s", serverName)
if len(foundServers) > 0 {
return "", fmt.Errorf("found server %s but none marked as latest: %v", serverName, foundServers)
}
return "", fmt.Errorf("could not find any server with name %s", serverName)
}
func verifyPublishedServer(id string, expected *apiv0.ServerJSON) error {
log.Printf(" 🔍 Verifying server with ID: %s", id)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, registryURL+"/v0/servers/"+id, nil)
@ -324,12 +337,12 @@ func compareServerJSON(expected, actual *apiv0.ServerJSON) error {
}
// Compare only publisher metadata if present
if expected.Meta != nil && expected.Meta.Publisher != nil {
if actual.Meta == nil || actual.Meta.Publisher == nil {
if expected.Meta != nil && expected.Meta.PublisherProvided != nil {
if actual.Meta == nil || actual.Meta.PublisherProvided == nil {
return fmt.Errorf("expected publisher metadata, but got none")
}
if !reflect.DeepEqual(expected.Meta.Publisher, actual.Meta.Publisher) {
return fmt.Errorf("publisher metadata mismatch: expected %+v, got %+v", expected.Meta.Publisher, actual.Meta.Publisher)
if !reflect.DeepEqual(expected.Meta.PublisherProvided, actual.Meta.PublisherProvided) {
return fmt.Errorf("publisher metadata mismatch: expected %+v, got %+v", expected.Meta.PublisherProvided, actual.Meta.PublisherProvided)
}
}