Skip to content

Commit f870a4c

Browse files
domdomeggclauderdimitrov
authored
Improve golang type documentation and examples (#631)
Enhanced Go struct tags throughout `pkg/api/v0/types.go` and `pkg/model/types.go` to better align with the OpenAPI specification. ## Changes - Added comprehensive `doc` tags with detailed field descriptions matching OpenAPI - Added `example` tags showing valid values for fields (e.g., `"io.github.user/weather"`, `"npx"`) - Added `format` tags (`uri`, `date-time`) for better type specification - Added `pattern` and `enum` tags for validation constraints - Added `placeholder` field to `Input` struct matching OpenAPI spec - Removed redundant comments in favor of inline struct tags These changes improve code clarity and make the types more self-documenting, while maintaining compatibility with the OpenAPI schema definitions in `docs/reference/api/openapi.yaml`. --------- Signed-off-by: Radoslav Dimitrov <[email protected]> Co-authored-by: Claude <[email protected]> Co-authored-by: Radoslav Dimitrov <[email protected]>
1 parent ba609e1 commit f870a4c

File tree

3 files changed

+78
-101
lines changed

3 files changed

+78
-101
lines changed

internal/api/handlers/v0/publish_test.go

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ func TestPublishEndpoint(t *testing.T) {
131131
name: "invalid token",
132132
requestBody: apiv0.ServerJSON{
133133
Schema: model.CurrentSchemaURL,
134-
Name: "test-server",
134+
Name: "example/test-server",
135135
Description: "A test server",
136136
Version: "1.0.0",
137137
},
@@ -252,8 +252,8 @@ func TestPublishEndpoint(t *testing.T) {
252252
},
253253
},
254254
setupRegistryService: func(_ service.RegistryService) {},
255-
expectedStatus: http.StatusBadRequest,
256-
expectedError: "server name cannot contain multiple slashes",
255+
expectedStatus: http.StatusUnprocessableEntity,
256+
expectedError: "expected string to match pattern",
257257
},
258258
{
259259
name: "invalid server name - multiple slashes (three slashes)",
@@ -270,8 +270,8 @@ func TestPublishEndpoint(t *testing.T) {
270270
},
271271
},
272272
setupRegistryService: func(_ service.RegistryService) {},
273-
expectedStatus: http.StatusBadRequest,
274-
expectedError: "server name cannot contain multiple slashes",
273+
expectedStatus: http.StatusUnprocessableEntity,
274+
expectedError: "expected string to match pattern",
275275
},
276276
{
277277
name: "invalid server name - consecutive slashes",
@@ -288,8 +288,8 @@ func TestPublishEndpoint(t *testing.T) {
288288
},
289289
},
290290
setupRegistryService: func(_ service.RegistryService) {},
291-
expectedStatus: http.StatusBadRequest,
292-
expectedError: "server name cannot contain multiple slashes",
291+
expectedStatus: http.StatusUnprocessableEntity,
292+
expectedError: "expected string to match pattern",
293293
},
294294
{
295295
name: "invalid server name - URL-like path",
@@ -306,8 +306,8 @@ func TestPublishEndpoint(t *testing.T) {
306306
},
307307
},
308308
setupRegistryService: func(_ service.RegistryService) {},
309-
expectedStatus: http.StatusBadRequest,
310-
expectedError: "server name cannot contain multiple slashes",
309+
expectedStatus: http.StatusUnprocessableEntity,
310+
expectedError: "expected string to match pattern",
311311
},
312312
{
313313
name: "invalid server name - many slashes",
@@ -324,8 +324,8 @@ func TestPublishEndpoint(t *testing.T) {
324324
},
325325
},
326326
setupRegistryService: func(_ service.RegistryService) {},
327-
expectedStatus: http.StatusBadRequest,
328-
expectedError: "server name cannot contain multiple slashes",
327+
expectedStatus: http.StatusUnprocessableEntity,
328+
expectedError: "expected string to match pattern",
329329
},
330330
{
331331
name: "invalid server name - with packages and remotes",
@@ -363,8 +363,8 @@ func TestPublishEndpoint(t *testing.T) {
363363
},
364364
},
365365
setupRegistryService: func(_ service.RegistryService) {},
366-
expectedStatus: http.StatusBadRequest,
367-
expectedError: "server name cannot contain multiple slashes",
366+
expectedStatus: http.StatusUnprocessableEntity,
367+
expectedError: "expected string to match pattern",
368368
},
369369
}
370370

@@ -447,25 +447,25 @@ func TestPublishEndpoint_MultipleSlashesEdgeCases(t *testing.T) {
447447
{
448448
name: "invalid - trailing slash after valid name",
449449
serverName: "com.example/server/",
450-
expectedStatus: http.StatusBadRequest,
450+
expectedStatus: http.StatusUnprocessableEntity,
451451
description: "Trailing slash creates multiple slashes",
452452
},
453453
{
454454
name: "invalid - leading and middle slash",
455455
serverName: "/com.example/server",
456-
expectedStatus: http.StatusBadRequest,
456+
expectedStatus: http.StatusUnprocessableEntity,
457457
description: "Leading slash with middle slash",
458458
},
459459
{
460460
name: "invalid - file system style path",
461461
serverName: "usr/local/bin/server",
462-
expectedStatus: http.StatusBadRequest,
462+
expectedStatus: http.StatusUnprocessableEntity,
463463
description: "File system style paths should be rejected",
464464
},
465465
{
466466
name: "invalid - version-like suffix",
467467
serverName: "com.example/server/v1.0.0",
468-
expectedStatus: http.StatusBadRequest,
468+
expectedStatus: http.StatusUnprocessableEntity,
469469
description: "Version suffixes with slash should be rejected",
470470
},
471471
}
@@ -517,9 +517,9 @@ func TestPublishEndpoint_MultipleSlashesEdgeCases(t *testing.T) {
517517
assert.Equal(t, tc.expectedStatus, rr.Code,
518518
"%s: expected status %d, got %d", tc.description, tc.expectedStatus, rr.Code)
519519

520-
if tc.expectedStatus == http.StatusBadRequest {
521-
assert.Contains(t, rr.Body.String(), "server name cannot contain multiple slashes",
522-
"%s: should contain specific error message", tc.description)
520+
if tc.expectedStatus == http.StatusUnprocessableEntity {
521+
assert.Contains(t, rr.Body.String(), "expected string to match pattern",
522+
"%s: should contain pattern validation error", tc.description)
523523
}
524524
})
525525
}

pkg/api/v0/types.go

Lines changed: 23 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,53 +6,46 @@ import (
66
"github.com/modelcontextprotocol/registry/pkg/model"
77
)
88

9-
// RegistryExtensions represents registry-generated metadata
109
type RegistryExtensions struct {
11-
Status model.Status `json:"status"`
12-
PublishedAt time.Time `json:"publishedAt"`
13-
UpdatedAt time.Time `json:"updatedAt,omitempty"`
14-
IsLatest bool `json:"isLatest"`
10+
Status model.Status `json:"status" enum:"active,deprecated,deleted" doc:"Server lifecycle status"`
11+
PublishedAt time.Time `json:"publishedAt" format:"date-time" doc:"Timestamp when the server was first published to the registry"`
12+
UpdatedAt time.Time `json:"updatedAt,omitempty" format:"date-time" doc:"Timestamp when the server entry was last updated"`
13+
IsLatest bool `json:"isLatest" doc:"Whether this is the latest version of the server"`
1514
}
1615

17-
// ResponseMeta represents the top-level metadata in API responses
1816
type ResponseMeta struct {
19-
Official *RegistryExtensions `json:"io.modelcontextprotocol.registry/official,omitempty"`
17+
Official *RegistryExtensions `json:"io.modelcontextprotocol.registry/official,omitempty" doc:"Official MCP registry metadata"`
2018
}
2119

22-
// ServerResponse represents the new API response format with separated metadata
2320
type ServerResponse struct {
24-
Server ServerJSON `json:"server"`
25-
Meta ResponseMeta `json:"_meta"`
21+
Server ServerJSON `json:"server" doc:"Server configuration and metadata"`
22+
Meta ResponseMeta `json:"_meta" doc:"Registry-managed metadata"`
2623
}
2724

28-
// ServerListResponse represents the paginated server list response
2925
type ServerListResponse struct {
30-
Servers []ServerResponse `json:"servers"`
31-
Metadata Metadata `json:"metadata"`
26+
Servers []ServerResponse `json:"servers" doc:"List of server entries"`
27+
Metadata Metadata `json:"metadata" doc:"Pagination metadata"`
3228
}
3329

34-
// ServerMeta represents the structured metadata with known extension fields
3530
type ServerMeta struct {
36-
PublisherProvided map[string]interface{} `json:"io.modelcontextprotocol.registry/publisher-provided,omitempty"`
31+
PublisherProvided map[string]interface{} `json:"io.modelcontextprotocol.registry/publisher-provided,omitempty" doc:"Publisher-provided metadata for downstream registries"`
3732
}
3833

39-
// ServerJSON represents complete server information as defined in the MCP spec, with extension support
4034
type ServerJSON struct {
41-
Schema string `json:"$schema" required:"true" minLength:"1"`
42-
Name string `json:"name" minLength:"1" maxLength:"200"`
43-
Description string `json:"description" minLength:"1" maxLength:"100"`
44-
Title string `json:"title,omitempty" minLength:"1" maxLength:"100"`
45-
Repository model.Repository `json:"repository,omitempty"`
46-
Version string `json:"version"`
47-
WebsiteURL string `json:"websiteUrl,omitempty"`
48-
Icons []model.Icon `json:"icons,omitempty"`
49-
Packages []model.Package `json:"packages,omitempty"`
50-
Remotes []model.Transport `json:"remotes,omitempty"`
51-
Meta *ServerMeta `json:"_meta,omitempty"`
35+
Schema string `json:"$schema" required:"true" minLength:"1" format:"uri" doc:"JSON Schema URI for this server.json format" example:"https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json"`
36+
Name string `json:"name" minLength:"3" maxLength:"200" pattern:"^[a-zA-Z0-9.-]+/[a-zA-Z0-9._-]+$" doc:"Server name in reverse-DNS format. Must contain exactly one forward slash separating namespace from server name." example:"io.github.user/weather"`
37+
Description string `json:"description" minLength:"1" maxLength:"100" doc:"Clear human-readable explanation of server functionality." example:"MCP server providing weather data and forecasts via OpenWeatherMap API"`
38+
Title string `json:"title,omitempty" minLength:"1" maxLength:"100" doc:"Optional human-readable title or display name for the MCP server." example:"Weather API"`
39+
Repository model.Repository `json:"repository,omitempty" doc:"Optional repository metadata for the MCP server source code."`
40+
Version string `json:"version" doc:"Version string for this server. SHOULD follow semantic versioning." example:"1.0.2"`
41+
WebsiteURL string `json:"websiteUrl,omitempty" format:"uri" doc:"Optional URL to the server's homepage, documentation, or project website." example:"https://modelcontextprotocol.io/examples"`
42+
Icons []model.Icon `json:"icons,omitempty" doc:"Optional set of sized icons that the client can display in a user interface."`
43+
Packages []model.Package `json:"packages,omitempty" doc:"Array of package configurations"`
44+
Remotes []model.Transport `json:"remotes,omitempty" doc:"Array of remote configurations"`
45+
Meta *ServerMeta `json:"_meta,omitempty" doc:"Extension metadata using reverse DNS namespacing for vendor-specific data"`
5246
}
5347

54-
// Metadata represents pagination metadata
5548
type Metadata struct {
56-
NextCursor string `json:"nextCursor,omitempty"`
57-
Count int `json:"count"`
49+
NextCursor string `json:"nextCursor,omitempty" doc:"Pagination cursor for retrieving the next page of results. Use this exact value in the cursor query parameter of your next request."`
50+
Count int `json:"count" doc:"Number of items in current page"`
5851
}

0 commit comments

Comments
 (0)