Skip to content

Commit 33ff851

Browse files
authored
mcp: update to jsonschema that does not validate structs (#511)
Update to the latest jsonschema commit, which removes the ability to validate structs and changes the type of ForOptions.TypeSchemas.
1 parent ef2fc60 commit 33ff851

File tree

11 files changed

+107
-45
lines changed

11 files changed

+107
-45
lines changed

docs/server.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -364,9 +364,9 @@ the `Tool.InputSchema` explicitly:
364364

365365
```go
366366
// Distinguished Go types allow custom schemas to be reused during inference.
367-
customSchemas := map[any]*jsonschema.Schema{
368-
Probability(0): {Type: "number", Minimum: jsonschema.Ptr(0.0), Maximum: jsonschema.Ptr(1.0)},
369-
WeatherType(""): {Type: "string", Enum: []any{Sunny, PartlyCloudy, Cloudy, Rainy, Snowy}},
367+
customSchemas := map[reflect.Type]*jsonschema.Schema{
368+
reflect.TypeFor[Probability](): {Type: "number", Minimum: jsonschema.Ptr(0.0), Maximum: jsonschema.Ptr(1.0)},
369+
reflect.TypeFor[WeatherType](): {Type: "string", Enum: []any{Sunny, PartlyCloudy, Cloudy, Rainy, Snowy}},
370370
}
371371
opts := &jsonschema.ForOptions{TypeSchemas: customSchemas}
372372
in, err := jsonschema.For[WeatherInput](opts)

examples/server/auth-middleware/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ require (
88
)
99

1010
require (
11-
github.com/google/jsonschema-go v0.2.3 // indirect
11+
github.com/google/jsonschema-go v0.3.0 // indirect
1212
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
1313
)
1414

examples/server/auth-middleware/go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeD
22
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
33
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
44
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
5-
github.com/google/jsonschema-go v0.2.3 h1:dkP3B96OtZKKFvdrUSaDkL+YDx8Uw9uC4Y+eukpCnmM=
6-
github.com/google/jsonschema-go v0.2.3/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
5+
github.com/google/jsonschema-go v0.3.0 h1:6AH2TxVNtk3IlvkkhjrtbUc4S8AvO0Xii0DxIygDg+Q=
6+
github.com/google/jsonschema-go v0.3.0/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
77
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
88
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
99
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=

examples/server/rate-limiting/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ require (
88
)
99

1010
require (
11-
github.com/google/jsonschema-go v0.2.3 // indirect
11+
github.com/google/jsonschema-go v0.3.0 // indirect
1212
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
1313
)
1414

examples/server/rate-limiting/go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
22
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
3-
github.com/google/jsonschema-go v0.2.3 h1:dkP3B96OtZKKFvdrUSaDkL+YDx8Uw9uC4Y+eukpCnmM=
4-
github.com/google/jsonschema-go v0.2.3/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
3+
github.com/google/jsonschema-go v0.3.0 h1:6AH2TxVNtk3IlvkkhjrtbUc4S8AvO0Xii0DxIygDg+Q=
4+
github.com/google/jsonschema-go v0.3.0/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
55
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
66
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
77
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=

examples/server/toolschemas/main.go

Lines changed: 52 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -54,27 +54,72 @@ func (t *manualGreeter) greet(_ context.Context, req *mcp.CallToolRequest) (*mcp
5454
// Handle the parsing and validation of input and output.
5555
//
5656
// Note that errors here are treated as tool errors, not protocol errors.
57+
58+
// First, unmarshal to a map[string]any and validate.
59+
if err := unmarshalAndValidate(req.Params.Arguments, t.inputSchema); err != nil {
60+
return errf("invalid input: %v", err), nil
61+
}
62+
63+
// Now unmarshal again to input.
5764
var input Input
5865
if err := json.Unmarshal(req.Params.Arguments, &input); err != nil {
5966
return errf("failed to unmarshal arguments: %v", err), nil
6067
}
61-
if err := t.inputSchema.Validate(input); err != nil {
62-
return errf("invalid input: %v", err), nil
63-
}
6468
output := Output{Greeting: "Hi " + input.Name}
65-
if err := t.outputSchema.Validate(output); err != nil {
66-
return errf("tool produced invalid output: %v", err), nil
67-
}
6869
outputJSON, err := json.Marshal(output)
6970
if err != nil {
7071
return errf("output failed to marshal: %v", err), nil
7172
}
73+
//
74+
if err := unmarshalAndValidate(outputJSON, t.outputSchema); err != nil {
75+
return errf("invalid output: %v", err), nil
76+
}
77+
7278
return &mcp.CallToolResult{
7379
Content: []mcp.Content{&mcp.TextContent{Text: string(outputJSON)}},
7480
StructuredContent: output,
7581
}, nil
7682
}
7783

84+
// unmarshalAndValidate unmarshals data to a map[string]any, then validates that against res.
85+
func unmarshalAndValidate(data []byte, res *jsonschema.Resolved) error {
86+
var m map[string]any
87+
if err := json.Unmarshal(data, &m); err != nil {
88+
return err
89+
}
90+
return res.Validate(m)
91+
}
92+
93+
var (
94+
inputSchema = &jsonschema.Schema{
95+
Type: "object",
96+
Properties: map[string]*jsonschema.Schema{
97+
"name": {Type: "string", MaxLength: jsonschema.Ptr(10)},
98+
},
99+
}
100+
outputSchema = &jsonschema.Schema{
101+
Type: "object",
102+
Properties: map[string]*jsonschema.Schema{
103+
"greeting": {Type: "string"},
104+
},
105+
}
106+
)
107+
108+
func newManualGreeter() (*manualGreeter, error) {
109+
resIn, err := inputSchema.Resolve(nil)
110+
if err != nil {
111+
return nil, err
112+
}
113+
resOut, err := outputSchema.Resolve(nil)
114+
if err != nil {
115+
return nil, err
116+
}
117+
return &manualGreeter{
118+
inputSchema: resIn,
119+
outputSchema: resOut,
120+
}, nil
121+
}
122+
78123
func main() {
79124
server := mcp.NewServer(&mcp.Implementation{Name: "greeter"}, nil)
80125

@@ -90,27 +135,7 @@ func main() {
90135
//
91136
// We don't need to do all this work: below, we use jsonschema.For to start
92137
// from the default schema.
93-
var (
94-
manual manualGreeter
95-
err error
96-
)
97-
inputSchema := &jsonschema.Schema{
98-
Type: "object",
99-
Properties: map[string]*jsonschema.Schema{
100-
"name": {Type: "string", MaxLength: jsonschema.Ptr(10)},
101-
},
102-
}
103-
manual.inputSchema, err = inputSchema.Resolve(nil)
104-
if err != nil {
105-
log.Fatal(err)
106-
}
107-
outputSchema := &jsonschema.Schema{
108-
Type: "object",
109-
Properties: map[string]*jsonschema.Schema{
110-
"greeting": {Type: "string"},
111-
},
112-
}
113-
manual.outputSchema, err = outputSchema.Resolve(nil)
138+
manual, err := newManualGreeter()
114139
if err != nil {
115140
log.Fatal(err)
116141
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright 2025 The Go MCP SDK Authors. All rights reserved.
2+
// Use of this source code is governed by an MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
import (
8+
"context"
9+
"encoding/json"
10+
"testing"
11+
12+
"github.com/modelcontextprotocol/go-sdk/mcp"
13+
)
14+
15+
func TestGreet(t *testing.T) {
16+
manual, err := newManualGreeter()
17+
if err != nil {
18+
t.Fatal(err)
19+
}
20+
res, err := manual.greet(context.Background(), &mcp.CallToolRequest{
21+
Params: &mcp.CallToolParamsRaw{
22+
Arguments: json.RawMessage(`{"name": "Bob"}`),
23+
},
24+
})
25+
if err != nil {
26+
t.Fatal(err)
27+
}
28+
if res.IsError {
29+
t.Fatalf("tool error: %q", res.Content[0].(*mcp.TextContent).Text)
30+
}
31+
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ go 1.23.0
55
require (
66
github.com/golang-jwt/jwt/v5 v5.2.2
77
github.com/google/go-cmp v0.7.0
8-
github.com/google/jsonschema-go v0.2.3
8+
github.com/google/jsonschema-go v0.3.0
99
github.com/yosida95/uritemplate/v3 v3.0.2
1010
golang.org/x/tools v0.34.0
1111
)

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
44
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
55
github.com/google/jsonschema-go v0.2.3 h1:dkP3B96OtZKKFvdrUSaDkL+YDx8Uw9uC4Y+eukpCnmM=
66
github.com/google/jsonschema-go v0.2.3/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
7+
github.com/google/jsonschema-go v0.2.4-0.20250922144851-e08864c65371 h1:e1VCqWtKpTYBOBhPcgGV5whTlMFpTbH5Ghm56wpxBsk=
8+
github.com/google/jsonschema-go v0.2.4-0.20250922144851-e08864c65371/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
9+
github.com/google/jsonschema-go v0.3.0 h1:6AH2TxVNtk3IlvkkhjrtbUc4S8AvO0Xii0DxIygDg+Q=
10+
github.com/google/jsonschema-go v0.3.0/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
711
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
812
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
913
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=

mcp/streamable_bench_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"context"
99
"net/http"
1010
"net/http/httptest"
11+
"reflect"
1112
"testing"
1213

1314
"github.com/google/jsonschema-go/jsonschema"
@@ -17,9 +18,9 @@ import (
1718
func BenchmarkStreamableServing(b *testing.B) {
1819
// This benchmark measures how fast we can handle a single tool on a
1920
// streamable server, including tool validation and stream management.
20-
customSchemas := map[any]*jsonschema.Schema{
21-
Probability(0): {Type: "number", Minimum: jsonschema.Ptr(0.0), Maximum: jsonschema.Ptr(1.0)},
22-
WeatherType(""): {Type: "string", Enum: []any{Sunny, PartlyCloudy, Cloudy, Rainy, Snowy}},
21+
customSchemas := map[reflect.Type]*jsonschema.Schema{
22+
reflect.TypeFor[Probability](): {Type: "number", Minimum: jsonschema.Ptr(0.0), Maximum: jsonschema.Ptr(1.0)},
23+
reflect.TypeFor[WeatherType](): {Type: "string", Enum: []any{Sunny, PartlyCloudy, Cloudy, Rainy, Snowy}},
2324
}
2425
opts := &jsonschema.ForOptions{TypeSchemas: customSchemas}
2526
in, err := jsonschema.For[WeatherInput](opts)

0 commit comments

Comments
 (0)