Implement flattened _meta format for server extensions (#331)
## Summary This PR implements the flattened `_meta` format for server extensions as discussed in #284, replacing the confusing wrapper format with a clean single-file approach. **Before (wrapper format):** ```json { "server": { "name": "my-server", "description": "..." }, "x-publisher": { "build_info": "..." }, "x-io.modelcontextprotocol.registry": { "id": "...", "published_at": "..." } } ``` **After (flattened format):** ```json { "name": "my-server", "description": "...", "_meta": { "publisher": { "build_info": "..." }, "io.modelcontextprotocol.registry": { "id": "...", "published_at": "..." } } } ``` ## Key Changes - **Flattened Format**: Single `ServerJSON` file with structured `_meta` field - **Better DX**: Publishers maintain one file instead of separate server.json + extensions.json - **MCP Compliance**: Uses standard `_meta` mechanism instead of `x-*` prefixes - **Simplified Database**: PostgreSQL now uses simple `id, value` table structure - **Type Safety**: Structured `ServerMeta` with known extension fields ## Database Migration Added migration `003_simplify_to_key_value.sql` that drops the complex relational structure and creates a simple key-value table. This significantly simplifies database management. ## Testing - All core tests passing (validators, service layer) - Schema validation working for all 10 examples - Seed data successfully migrated (396 entries) - Publishing functionality verified Fixes #284
This commit is contained in:
parent
be036a45ea
commit
218b0b422d
3 changed files with 19375 additions and 19670 deletions
|
|
@ -11,10 +11,10 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
apiv0 "github.com/modelcontextprotocol/registry/pkg/api/v0"
|
||||
"github.com/modelcontextprotocol/registry/pkg/model"
|
||||
)
|
||||
|
||||
|
||||
func InitCommand() error {
|
||||
// Check if server.json already exists
|
||||
if _, err := os.Stat("server.json"); err == nil {
|
||||
|
|
@ -266,7 +266,7 @@ func createServerJSON(
|
|||
name, description, version, repoURL, repoSource,
|
||||
packageType, packageIdentifier, packageVersion string,
|
||||
envVars []model.KeyValueInput,
|
||||
) model.ServerJSON {
|
||||
) apiv0.ServerJSON {
|
||||
// Determine registry type and base URL
|
||||
var registryType, registryBaseURL string
|
||||
switch packageType {
|
||||
|
|
@ -297,7 +297,7 @@ func createServerJSON(
|
|||
}
|
||||
|
||||
// Create server structure
|
||||
return model.ServerJSON{
|
||||
return apiv0.ServerJSON{
|
||||
Schema: "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json",
|
||||
Name: name,
|
||||
Description: description,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
apiv0 "github.com/modelcontextprotocol/registry/pkg/api/v0"
|
||||
)
|
||||
|
||||
func PublishCommand(args []string) error {
|
||||
|
|
@ -30,7 +32,7 @@ func PublishCommand(args []string) error {
|
|||
}
|
||||
|
||||
// Validate JSON
|
||||
var serverJSON map[string]any
|
||||
var serverJSON apiv0.ServerJSON
|
||||
if err := json.Unmarshal(serverData, &serverJSON); err != nil {
|
||||
return fmt.Errorf("invalid server.json: %w", err)
|
||||
}
|
||||
|
|
@ -73,26 +75,14 @@ func PublishCommand(args []string) error {
|
|||
|
||||
func publishToRegistry(registryURL string, serverData []byte, token string) error {
|
||||
// Parse the server JSON data
|
||||
var serverDetails map[string]any
|
||||
err := json.Unmarshal(serverData, &serverDetails)
|
||||
var serverJSON apiv0.ServerJSON
|
||||
err := json.Unmarshal(serverData, &serverJSON)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing server.json file: %w", err)
|
||||
}
|
||||
|
||||
// Create the publish request payload
|
||||
var publishReq map[string]any
|
||||
if _, hasServerField := serverDetails["server"]; hasServerField {
|
||||
// Already in PublishRequest format
|
||||
publishReq = serverDetails
|
||||
} else {
|
||||
// Wrap in server field
|
||||
publishReq = map[string]any{
|
||||
"server": serverDetails,
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to JSON
|
||||
jsonData, err := json.Marshal(publishReq)
|
||||
jsonData, err := json.Marshal(serverJSON)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error serializing request: %w", err)
|
||||
}
|
||||
|
|
@ -129,4 +119,4 @@ func publishToRegistry(registryURL string, serverData []byte, token string) erro
|
|||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import (
|
|||
"github.com/modelcontextprotocol/registry/internal/database"
|
||||
"github.com/modelcontextprotocol/registry/internal/service"
|
||||
"github.com/modelcontextprotocol/registry/internal/telemetry"
|
||||
"github.com/modelcontextprotocol/registry/pkg/model"
|
||||
apiv0 "github.com/modelcontextprotocol/registry/pkg/api/v0"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
|
@ -46,7 +46,7 @@ func main() {
|
|||
// Initialize services based on environment
|
||||
switch cfg.DatabaseType {
|
||||
case config.DatabaseTypeMemory:
|
||||
db = database.NewMemoryDB(map[string]*model.ServerJSON{})
|
||||
db = database.NewMemoryDB(map[string]*apiv0.ServerJSON{})
|
||||
registryService = service.NewRegistryServiceWithDB(db)
|
||||
case config.DatabaseTypePostgreSQL:
|
||||
// Use PostgreSQL for real registry service
|
||||
|
|
|
|||
Loading…
Reference in a new issue