feat: refactor to account for updated openAPI schema

* Update the model to handle new changes to schema based on https://github.com/modelcontextprotocol/registry/pull/33

* Made the import script a little simpler.
* Add a new updated seed file
* update template mcp.json file for publisher
This commit is contained in:
Avinash Sridhar 2025-05-16 22:49:27 -04:00 • committed by Toby Padilla
commit 5c238eceef
11 changed files with 17637 additions and 359 deletions

17446
data/seed_2025_05_16.json Normal file

File diff suppressed because it is too large Load diff

View file

@ -13,8 +13,8 @@ import (
// Response is a paginated API response // Response is a paginated API response
type PaginatedResponse struct { type PaginatedResponse struct {
Data []model.Entry `json:"servers"` Data []model.Server `json:"servers"`
Metadata Metadata `json:"metadata,omitempty"` Metadata Metadata `json:"metadata,omitempty"`
} }
// Metadata contains pagination metadata // Metadata contains pagination metadata

View file

@ -18,7 +18,7 @@ var (
// Database defines the interface for database operations on MCPRegistry entries // Database defines the interface for database operations on MCPRegistry entries
type Database interface { type Database interface {
// List retrieves all MCPRegistry entries with optional filtering // List retrieves all MCPRegistry entries with optional filtering
List(ctx context.Context, filter map[string]interface{}, cursor string, limit int) ([]*model.Entry, string, error) List(ctx context.Context, filter map[string]interface{}, cursor string, limit int) ([]*model.Server, string, error)
// GetByID retrieves a single ServerDetail by it's ID // GetByID retrieves a single ServerDetail by it's ID
GetByID(ctx context.Context, id string) (*model.ServerDetail, error) GetByID(ctx context.Context, id string) (*model.ServerDetail, error)
// Publish adds a new ServerDetail to the database // Publish adds a new ServerDetail to the database

View file

@ -60,231 +60,26 @@ func readSeedFile(path string) ([]model.ServerDetail, error) {
return nil, fmt.Errorf("failed to parse JSON: %v (original error: %v)", jsonErr, err) return nil, fmt.Errorf("failed to parse JSON: %v (original error: %v)", jsonErr, err)
} }
// Convert raw data to model format
servers = convertToServerDetails(rawData)
} }
log.Printf("Found %d server entries in seed file", len(servers)) log.Printf("Found %d server entries in seed file", len(servers))
return servers, nil return servers, nil
} }
// convertToServerDetails converts raw JSON data to ServerDetail structs
func convertToServerDetails(rawData []map[string]interface{}) []model.ServerDetail {
servers := make([]model.ServerDetail, 0, len(rawData))
for _, data := range rawData {
server := model.ServerDetail{}
// Extract basic fields
if id, ok := data["id"].(string); ok {
server.ID = id
}
if name, ok := data["name"].(string); ok {
server.Name = name
}
if desc, ok := data["description"].(string); ok {
server.Description = desc
}
if version, ok := data["version"].(string); ok {
if version == "" || version == "latest" {
version = "0.0.1"
}
server.VersionDetail = model.VersionDetail{
Version: version,
ReleaseDate: time.Now().Format(time.RFC3339),
IsLatest: true,
}
}
// Extract repository
if repo, ok := data["repository"].(map[string]interface{}); ok {
server.Repository = model.Repository{
URL: getStringValue(repo, "url"),
SubFolder: getStringValue(repo, "subfolder"),
Branch: getStringValue(repo, "branch"),
Commit: getStringValue(repo, "commit"),
}
}
// Extract registries
if registries, ok := data["registries"].([]interface{}); ok {
for _, reg := range registries {
if regMap, ok := reg.(map[string]interface{}); ok {
registry := model.Registries{
Name: getStringValue(regMap, "name"),
PackageName: getStringValue(regMap, "packagename"),
License: getStringValue(regMap, "license"),
}
// Handle command arguments if present
if cmd, ok := regMap["command_arguments"].(map[string]interface{}); ok {
commandArgs := model.Command{}
// Extract sub commands
if subCmds, ok := cmd["sub_commands"].([]interface{}); ok {
for _, sc := range subCmds {
if scMap, ok := sc.(map[string]interface{}); ok {
subCmd := model.SubCommand{
Name: getStringValue(scMap, "name"),
Description: getStringValue(scMap, "description"),
}
// Extract named arguments in sub commands
if namedArgs, ok := scMap["named_arguments"].([]interface{}); ok {
for _, na := range namedArgs {
if naMap, ok := na.(map[string]interface{}); ok {
namedArg := extractNamedArgument(naMap)
subCmd.NamedArguments = append(subCmd.NamedArguments, namedArg)
}
}
}
commandArgs.SubCommands = append(commandArgs.SubCommands, subCmd)
}
}
}
// Extract positional arguments
if posArgs, ok := cmd["positional_arguments"].([]interface{}); ok {
for _, pa := range posArgs {
if paMap, ok := pa.(map[string]interface{}); ok {
posArg := model.PositionalArgument{}
if pos, ok := paMap["position"].(float64); ok {
posArg.Position = int(pos)
}
if arg, ok := paMap["argument"].(map[string]interface{}); ok {
posArg.Argument = extractArgument(arg)
}
commandArgs.PositionalArguments = append(commandArgs.PositionalArguments, posArg)
}
}
}
// Extract environment variables
if envVars, ok := cmd["environment_variables"].([]interface{}); ok {
for _, ev := range envVars {
if evMap, ok := ev.(map[string]interface{}); ok {
envVar := model.EnvironmentVariable{
Name: getStringValue(evMap, "name"),
Description: getStringValue(evMap, "description"),
}
if required, ok := evMap["required"].(bool); ok {
envVar.Required = required
}
commandArgs.EnvironmentVariables = append(commandArgs.EnvironmentVariables, envVar)
}
}
}
// Extract named arguments
if namedArgs, ok := cmd["named_arguments"].([]interface{}); ok {
for _, na := range namedArgs {
if naMap, ok := na.(map[string]interface{}); ok {
namedArg := extractNamedArgument(naMap)
commandArgs.NamedArguments = append(commandArgs.NamedArguments, namedArg)
}
}
}
registry.CommandArguments = commandArgs
}
server.Registries = append(server.Registries, registry)
}
}
}
// Extract remotes
if remotes, ok := data["remotes"].([]interface{}); ok {
for _, rem := range remotes {
if remMap, ok := rem.(map[string]interface{}); ok {
remote := model.Remotes{
TransportType: getStringValue(remMap, "transport_type"),
Url: getStringValue(remMap, "url"),
}
server.Remotes = append(server.Remotes, remote)
}
}
}
servers = append(servers, server)
}
return servers
}
// extractArgument extracts an Argument struct from a map
func extractArgument(data map[string]interface{}) model.Argument {
arg := model.Argument{
Name: getStringValue(data, "name"),
Description: getStringValue(data, "description"),
DefaultValue: getStringValue(data, "default_value"),
}
// Extract boolean fields
if isRequired, ok := data["is_required"].(bool); ok {
arg.IsRequired = isRequired
}
if isEditable, ok := data["is_editable"].(bool); ok {
arg.IsEditable = isEditable
}
if isRepeatable, ok := data["is_repeatable"].(bool); ok {
arg.IsRepeatable = isRepeatable
}
// Extract string array for choices
if choices, ok := data["choices"].([]interface{}); ok {
for _, choice := range choices {
if strChoice, ok := choice.(string); ok {
arg.Choices = append(arg.Choices, strChoice)
}
}
}
return arg
}
// extractNamedArgument extracts a NamedArguments struct from a map
func extractNamedArgument(data map[string]interface{}) model.NamedArguments {
namedArg := model.NamedArguments{
ShortFlag: getStringValue(data, "short_flag"),
LongFlag: getStringValue(data, "long_flag"),
}
if requiresValue, ok := data["requires_value"].(bool); ok {
namedArg.RequiresValue = requiresValue
}
if arg, ok := data["argument"].(map[string]interface{}); ok {
namedArg.Argument = extractArgument(arg)
}
return namedArg
}
// getStringValue safely extracts a string value from a map
func getStringValue(data map[string]interface{}, key string) string {
if val, ok := data[key].(string); ok {
return val
}
return ""
}
// importData imports the seed data into MongoDB // importData imports the seed data into MongoDB
func importData(ctx context.Context, collection *mongo.Collection, servers []model.ServerDetail) { func importData(ctx context.Context, collection *mongo.Collection, servers []model.ServerDetail) {
log.Printf("Importing %d servers into collection %s", len(servers), collection.Name()) log.Printf("Importing %d servers into collection %s", len(servers), collection.Name())
for i, server := range servers { for i, server := range servers {
if server.ID == "" || server.Name == "" {
log.Printf("Skipping server %d: ID or Name is empty", i+1)
continue
}
// Create filter based on server ID // Create filter based on server ID
filter := bson.M{"id": server.ID} filter := bson.M{"id": server.ID}
if server.VersionDetail.Version == "" { if server.VersionDetail.Version == "" {
server.VersionDetail.Version = "0.0.1" server.VersionDetail.Version = "0.0.1-seed"
server.VersionDetail.ReleaseDate = time.Now().Format(time.RFC3339) server.VersionDetail.ReleaseDate = time.Now().Format(time.RFC3339)
server.VersionDetail.IsLatest = true server.VersionDetail.IsLatest = true

View file

@ -10,19 +10,19 @@ import (
// MemoryDB is an in-memory implementation of the Database interface // MemoryDB is an in-memory implementation of the Database interface
type MemoryDB struct { type MemoryDB struct {
entries map[string]*model.Entry entries map[string]*model.Server
mu sync.RWMutex mu sync.RWMutex
} }
// NewMemoryDB creates a new instance of the in-memory database // NewMemoryDB creates a new instance of the in-memory database
func NewMemoryDB(e map[string]*model.Entry) *MemoryDB { func NewMemoryDB(e map[string]*model.Server) *MemoryDB {
return &MemoryDB{ return &MemoryDB{
entries: e, entries: e,
} }
} }
// List retrieves all MCPRegistry entries with optional filtering and pagination // List retrieves all MCPRegistry entries with optional filtering and pagination
func (db *MemoryDB) List(ctx context.Context, filter map[string]interface{}, cursor string, limit int) ([]*model.Entry, string, error) { func (db *MemoryDB) List(ctx context.Context, filter map[string]interface{}, cursor string, limit int) ([]*model.Server, string, error) {
if ctx.Err() != nil { if ctx.Err() != nil {
return nil, "", ctx.Err() return nil, "", ctx.Err()
} }
@ -35,14 +35,14 @@ func (db *MemoryDB) List(ctx context.Context, filter map[string]interface{}, cur
defer db.mu.RUnlock() defer db.mu.RUnlock()
// Convert all entries to a slice for pagination // Convert all entries to a slice for pagination
var allEntries []*model.Entry var allEntries []*model.Server
for _, entry := range db.entries { for _, entry := range db.entries {
entryCopy := *entry entryCopy := *entry
allEntries = append(allEntries, &entryCopy) allEntries = append(allEntries, &entryCopy)
} }
// Simple filtering implementation // Simple filtering implementation
var filteredEntries []*model.Entry var filteredEntries []*model.Server
for _, entry := range allEntries { for _, entry := range allEntries {
include := true include := true
@ -98,11 +98,11 @@ func (db *MemoryDB) List(ctx context.Context, filter map[string]interface{}, cur
endIdx = len(filteredEntries) endIdx = len(filteredEntries)
} }
var result []*model.Entry var result []*model.Server
if startIdx < len(filteredEntries) { if startIdx < len(filteredEntries) {
result = filteredEntries[startIdx:endIdx] result = filteredEntries[startIdx:endIdx]
} else { } else {
result = []*model.Entry{} result = []*model.Server{}
} }
// Determine next cursor // Determine next cursor
@ -125,11 +125,13 @@ func (db *MemoryDB) GetByID(ctx context.Context, id string) (*model.ServerDetail
if entry, exists := db.entries[id]; exists { if entry, exists := db.entries[id]; exists {
return &model.ServerDetail{ return &model.ServerDetail{
ID: entry.ID, Server: model.Server{
Name: entry.Name, ID: entry.ID,
Description: entry.Description, Name: entry.Name,
VersionDetail: entry.VersionDetail, Description: entry.Description,
Repository: entry.Repository, VersionDetail: entry.VersionDetail,
Repository: entry.Repository,
},
}, nil }, nil
} }
@ -162,7 +164,7 @@ func (db *MemoryDB) Publish(ctx context.Context, serverDetail *model.ServerDetai
return ErrInvalidInput return ErrInvalidInput
} }
db.entries[serverDetail.ID] = &model.Entry{ db.entries[serverDetail.ID] = &model.Server{
ID: serverDetail.ID, ID: serverDetail.ID,
Name: serverDetail.Name, Name: serverDetail.Name,
Description: serverDetail.Description, Description: serverDetail.Description,

View file

@ -72,7 +72,7 @@ func NewMongoDB(ctx context.Context, connectionURI, databaseName, collectionName
} }
// List retrieves MCPRegistry entries with optional filtering and pagination // List retrieves MCPRegistry entries with optional filtering and pagination
func (db *MongoDB) List(ctx context.Context, filter map[string]interface{}, cursor string, limit int) ([]*model.Entry, string, error) { func (db *MongoDB) List(ctx context.Context, filter map[string]interface{}, cursor string, limit int) ([]*model.Server, string, error) {
if limit <= 0 { if limit <= 0 {
// Set default limit if not provided // Set default limit if not provided
limit = 10 limit = 10
@ -84,14 +84,16 @@ func (db *MongoDB) List(ctx context.Context, filter map[string]interface{}, curs
// Convert Go map to MongoDB filter // Convert Go map to MongoDB filter
mongoFilter := bson.M{ mongoFilter := bson.M{
"versiondetail.islatest": true, "version_detail.is_latest": true,
} }
// Map common filter keys to MongoDB document paths // Map common filter keys to MongoDB document paths
for k, v := range filter { for k, v := range filter {
// Handle nested fields with dot notation // Handle nested fields with dot notation
switch k { switch k {
case "publisher.trusted": case "version":
mongoFilter["publisher.trusted"] = v mongoFilter["version_detail.version"] = v
case "name":
mongoFilter["name"] = v
default: default:
mongoFilter[k] = v mongoFilter[k] = v
} }
@ -108,7 +110,7 @@ func (db *MongoDB) List(ctx context.Context, filter map[string]interface{}, curs
} }
// Fetch the document at the cursor to get its sort values // Fetch the document at the cursor to get its sort values
var cursorDoc model.Entry var cursorDoc model.Server
err := db.collection.FindOne(ctx, bson.M{"id": cursor}).Decode(&cursorDoc) err := db.collection.FindOne(ctx, bson.M{"id": cursor}).Decode(&cursorDoc)
if err != nil { if err != nil {
if err == mongo.ErrNoDocuments { if err == mongo.ErrNoDocuments {
@ -138,7 +140,7 @@ func (db *MongoDB) List(ctx context.Context, filter map[string]interface{}, curs
defer mongoCursor.Close(ctx) defer mongoCursor.Close(ctx)
// Decode results // Decode results
var results []*model.Entry var results []*model.Server
if err = mongoCursor.All(ctx, &results); err != nil { if err = mongoCursor.All(ctx, &results); err != nil {
return nil, "", err return nil, "", err
} }
@ -183,8 +185,8 @@ func (db *MongoDB) Publish(ctx context.Context, serverDetail *model.ServerDetail
} }
// find a server detail with the same name and check that the current version is greater than the existing one // find a server detail with the same name and check that the current version is greater than the existing one
filter := bson.M{ filter := bson.M{
"name": serverDetail.Name, "name": serverDetail.Name,
"versiondetail.islatest": true, "version_detail.is_latest": true,
} }
var existingEntry model.ServerDetail var existingEntry model.ServerDetail

View file

@ -24,88 +24,105 @@ type PublishRequest struct {
AuthStatusToken string `json:"-"` // Used internally for device flows AuthStatusToken string `json:"-"` // Used internally for device flows
} }
type Entry struct { // Repository represents a source code repository as defined in the spec
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
Repository Repository `json:"repository,omitempty"`
VersionDetail VersionDetail `json:"version_detail,omitempty"`
}
type Repository struct { type Repository struct {
ID string `json:"id,omitempty"` URL string `json:"url" bson:"url"`
URL string `json:"url,omitempty"` Source string `json:"source" bson:"source"`
Source string `json:"source,omitempty"` ID string `json:"id" bson:"id"`
SubFolder string `json:"subfolder,omitempty"`
Branch string `json:"branch,omitempty"`
Commit string `json:"commit,omitempty"`
} }
type VersionDetail struct { // ServerList represents the response for listing servers as defined in the spec
Version string `json:"version,omitempty"` type ServerList struct {
ReleaseDate string `json:"release_date,omitempty"` //RFC 3339 date format Servers []Server `json:"servers" bson:"servers"`
IsLatest bool `json:"is_latest,omitempty"` Next string `json:"next,omitempty" bson:"next,omitempty"`
TotalCount int `json:"total_count" bson:"total_count"`
} }
type ServerDetail struct { // create an enum for Format
ID string `json:"id,omitempty"` type Format string
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"` const (
VersionDetail VersionDetail `json:"version_detail,omitempty"` FormatString Format = "string"
Repository Repository `json:"repository,omitempty"` FormatNumber Format = "number"
PackageCanonical string `json:"package_canonical,omitempty"` FormatBoolean Format = "boolean"
Registries []Registries `json:"registries,omitempty"` FormatFilePath Format = "file_path"
Remotes []Remotes `json:"remotes,omitempty"` )
// UserInput represents a user input as defined in the spec
type Input struct {
Description string `json:"description,omitempty" bson:"description,omitempty"`
IsRequired bool `json:"is_required,omitempty" bson:"is_required,omitempty"`
Format Format `json:"format,omitempty" bson:"format,omitempty"`
Value string `json:"value,omitempty" bson:"value,omitempty"`
IsSecret bool `json:"is_secret,omitempty" bson:"is_secret,omitempty"`
Default string `json:"default,omitempty" bson:"default,omitempty"`
Choices []string `json:"choices,omitempty" bson:"choices,omitempty"`
Template string `json:"template,omitempty" bson:"template,omitempty"`
Properties map[string]Input `json:"properties,omitempty" bson:"properties,omitempty"`
} }
type Registries struct { type InputWithVariables struct {
Name string `json:"name,omitempty"` Input `json:",inline" bson:",inline"`
PackageName string `json:"package_name,omitempty"` Variables map[string]Input `json:"variables,omitempty" bson:"variables,omitempty"`
License string `json:"license,omitempty"`
CommandArguments Command `json:"command_arguments,omitempty"`
} }
type Remotes struct { type KeyValueInput struct {
TransportType string `json:"transport_type,omitempty"` InputWithVariables `json:",inline" bson:",inline"`
Url string `json:"url,omitempty"` Name string `json:"name" bson:"name"`
} }
type ArgumentType string
type Command struct { const (
SubCommands []SubCommand `json:"sub_commands,omitempty"` ArgumentTypePositional ArgumentType = "positional"
PositionalArguments []PositionalArgument `json:"positional_arguments,omitempty"` ArgumentTypeNamed ArgumentType = "named"
EnvironmentVariables []EnvironmentVariable `json:"environment_variables,omitempty"` )
NamedArguments []NamedArguments `json:"named_arguments,omitempty"`
}
type EnvironmentVariable struct {
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
Required bool `json:"required,omitempty"`
}
// RuntimeArgument defines a type that can be either a PositionalArgument or a NamedArgument
type Argument struct { type Argument struct {
Name string `json:"name,omitempty"` InputWithVariables `json:",inline" bson:",inline"`
Description string `json:"description,omitempty"` Type ArgumentType `json:"type" bson:"type"`
DefaultValue string `json:"default_value,omitempty"` Name string `json:"name,omitempty" bson:"name,omitempty"`
IsRequired bool `json:"is_required,omitempty"` IsRepeated bool `json:"is_repeated,omitempty" bson:"is_repeated,omitempty"`
IsEditable bool `json:"is_editable,omitempty"` ValueHint string `json:"value_hint,omitempty" bson:"value_hint,omitempty"`
IsRepeatable bool `json:"is_repeatable,omitempty"`
Choices []string `json:"choices,omitempty"`
} }
type PositionalArgument struct { type Package struct {
Position int `json:"position,omitempty"` RegistryName string `json:"registry_name" bson:"registry_name"`
Argument Argument `json:"argument,omitempty"` Name string `json:"name" bson:"name"`
Version string `json:"version" bson:"version"`
RunTimeHint string `json:"runtime_hint,omitempty" bson:"runtime_hint,omitempty"`
RuntimeArguments []Argument `json:"runtime_arguments,omitempty" bson:"runtime_arguments,omitempty"`
PackageArguments []Argument `json:"package_arguments,omitempty" bson:"package_arguments,omitempty"`
EnvironmentVariables []KeyValueInput `json:"environment_variables,omitempty" bson:"environment_variables,omitempty"`
} }
type SubCommand struct { // Remote represents a remote connection endpoint
Name string `json:"name,omitempty"` type Remote struct {
Description string `json:"description,omitempty"` TransportType string `json:"transport_type" bson:"transport_type"`
NamedArguments []NamedArguments `json:"named_arguments,omitempty"` URL string `json:"url" bson:"url"`
Headers []Input `json:"headers,omitempty" bson:"headers,omitempty"`
} }
type NamedArguments struct {
ShortFlag string `json:"short_flag,omitempty"` // VersionDetail represents the version details of a server
LongFlag string `json:"long_flag,omitempty"` type VersionDetail struct {
RequiresValue bool `json:"requires_value,omitempty"` Version string `json:"version" bson:"version"`
Argument Argument `json:"argument,omitempty"` ReleaseDate string `json:"release_date" bson:"release_date"`
IsLatest bool `json:"is_latest" bson:"is_latest"`
}
// Server represents a basic server information as defined in the spec
type Server struct {
ID string `json:"id" bson:"id"`
Name string `json:"name" bson:"name"`
Description string `json:"description" bson:"description"`
Repository Repository `json:"repository" bson:"repository"`
VersionDetail VersionDetail `json:"version_detail" bson:"version_detail"`
}
// ServerDetail represents detailed server information as defined in the spec
type ServerDetail struct {
Server `json:",inline" bson:",inline"`
PackageCanonical string `json:"package_canonical,omitempty" bson:"package_canonical,omitempty"`
Packages []Package `json:"packages,omitempty" bson:"packages,omitempty"`
Remotes []Remote `json:"remotes,omitempty" bson:"remotes,omitempty"`
} }

View file

@ -17,16 +17,15 @@ type fakeRegistryService struct {
// NewFakeRegistryService creates a new fake registry service with pre-populated data // NewFakeRegistryService creates a new fake registry service with pre-populated data
func NewFakeRegistryService() RegistryService { func NewFakeRegistryService() RegistryService {
// Sample registry entries with updated model structure // Sample registry entries with updated model structure
registries := []*model.Entry{ registries := []*model.Server{
{ {
ID: uuid.New().String(), ID: uuid.New().String(),
Name: "bluegreen/mcp-server", Name: "bluegreen/mcp-server",
Description: "A dummy MCP registry for testing", Description: "A dummy MCP registry for testing",
Repository: model.Repository{ Repository: model.Repository{
URL: "https://github.com/example/mcp-1", URL: "https://github.com/example/mcp-1",
SubFolder: "server", Source: "github",
Branch: "main", ID: "example/mcp-1",
Commit: "abc123def456",
}, },
VersionDetail: model.VersionDetail{ VersionDetail: model.VersionDetail{
Version: "1.0.0", Version: "1.0.0",
@ -39,10 +38,9 @@ func NewFakeRegistryService() RegistryService {
Name: "orangepurple/mcp-server", Name: "orangepurple/mcp-server",
Description: "Another dummy MCP registry for testing", Description: "Another dummy MCP registry for testing",
Repository: model.Repository{ Repository: model.Repository{
URL: "https://github.com/example/mcp-2", URL: "https://github.com/example/mcp-2",
SubFolder: "mcp", Source: "github",
Branch: "develop", ID: "example/mcp-2",
Commit: "def456ghi789",
}, },
VersionDetail: model.VersionDetail{ VersionDetail: model.VersionDetail{
Version: "0.9.0", Version: "0.9.0",
@ -55,10 +53,9 @@ func NewFakeRegistryService() RegistryService {
Name: "greenyellow/mcp-server", Name: "greenyellow/mcp-server",
Description: "Yet another dummy MCP registry for testing", Description: "Yet another dummy MCP registry for testing",
Repository: model.Repository{ Repository: model.Repository{
URL: "https://github.com/example/mcp-3", URL: "https://github.com/example/mcp-3",
SubFolder: "", Source: "github",
Branch: "feature/mcp-server", ID: "example/mcp-3",
Commit: "789jkl012mno",
}, },
VersionDetail: model.VersionDetail{ VersionDetail: model.VersionDetail{
Version: "0.9.5", Version: "0.9.5",
@ -69,7 +66,7 @@ func NewFakeRegistryService() RegistryService {
} }
// Create a new in-memory database // Create a new in-memory database
registryMap := make(map[string]*model.Entry) registryMap := make(map[string]*model.Server)
for _, entry := range registries { for _, entry := range registries {
registryMap[entry.ID] = entry registryMap[entry.ID] = entry
} }
@ -80,7 +77,7 @@ func NewFakeRegistryService() RegistryService {
} }
// List retrieves MCPRegistry entries with optional filtering and pagination // List retrieves MCPRegistry entries with optional filtering and pagination
func (s *fakeRegistryService) List(cursor string, limit int) ([]model.Entry, string, error) { func (s *fakeRegistryService) List(cursor string, limit int) ([]model.Server, string, error) {
// Create a timeout context for the database operation // Create a timeout context for the database operation
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel() defer cancel()
@ -90,9 +87,8 @@ func (s *fakeRegistryService) List(cursor string, limit int) ([]model.Entry, str
if err != nil { if err != nil {
return nil, "", err return nil, "", err
} }
// Convert from []*model.Server to []model.Server
// Convert from []*model.Entry to []model.Entry result := make([]model.Server, len(entries))
result := make([]model.Entry, len(entries))
for i, entry := range entries { for i, entry := range entries {
result[i] = *entry result[i] = *entry
} }

View file

@ -21,7 +21,7 @@ func NewRegistryServiceWithDB(db database.Database) RegistryService {
} }
// GetAll returns all registry entries // GetAll returns all registry entries
func (s *registryServiceImpl) GetAll() ([]model.Entry, error) { func (s *registryServiceImpl) GetAll() ([]model.Server, error) {
// Create a timeout context for the database operation // Create a timeout context for the database operation
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel() defer cancel()
@ -32,8 +32,8 @@ func (s *registryServiceImpl) GetAll() ([]model.Entry, error) {
return nil, err return nil, err
} }
// Convert from []*model.Entry to []model.Entry // Convert from []*model.Server to []model.Server
result := make([]model.Entry, len(entries)) result := make([]model.Server, len(entries))
for i, entry := range entries { for i, entry := range entries {
result[i] = *entry result[i] = *entry
} }
@ -42,7 +42,7 @@ func (s *registryServiceImpl) GetAll() ([]model.Entry, error) {
} }
// List returns registry entries with cursor-based pagination // List returns registry entries with cursor-based pagination
func (s *registryServiceImpl) List(cursor string, limit int) ([]model.Entry, string, error) { func (s *registryServiceImpl) List(cursor string, limit int) ([]model.Server, string, error) {
// Create a timeout context for the database operation // Create a timeout context for the database operation
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel() defer cancel()
@ -58,8 +58,8 @@ func (s *registryServiceImpl) List(cursor string, limit int) ([]model.Entry, str
return nil, "", err return nil, "", err
} }
// Convert from []*model.MCPRegistry to []model.MCPRegistry // Convert from []*model.Server to []model.Server
result := make([]model.Entry, len(entries)) result := make([]model.Server, len(entries))
for i, entry := range entries { for i, entry := range entries {
result[i] = *entry result[i] = *entry
} }

View file

@ -4,7 +4,7 @@ import "github.com/modelcontextprotocol/registry/internal/model"
// RegistryService defines the interface for registry operations // RegistryService defines the interface for registry operations
type RegistryService interface { type RegistryService interface {
List(cursor string, limit int) ([]model.Entry, string, error) List(cursor string, limit int) ([]model.Server, string, error)
GetByID(id string) (*model.ServerDetail, error) GetByID(id string) (*model.ServerDetail, error)
Publish(serverDetail *model.ServerDetail) error Publish(serverDetail *model.ServerDetail) error
} }

View file

@ -1,38 +1,58 @@
{ {
"name": "io.github.<username>/<repository>", "description": "<your description here>",
"description": "<description>", "name": "io.github.<owner>/<server-name>",
"version_detail": { "package_canonical": "<package_registry",
"version": "1.0.2" "packages": [
}, {
"registries": [ "registry_name": "npm",
{ "name": "@<owner>/<server-name>",
"name": "npm", "version": "0.2.23",
"package_name": "<npm_package_name>", "package_arguments": [
"license": "MIT", {
"command_arguments": { "description": "Specify services and permissions.",
"sub_commands": [ "is_required": true,
{ "format": "string",
"name": "start", "value": "-s",
"description": "Start the server", "default": "-s",
"named_arguments": null "type": "positional",
} "value_hint": "-s"
], }
"positional_arguments": null, ],
"environment_variables": [ "environment_variables": [
{ {
"name": "PORT", "description": "API Key to access the server",
"description": "Port to run the server on", "name": "API_KEY"
"required": false }
} ]
], },{
"named_arguments": null "registry_name": "docker>",
} "name": "@<owner>/<server-name>-cli",
"version": "0.123.223",
"runtime_hint": "docker",
"runtime_arguments": [
{
"description": "Specify services and permissions.",
"is_required": true,
"format": "string",
"value": "--mount",
"default": "--mount",
"type": "positional",
"value_hint": "--mount"
}
],
"environment_variables": [
{
"description": "API Key to access the server",
"name": "API_KEY"
}
]
}
],
"repository": {
"url": "https://github.com//<owner>/<server-name>",
"source": "github"
},
"version_detail": {
"version": "0.0.1-<publisher_version>"
} }
], }
"remotes": [
{
"transporttype": "http",
"url": "http://example.com/api"
}
]
}