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
type PaginatedResponse struct {
Data []model.Entry `json:"servers"`
Metadata Metadata `json:"metadata,omitempty"`
Data []model.Server `json:"servers"`
Metadata Metadata `json:"metadata,omitempty"`
}
// Metadata contains pagination metadata

View file

@ -18,7 +18,7 @@ var (
// Database defines the interface for database operations on MCPRegistry entries
type Database interface {
// 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(ctx context.Context, id string) (*model.ServerDetail, error)
// 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)
}
// Convert raw data to model format
servers = convertToServerDetails(rawData)
}
log.Printf("Found %d server entries in seed file", len(servers))
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
func importData(ctx context.Context, collection *mongo.Collection, servers []model.ServerDetail) {
log.Printf("Importing %d servers into collection %s", len(servers), collection.Name())
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
filter := bson.M{"id": server.ID}
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.IsLatest = true

View file

@ -10,19 +10,19 @@ import (
// MemoryDB is an in-memory implementation of the Database interface
type MemoryDB struct {
entries map[string]*model.Entry
entries map[string]*model.Server
mu sync.RWMutex
}
// 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{
entries: e,
}
}
// 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 {
return nil, "", ctx.Err()
}
@ -35,14 +35,14 @@ func (db *MemoryDB) List(ctx context.Context, filter map[string]interface{}, cur
defer db.mu.RUnlock()
// Convert all entries to a slice for pagination
var allEntries []*model.Entry
var allEntries []*model.Server
for _, entry := range db.entries {
entryCopy := *entry
allEntries = append(allEntries, &entryCopy)
}
// Simple filtering implementation
var filteredEntries []*model.Entry
var filteredEntries []*model.Server
for _, entry := range allEntries {
include := true
@ -98,11 +98,11 @@ func (db *MemoryDB) List(ctx context.Context, filter map[string]interface{}, cur
endIdx = len(filteredEntries)
}
var result []*model.Entry
var result []*model.Server
if startIdx < len(filteredEntries) {
result = filteredEntries[startIdx:endIdx]
} else {
result = []*model.Entry{}
result = []*model.Server{}
}
// 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 {
return &model.ServerDetail{
ID: entry.ID,
Name: entry.Name,
Description: entry.Description,
VersionDetail: entry.VersionDetail,
Repository: entry.Repository,
Server: model.Server{
ID: entry.ID,
Name: entry.Name,
Description: entry.Description,
VersionDetail: entry.VersionDetail,
Repository: entry.Repository,
},
}, nil
}
@ -162,7 +164,7 @@ func (db *MemoryDB) Publish(ctx context.Context, serverDetail *model.ServerDetai
return ErrInvalidInput
}
db.entries[serverDetail.ID] = &model.Entry{
db.entries[serverDetail.ID] = &model.Server{
ID: serverDetail.ID,
Name: serverDetail.Name,
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
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 {
// Set default limit if not provided
limit = 10
@ -84,14 +84,16 @@ func (db *MongoDB) List(ctx context.Context, filter map[string]interface{}, curs
// Convert Go map to MongoDB filter
mongoFilter := bson.M{
"versiondetail.islatest": true,
"version_detail.is_latest": true,
}
// Map common filter keys to MongoDB document paths
for k, v := range filter {
// Handle nested fields with dot notation
switch k {
case "publisher.trusted":
mongoFilter["publisher.trusted"] = v
case "version":
mongoFilter["version_detail.version"] = v
case "name":
mongoFilter["name"] = v
default:
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
var cursorDoc model.Entry
var cursorDoc model.Server
err := db.collection.FindOne(ctx, bson.M{"id": cursor}).Decode(&cursorDoc)
if err != nil {
if err == mongo.ErrNoDocuments {
@ -138,7 +140,7 @@ func (db *MongoDB) List(ctx context.Context, filter map[string]interface{}, curs
defer mongoCursor.Close(ctx)
// Decode results
var results []*model.Entry
var results []*model.Server
if err = mongoCursor.All(ctx, &results); err != nil {
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
filter := bson.M{
"name": serverDetail.Name,
"versiondetail.islatest": true,
"name": serverDetail.Name,
"version_detail.is_latest": true,
}
var existingEntry model.ServerDetail

View file

@ -24,88 +24,105 @@ type PublishRequest struct {
AuthStatusToken string `json:"-"` // Used internally for device flows
}
type Entry struct {
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"`
}
// Repository represents a source code repository as defined in the spec
type Repository struct {
ID string `json:"id,omitempty"`
URL string `json:"url,omitempty"`
Source string `json:"source,omitempty"`
SubFolder string `json:"subfolder,omitempty"`
Branch string `json:"branch,omitempty"`
Commit string `json:"commit,omitempty"`
URL string `json:"url" bson:"url"`
Source string `json:"source" bson:"source"`
ID string `json:"id" bson:"id"`
}
type VersionDetail struct {
Version string `json:"version,omitempty"`
ReleaseDate string `json:"release_date,omitempty"` //RFC 3339 date format
IsLatest bool `json:"is_latest,omitempty"`
// ServerList represents the response for listing servers as defined in the spec
type ServerList struct {
Servers []Server `json:"servers" bson:"servers"`
Next string `json:"next,omitempty" bson:"next,omitempty"`
TotalCount int `json:"total_count" bson:"total_count"`
}
type ServerDetail struct {
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
VersionDetail VersionDetail `json:"version_detail,omitempty"`
Repository Repository `json:"repository,omitempty"`
PackageCanonical string `json:"package_canonical,omitempty"`
Registries []Registries `json:"registries,omitempty"`
Remotes []Remotes `json:"remotes,omitempty"`
// create an enum for Format
type Format string
const (
FormatString Format = "string"
FormatNumber Format = "number"
FormatBoolean Format = "boolean"
FormatFilePath Format = "file_path"
)
// 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 {
Name string `json:"name,omitempty"`
PackageName string `json:"package_name,omitempty"`
License string `json:"license,omitempty"`
CommandArguments Command `json:"command_arguments,omitempty"`
type InputWithVariables struct {
Input `json:",inline" bson:",inline"`
Variables map[string]Input `json:"variables,omitempty" bson:"variables,omitempty"`
}
type Remotes struct {
TransportType string `json:"transport_type,omitempty"`
Url string `json:"url,omitempty"`
type KeyValueInput struct {
InputWithVariables `json:",inline" bson:",inline"`
Name string `json:"name" bson:"name"`
}
type ArgumentType string
type Command struct {
SubCommands []SubCommand `json:"sub_commands,omitempty"`
PositionalArguments []PositionalArgument `json:"positional_arguments,omitempty"`
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"`
}
const (
ArgumentTypePositional ArgumentType = "positional"
ArgumentTypeNamed ArgumentType = "named"
)
// RuntimeArgument defines a type that can be either a PositionalArgument or a NamedArgument
type Argument struct {
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
DefaultValue string `json:"default_value,omitempty"`
IsRequired bool `json:"is_required,omitempty"`
IsEditable bool `json:"is_editable,omitempty"`
IsRepeatable bool `json:"is_repeatable,omitempty"`
Choices []string `json:"choices,omitempty"`
InputWithVariables `json:",inline" bson:",inline"`
Type ArgumentType `json:"type" bson:"type"`
Name string `json:"name,omitempty" bson:"name,omitempty"`
IsRepeated bool `json:"is_repeated,omitempty" bson:"is_repeated,omitempty"`
ValueHint string `json:"value_hint,omitempty" bson:"value_hint,omitempty"`
}
type PositionalArgument struct {
Position int `json:"position,omitempty"`
Argument Argument `json:"argument,omitempty"`
type Package struct {
RegistryName string `json:"registry_name" bson:"registry_name"`
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 {
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
NamedArguments []NamedArguments `json:"named_arguments,omitempty"`
// Remote represents a remote connection endpoint
type Remote struct {
TransportType string `json:"transport_type" bson:"transport_type"`
URL string `json:"url" bson:"url"`
Headers []Input `json:"headers,omitempty" bson:"headers,omitempty"`
}
type NamedArguments struct {
ShortFlag string `json:"short_flag,omitempty"`
LongFlag string `json:"long_flag,omitempty"`
RequiresValue bool `json:"requires_value,omitempty"`
Argument Argument `json:"argument,omitempty"`
// VersionDetail represents the version details of a server
type VersionDetail struct {
Version string `json:"version" bson:"version"`
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
func NewFakeRegistryService() RegistryService {
// Sample registry entries with updated model structure
registries := []*model.Entry{
registries := []*model.Server{
{
ID: uuid.New().String(),
Name: "bluegreen/mcp-server",
Description: "A dummy MCP registry for testing",
Repository: model.Repository{
URL: "https://github.com/example/mcp-1",
SubFolder: "server",
Branch: "main",
Commit: "abc123def456",
URL: "https://github.com/example/mcp-1",
Source: "github",
ID: "example/mcp-1",
},
VersionDetail: model.VersionDetail{
Version: "1.0.0",
@ -39,10 +38,9 @@ func NewFakeRegistryService() RegistryService {
Name: "orangepurple/mcp-server",
Description: "Another dummy MCP registry for testing",
Repository: model.Repository{
URL: "https://github.com/example/mcp-2",
SubFolder: "mcp",
Branch: "develop",
Commit: "def456ghi789",
URL: "https://github.com/example/mcp-2",
Source: "github",
ID: "example/mcp-2",
},
VersionDetail: model.VersionDetail{
Version: "0.9.0",
@ -55,10 +53,9 @@ func NewFakeRegistryService() RegistryService {
Name: "greenyellow/mcp-server",
Description: "Yet another dummy MCP registry for testing",
Repository: model.Repository{
URL: "https://github.com/example/mcp-3",
SubFolder: "",
Branch: "feature/mcp-server",
Commit: "789jkl012mno",
URL: "https://github.com/example/mcp-3",
Source: "github",
ID: "example/mcp-3",
},
VersionDetail: model.VersionDetail{
Version: "0.9.5",
@ -69,7 +66,7 @@ func NewFakeRegistryService() RegistryService {
}
// Create a new in-memory database
registryMap := make(map[string]*model.Entry)
registryMap := make(map[string]*model.Server)
for _, entry := range registries {
registryMap[entry.ID] = entry
}
@ -80,7 +77,7 @@ func NewFakeRegistryService() RegistryService {
}
// 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
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
@ -90,9 +87,8 @@ func (s *fakeRegistryService) List(cursor string, limit int) ([]model.Entry, str
if err != nil {
return nil, "", err
}
// Convert from []*model.Entry to []model.Entry
result := make([]model.Entry, len(entries))
// Convert from []*model.Server to []model.Server
result := make([]model.Server, len(entries))
for i, entry := range entries {
result[i] = *entry
}

View file

@ -21,7 +21,7 @@ func NewRegistryServiceWithDB(db database.Database) RegistryService {
}
// 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
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
@ -32,8 +32,8 @@ func (s *registryServiceImpl) GetAll() ([]model.Entry, error) {
return nil, err
}
// Convert from []*model.Entry to []model.Entry
result := make([]model.Entry, len(entries))
// Convert from []*model.Server to []model.Server
result := make([]model.Server, len(entries))
for i, entry := range entries {
result[i] = *entry
}
@ -42,7 +42,7 @@ func (s *registryServiceImpl) GetAll() ([]model.Entry, error) {
}
// 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
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
@ -58,8 +58,8 @@ func (s *registryServiceImpl) List(cursor string, limit int) ([]model.Entry, str
return nil, "", err
}
// Convert from []*model.MCPRegistry to []model.MCPRegistry
result := make([]model.Entry, len(entries))
// Convert from []*model.Server to []model.Server
result := make([]model.Server, len(entries))
for i, entry := range entries {
result[i] = *entry
}

View file

@ -4,7 +4,7 @@ import "github.com/modelcontextprotocol/registry/internal/model"
// RegistryService defines the interface for registry operations
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)
Publish(serverDetail *model.ServerDetail) error
}

View file

@ -1,38 +1,58 @@
{
"name": "io.github.<username>/<repository>",
"description": "<description>",
"version_detail": {
"version": "1.0.2"
},
"registries": [
{
"name": "npm",
"package_name": "<npm_package_name>",
"license": "MIT",
"command_arguments": {
"sub_commands": [
{
"name": "start",
"description": "Start the server",
"named_arguments": null
}
],
"positional_arguments": null,
"environment_variables": [
{
"name": "PORT",
"description": "Port to run the server on",
"required": false
}
],
"named_arguments": null
}
"description": "<your description here>",
"name": "io.github.<owner>/<server-name>",
"package_canonical": "<package_registry",
"packages": [
{
"registry_name": "npm",
"name": "@<owner>/<server-name>",
"version": "0.2.23",
"package_arguments": [
{
"description": "Specify services and permissions.",
"is_required": true,
"format": "string",
"value": "-s",
"default": "-s",
"type": "positional",
"value_hint": "-s"
}
],
"environment_variables": [
{
"description": "API Key to access the server",
"name": "API_KEY"
}
]
},{
"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"
}
]
}
}