Skip to content

Commit 5c238ec

Browse files
sridharavinashtoby
authored andcommitted
feat: refactor to account for updated openAPI schema
* Update the model to handle new changes to schema based on #33 * Made the import script a little simpler. * Add a new updated seed file * update template mcp.json file for publisher
1 parent 5ef4d76 commit 5c238ec

File tree

11 files changed

+17637
-359
lines changed

11 files changed

+17637
-359
lines changed

data/seed_2025_05_16.json

Lines changed: 17446 additions & 0 deletions
Large diffs are not rendered by default.

internal/api/handlers/v0/servers.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ import (
1313

1414
// Response is a paginated API response
1515
type PaginatedResponse struct {
16-
Data []model.Entry `json:"servers"`
17-
Metadata Metadata `json:"metadata,omitempty"`
16+
Data []model.Server `json:"servers"`
17+
Metadata Metadata `json:"metadata,omitempty"`
1818
}
1919

2020
// Metadata contains pagination metadata

internal/database/database.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ var (
1818
// Database defines the interface for database operations on MCPRegistry entries
1919
type Database interface {
2020
// List retrieves all MCPRegistry entries with optional filtering
21-
List(ctx context.Context, filter map[string]interface{}, cursor string, limit int) ([]*model.Entry, string, error)
21+
List(ctx context.Context, filter map[string]interface{}, cursor string, limit int) ([]*model.Server, string, error)
2222
// GetByID retrieves a single ServerDetail by it's ID
2323
GetByID(ctx context.Context, id string) (*model.ServerDetail, error)
2424
// Publish adds a new ServerDetail to the database

internal/database/import.go

Lines changed: 5 additions & 210 deletions
Original file line numberDiff line numberDiff line change
@@ -60,231 +60,26 @@ func readSeedFile(path string) ([]model.ServerDetail, error) {
6060
return nil, fmt.Errorf("failed to parse JSON: %v (original error: %v)", jsonErr, err)
6161
}
6262

63-
// Convert raw data to model format
64-
servers = convertToServerDetails(rawData)
6563
}
6664

6765
log.Printf("Found %d server entries in seed file", len(servers))
6866
return servers, nil
6967
}
7068

71-
// convertToServerDetails converts raw JSON data to ServerDetail structs
72-
func convertToServerDetails(rawData []map[string]interface{}) []model.ServerDetail {
73-
servers := make([]model.ServerDetail, 0, len(rawData))
74-
75-
for _, data := range rawData {
76-
server := model.ServerDetail{}
77-
78-
// Extract basic fields
79-
if id, ok := data["id"].(string); ok {
80-
server.ID = id
81-
}
82-
if name, ok := data["name"].(string); ok {
83-
server.Name = name
84-
}
85-
if desc, ok := data["description"].(string); ok {
86-
server.Description = desc
87-
}
88-
if version, ok := data["version"].(string); ok {
89-
if version == "" || version == "latest" {
90-
version = "0.0.1"
91-
}
92-
server.VersionDetail = model.VersionDetail{
93-
Version: version,
94-
ReleaseDate: time.Now().Format(time.RFC3339),
95-
IsLatest: true,
96-
}
97-
}
98-
99-
// Extract repository
100-
if repo, ok := data["repository"].(map[string]interface{}); ok {
101-
server.Repository = model.Repository{
102-
URL: getStringValue(repo, "url"),
103-
SubFolder: getStringValue(repo, "subfolder"),
104-
Branch: getStringValue(repo, "branch"),
105-
Commit: getStringValue(repo, "commit"),
106-
}
107-
}
108-
109-
// Extract registries
110-
if registries, ok := data["registries"].([]interface{}); ok {
111-
for _, reg := range registries {
112-
if regMap, ok := reg.(map[string]interface{}); ok {
113-
registry := model.Registries{
114-
Name: getStringValue(regMap, "name"),
115-
PackageName: getStringValue(regMap, "packagename"),
116-
License: getStringValue(regMap, "license"),
117-
}
118-
119-
// Handle command arguments if present
120-
if cmd, ok := regMap["command_arguments"].(map[string]interface{}); ok {
121-
commandArgs := model.Command{}
122-
123-
// Extract sub commands
124-
if subCmds, ok := cmd["sub_commands"].([]interface{}); ok {
125-
for _, sc := range subCmds {
126-
if scMap, ok := sc.(map[string]interface{}); ok {
127-
subCmd := model.SubCommand{
128-
Name: getStringValue(scMap, "name"),
129-
Description: getStringValue(scMap, "description"),
130-
}
131-
132-
// Extract named arguments in sub commands
133-
if namedArgs, ok := scMap["named_arguments"].([]interface{}); ok {
134-
for _, na := range namedArgs {
135-
if naMap, ok := na.(map[string]interface{}); ok {
136-
namedArg := extractNamedArgument(naMap)
137-
subCmd.NamedArguments = append(subCmd.NamedArguments, namedArg)
138-
}
139-
}
140-
}
141-
142-
commandArgs.SubCommands = append(commandArgs.SubCommands, subCmd)
143-
}
144-
}
145-
}
146-
147-
// Extract positional arguments
148-
if posArgs, ok := cmd["positional_arguments"].([]interface{}); ok {
149-
for _, pa := range posArgs {
150-
if paMap, ok := pa.(map[string]interface{}); ok {
151-
posArg := model.PositionalArgument{}
152-
153-
if pos, ok := paMap["position"].(float64); ok {
154-
posArg.Position = int(pos)
155-
}
156-
157-
if arg, ok := paMap["argument"].(map[string]interface{}); ok {
158-
posArg.Argument = extractArgument(arg)
159-
}
160-
161-
commandArgs.PositionalArguments = append(commandArgs.PositionalArguments, posArg)
162-
}
163-
}
164-
}
165-
166-
// Extract environment variables
167-
if envVars, ok := cmd["environment_variables"].([]interface{}); ok {
168-
for _, ev := range envVars {
169-
if evMap, ok := ev.(map[string]interface{}); ok {
170-
envVar := model.EnvironmentVariable{
171-
Name: getStringValue(evMap, "name"),
172-
Description: getStringValue(evMap, "description"),
173-
}
174-
175-
if required, ok := evMap["required"].(bool); ok {
176-
envVar.Required = required
177-
}
178-
179-
commandArgs.EnvironmentVariables = append(commandArgs.EnvironmentVariables, envVar)
180-
}
181-
}
182-
}
183-
184-
// Extract named arguments
185-
if namedArgs, ok := cmd["named_arguments"].([]interface{}); ok {
186-
for _, na := range namedArgs {
187-
if naMap, ok := na.(map[string]interface{}); ok {
188-
namedArg := extractNamedArgument(naMap)
189-
commandArgs.NamedArguments = append(commandArgs.NamedArguments, namedArg)
190-
}
191-
}
192-
}
193-
194-
registry.CommandArguments = commandArgs
195-
}
196-
197-
server.Registries = append(server.Registries, registry)
198-
}
199-
}
200-
}
201-
202-
// Extract remotes
203-
if remotes, ok := data["remotes"].([]interface{}); ok {
204-
for _, rem := range remotes {
205-
if remMap, ok := rem.(map[string]interface{}); ok {
206-
remote := model.Remotes{
207-
TransportType: getStringValue(remMap, "transport_type"),
208-
Url: getStringValue(remMap, "url"),
209-
}
210-
server.Remotes = append(server.Remotes, remote)
211-
}
212-
}
213-
}
214-
215-
servers = append(servers, server)
216-
}
217-
218-
return servers
219-
}
220-
221-
// extractArgument extracts an Argument struct from a map
222-
func extractArgument(data map[string]interface{}) model.Argument {
223-
arg := model.Argument{
224-
Name: getStringValue(data, "name"),
225-
Description: getStringValue(data, "description"),
226-
DefaultValue: getStringValue(data, "default_value"),
227-
}
228-
229-
// Extract boolean fields
230-
if isRequired, ok := data["is_required"].(bool); ok {
231-
arg.IsRequired = isRequired
232-
}
233-
if isEditable, ok := data["is_editable"].(bool); ok {
234-
arg.IsEditable = isEditable
235-
}
236-
if isRepeatable, ok := data["is_repeatable"].(bool); ok {
237-
arg.IsRepeatable = isRepeatable
238-
}
239-
240-
// Extract string array for choices
241-
if choices, ok := data["choices"].([]interface{}); ok {
242-
for _, choice := range choices {
243-
if strChoice, ok := choice.(string); ok {
244-
arg.Choices = append(arg.Choices, strChoice)
245-
}
246-
}
247-
}
248-
249-
return arg
250-
}
251-
252-
// extractNamedArgument extracts a NamedArguments struct from a map
253-
func extractNamedArgument(data map[string]interface{}) model.NamedArguments {
254-
namedArg := model.NamedArguments{
255-
ShortFlag: getStringValue(data, "short_flag"),
256-
LongFlag: getStringValue(data, "long_flag"),
257-
}
258-
259-
if requiresValue, ok := data["requires_value"].(bool); ok {
260-
namedArg.RequiresValue = requiresValue
261-
}
262-
263-
if arg, ok := data["argument"].(map[string]interface{}); ok {
264-
namedArg.Argument = extractArgument(arg)
265-
}
266-
267-
return namedArg
268-
}
269-
270-
// getStringValue safely extracts a string value from a map
271-
func getStringValue(data map[string]interface{}, key string) string {
272-
if val, ok := data[key].(string); ok {
273-
return val
274-
}
275-
return ""
276-
}
277-
27869
// importData imports the seed data into MongoDB
27970
func importData(ctx context.Context, collection *mongo.Collection, servers []model.ServerDetail) {
28071
log.Printf("Importing %d servers into collection %s", len(servers), collection.Name())
28172

28273
for i, server := range servers {
74+
if server.ID == "" || server.Name == "" {
75+
log.Printf("Skipping server %d: ID or Name is empty", i+1)
76+
continue
77+
}
28378
// Create filter based on server ID
28479
filter := bson.M{"id": server.ID}
28580

28681
if server.VersionDetail.Version == "" {
287-
server.VersionDetail.Version = "0.0.1"
82+
server.VersionDetail.Version = "0.0.1-seed"
28883
server.VersionDetail.ReleaseDate = time.Now().Format(time.RFC3339)
28984
server.VersionDetail.IsLatest = true
29085

internal/database/memory.go

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,19 @@ import (
1010

1111
// MemoryDB is an in-memory implementation of the Database interface
1212
type MemoryDB struct {
13-
entries map[string]*model.Entry
13+
entries map[string]*model.Server
1414
mu sync.RWMutex
1515
}
1616

1717
// NewMemoryDB creates a new instance of the in-memory database
18-
func NewMemoryDB(e map[string]*model.Entry) *MemoryDB {
18+
func NewMemoryDB(e map[string]*model.Server) *MemoryDB {
1919
return &MemoryDB{
2020
entries: e,
2121
}
2222
}
2323

2424
// List retrieves all MCPRegistry entries with optional filtering and pagination
25-
func (db *MemoryDB) List(ctx context.Context, filter map[string]interface{}, cursor string, limit int) ([]*model.Entry, string, error) {
25+
func (db *MemoryDB) List(ctx context.Context, filter map[string]interface{}, cursor string, limit int) ([]*model.Server, string, error) {
2626
if ctx.Err() != nil {
2727
return nil, "", ctx.Err()
2828
}
@@ -35,14 +35,14 @@ func (db *MemoryDB) List(ctx context.Context, filter map[string]interface{}, cur
3535
defer db.mu.RUnlock()
3636

3737
// Convert all entries to a slice for pagination
38-
var allEntries []*model.Entry
38+
var allEntries []*model.Server
3939
for _, entry := range db.entries {
4040
entryCopy := *entry
4141
allEntries = append(allEntries, &entryCopy)
4242
}
4343

4444
// Simple filtering implementation
45-
var filteredEntries []*model.Entry
45+
var filteredEntries []*model.Server
4646
for _, entry := range allEntries {
4747
include := true
4848

@@ -98,11 +98,11 @@ func (db *MemoryDB) List(ctx context.Context, filter map[string]interface{}, cur
9898
endIdx = len(filteredEntries)
9999
}
100100

101-
var result []*model.Entry
101+
var result []*model.Server
102102
if startIdx < len(filteredEntries) {
103103
result = filteredEntries[startIdx:endIdx]
104104
} else {
105-
result = []*model.Entry{}
105+
result = []*model.Server{}
106106
}
107107

108108
// Determine next cursor
@@ -125,11 +125,13 @@ func (db *MemoryDB) GetByID(ctx context.Context, id string) (*model.ServerDetail
125125

126126
if entry, exists := db.entries[id]; exists {
127127
return &model.ServerDetail{
128-
ID: entry.ID,
129-
Name: entry.Name,
130-
Description: entry.Description,
131-
VersionDetail: entry.VersionDetail,
132-
Repository: entry.Repository,
128+
Server: model.Server{
129+
ID: entry.ID,
130+
Name: entry.Name,
131+
Description: entry.Description,
132+
VersionDetail: entry.VersionDetail,
133+
Repository: entry.Repository,
134+
},
133135
}, nil
134136
}
135137

@@ -162,7 +164,7 @@ func (db *MemoryDB) Publish(ctx context.Context, serverDetail *model.ServerDetai
162164
return ErrInvalidInput
163165
}
164166

165-
db.entries[serverDetail.ID] = &model.Entry{
167+
db.entries[serverDetail.ID] = &model.Server{
166168
ID: serverDetail.ID,
167169
Name: serverDetail.Name,
168170
Description: serverDetail.Description,

internal/database/mongo.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ func NewMongoDB(ctx context.Context, connectionURI, databaseName, collectionName
7272
}
7373

7474
// List retrieves MCPRegistry entries with optional filtering and pagination
75-
func (db *MongoDB) List(ctx context.Context, filter map[string]interface{}, cursor string, limit int) ([]*model.Entry, string, error) {
75+
func (db *MongoDB) List(ctx context.Context, filter map[string]interface{}, cursor string, limit int) ([]*model.Server, string, error) {
7676
if limit <= 0 {
7777
// Set default limit if not provided
7878
limit = 10
@@ -84,14 +84,16 @@ func (db *MongoDB) List(ctx context.Context, filter map[string]interface{}, curs
8484

8585
// Convert Go map to MongoDB filter
8686
mongoFilter := bson.M{
87-
"versiondetail.islatest": true,
87+
"version_detail.is_latest": true,
8888
}
8989
// Map common filter keys to MongoDB document paths
9090
for k, v := range filter {
9191
// Handle nested fields with dot notation
9292
switch k {
93-
case "publisher.trusted":
94-
mongoFilter["publisher.trusted"] = v
93+
case "version":
94+
mongoFilter["version_detail.version"] = v
95+
case "name":
96+
mongoFilter["name"] = v
9597
default:
9698
mongoFilter[k] = v
9799
}
@@ -108,7 +110,7 @@ func (db *MongoDB) List(ctx context.Context, filter map[string]interface{}, curs
108110
}
109111

110112
// Fetch the document at the cursor to get its sort values
111-
var cursorDoc model.Entry
113+
var cursorDoc model.Server
112114
err := db.collection.FindOne(ctx, bson.M{"id": cursor}).Decode(&cursorDoc)
113115
if err != nil {
114116
if err == mongo.ErrNoDocuments {
@@ -138,7 +140,7 @@ func (db *MongoDB) List(ctx context.Context, filter map[string]interface{}, curs
138140
defer mongoCursor.Close(ctx)
139141

140142
// Decode results
141-
var results []*model.Entry
143+
var results []*model.Server
142144
if err = mongoCursor.All(ctx, &results); err != nil {
143145
return nil, "", err
144146
}
@@ -183,8 +185,8 @@ func (db *MongoDB) Publish(ctx context.Context, serverDetail *model.ServerDetail
183185
}
184186
// find a server detail with the same name and check that the current version is greater than the existing one
185187
filter := bson.M{
186-
"name": serverDetail.Name,
187-
"versiondetail.islatest": true,
188+
"name": serverDetail.Name,
189+
"version_detail.is_latest": true,
188190
}
189191

190192
var existingEntry model.ServerDetail

0 commit comments

Comments
 (0)