Skip to content

Commit 774a0df

Browse files
tadasantclaudechlowell
authored
Add validation tools for server.json schemas and examples (modelcontextprotocol#176)
This is a first PR for modelcontextprotocol#159 - adding some validation scripts. I want to get to a place where we are confident that making schema changes is in sync with the rest of our code. Step one is making sure the schemas are in sync with our examples. Step two will be making sure the schemas are in sync with our actual Registry implementation code. This is just for the JSON right now; if we're feeling good about this approach I'll do an analogous change for the OpenAPI schemas as well. I'll also follow with adding usage of these scripts to our CI pipeline so we have automated checks on them with every change. ## Summary - Added CLI validation tools for JSON schemas and examples in `docs/server-json/` - Fixed some minor validation errors found in `examples.md` - Ensures schema consistency and example validity in CI/CD pipelines ## Changes ### New Validation Tools 1. **`tools/validate-schemas`** - Validates that `schema.json` and `registry-schema.json` are valid JSON Schema documents 2. **`tools/validate-examples`** - Validates all JSON examples in `examples.md` against both schemas ### Key Features - ✅ Validates JSON schema syntax and structure - ✅ Validates all examples against both base and registry schemas - ✅ Tracks validation counts to prevent silent failures - ✅ Proper exit codes for CI integration - ✅ Clear error messages for debugging ### Fixes to examples.md - Added missing `repository.id` field in NuGet example - Changed `repository.source` from "gitlab" to "github" in Docker example ### Usage ```bash # From repository root ./tools/validate-schemas.sh ./tools/validate-examples.sh ``` ## Testing I ran these scripts locally with minor (breaking) changes, which they each correctly reported. As submitted, they are both passing. 🤖 Co-authored with [Claude Code](https://claude.ai/code) --------- Co-authored-by: Claude <[email protected]> Co-authored-by: Charles Lowell <[email protected]>
1 parent 4e221e4 commit 774a0df

File tree

17 files changed

+321
-11
lines changed

17 files changed

+321
-11
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@ build/*
55
**/bin
66
cmd/registry/registry
77
publisher
8+
validate-examples
9+
validate-schemas

.golangci.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ linters:
7171
- whitespace
7272

7373
linters-settings:
74+
revive:
75+
rules:
76+
- name: use-any
77+
disabled: false
78+
severity: error
7479
cyclop:
7580
max-complexity: 50
7681
funlen:

docs/server-json/README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,25 @@ References:
1515
- [schema.json](./schema.json) - The official JSON schema specification for this representation
1616
- [examples.md](./examples.md) - Example manifestations of the JSON schema
1717
- [registry-schema.json](./registry-schema.json) - A more constrained version of `schema.json` that the official registry supports
18+
19+
## Validation Tools
20+
21+
Two validation tools are provided in the repository's `tools/` directory:
22+
- **validate-schemas** - Validates that `schema.json` and `registry-schema.json` are valid JSON Schema documents
23+
- **validate-examples** - Validates that all JSON examples in `examples.md` conform to both schemas
24+
25+
### Usage
26+
27+
From the repository root:
28+
29+
#### Validate JSON Schemas
30+
31+
```bash
32+
./tools/validate-schemas.sh
33+
```
34+
35+
#### Validate Examples
36+
37+
```bash
38+
./tools/validate-examples.sh
39+
```

docs/server-json/examples.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ The `dnx` tool ships with the .NET 10 SDK, starting with Preview 6.
219219
"repository": {
220220
"url": "https://github.com/joelverhagen/Knapcode.SampleMcpServer",
221221
"source": "github",
222-
"id": "def456gh-ijkl-7890-mnop-qrstuvwxyz13"
222+
"id": "example-nuget-id-0000-1111-222222222222"
223223
},
224224
"version_detail": {
225225
"version": "0.3.0-beta"
@@ -251,7 +251,7 @@ The `dnx` tool ships with the .NET 10 SDK, starting with Preview 6.
251251
"description": "MCP server for database operations with support for multiple database types",
252252
"repository": {
253253
"url": "https://github.com/example/mcp-database",
254-
"source": "gitlab",
254+
"source": "github",
255255
"id": "ghi789jk-lmno-1234-pqrs-tuvwxyz56789"
256256
},
257257
"version_detail": {

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go 1.23.0
55
require (
66
github.com/caarlos0/env/v11 v11.3.1
77
github.com/google/uuid v1.6.0
8+
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
89
github.com/stretchr/testify v1.10.0
910
github.com/swaggo/files v1.0.1
1011
github.com/swaggo/http-swagger v1.3.4

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
3434
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
3535
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
3636
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
37+
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4=
38+
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY=
3739
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
3840
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
3941
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=

internal/api/handlers/v0/auth.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ func StartAuthHandler(authService auth.Service) http.HandlerFunc {
6464
// Return successful response
6565
w.Header().Set("Content-Type", "application/json")
6666
w.WriteHeader(http.StatusOK)
67-
if err := json.NewEncoder(w).Encode(map[string]interface{}{
67+
if err := json.NewEncoder(w).Encode(map[string]any{
6868
"flow_info": flowInfo,
6969
"status_token": statusToken,
7070
"expires_in": 300, // 5 minutes
@@ -98,7 +98,7 @@ func CheckAuthStatusHandler(authService auth.Service) http.HandlerFunc {
9898
// Auth is still pending
9999
w.Header().Set("Content-Type", "application/json")
100100
w.WriteHeader(http.StatusOK)
101-
if err := json.NewEncoder(w).Encode(map[string]interface{}{
101+
if err := json.NewEncoder(w).Encode(map[string]any{
102102
"status": "pending",
103103
}); err != nil {
104104
http.Error(w, "Failed to encode response", http.StatusInternalServerError)
@@ -115,7 +115,7 @@ func CheckAuthStatusHandler(authService auth.Service) http.HandlerFunc {
115115
// Authentication completed successfully
116116
w.Header().Set("Content-Type", "application/json")
117117
w.WriteHeader(http.StatusOK)
118-
if err := json.NewEncoder(w).Encode(map[string]interface{}{
118+
if err := json.NewEncoder(w).Encode(map[string]any{
119119
"status": "complete",
120120
"token": token,
121121
}); err != nil {

internal/api/handlers/v0/publish_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ func TestPublishHandler(t *testing.T) {
6161
testCases := []struct {
6262
name string
6363
method string
64-
requestBody interface{}
64+
requestBody any
6565
authHeader string
6666
setupMocks func(*MockRegistryService, *MockAuthService)
6767
expectedStatus int

internal/database/database.go

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

internal/database/import.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func ReadSeedFile(path string) ([]model.ServerDetail, error) {
3333
var servers []model.ServerDetail
3434
if err := json.Unmarshal(fileContent, &servers); err != nil {
3535
// Try parsing as a raw JSON array and then convert to our model
36-
var rawData []map[string]interface{}
36+
var rawData []map[string]any
3737
if jsonErr := json.Unmarshal(fileContent, &rawData); jsonErr != nil {
3838
return nil, fmt.Errorf("failed to parse JSON: %w (original error: %w)", jsonErr, err)
3939
}

0 commit comments

Comments
 (0)