From d0941b2cbc22443d6f4f690fad8dd2fa46422173 Mon Sep 17 00:00:00 2001 From: Serob Nahapetyan Date: Wed, 23 Sep 2026 08:42:10 +0000 Subject: [PATCH 1/3] replace segmentio/encoding with encoding/json/v2 --- .github/workflows/test.yml | 2 +- go.mod | 11 +++-------- go.sum | 8 ++------ internal/json/json.go | 23 +++++++++++++++-------- 4 files changed, 21 insertions(+), 23 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5c351b3f9..47bb47d97 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -40,7 +40,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go: ["1.25", "1.26"] + go: ["1.26", "1.27"] steps: - name: Check out code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 diff --git a/go.mod b/go.mod index 3287a9578..190d9f093 100644 --- a/go.mod +++ b/go.mod @@ -1,20 +1,15 @@ module github.com/modelcontextprotocol/go-sdk -go 1.25.0 +go 1.26 require ( + github.com/go-json-experiment/json v0.0.0-20260820222146-c27c302e5fc3 github.com/golang-jwt/jwt/v5 v5.3.1 github.com/google/go-cmp v0.7.0 github.com/google/jsonschema-go v0.4.3 - github.com/segmentio/encoding v0.5.4 github.com/yosida95/uritemplate/v3 v3.0.2 golang.org/x/oauth2 v0.35.0 + golang.org/x/sync v0.20.0 golang.org/x/time v0.15.0 golang.org/x/tools v0.42.0 ) - -require ( - github.com/segmentio/asm v1.1.3 // indirect - golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.41.0 // indirect -) diff --git a/go.sum b/go.sum index c13454aad..7e20e83b8 100644 --- a/go.sum +++ b/go.sum @@ -1,21 +1,17 @@ +github.com/go-json-experiment/json v0.0.0-20260820222146-c27c302e5fc3 h1:UADEEmDKgfXbtnGJZ97beY5XLo9ZechG1nlU4KnRrkE= +github.com/go-json-experiment/json v0.0.0-20260820222146-c27c302e5fc3/go.mod h1:tphK2c80bpPhMOI4v6bIc2xWywPfbqi1Z06+RcrMkDg= github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/jsonschema-go v0.4.3 h1:/DBOLZTfDow7pe2GmaJNhltueGTtDKICi8V8p+DQPd0= github.com/google/jsonschema-go v0.4.3/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE= -github.com/segmentio/asm v1.1.3 h1:WM03sfUOENvvKexOLp+pCqgb/WDjsi7EK8gIsICtzhc= -github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg= -github.com/segmentio/encoding v0.5.4 h1:OW1VRern8Nw6ITAtwSZ7Idrl3MXCFwXHPgqESYfvNt0= -github.com/segmentio/encoding v0.5.4/go.mod h1:HS1ZKa3kSN32ZHVZ7ZLPLXWvOVIiZtyJnO1gPH1sKt0= github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ= golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= -golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= diff --git a/internal/json/json.go b/internal/json/json.go index 3a0e52b2a..80e864dd3 100644 --- a/internal/json/json.go +++ b/internal/json/json.go @@ -8,10 +8,13 @@ package json import ( "bytes" + "encoding/json" "fmt" "io" - "github.com/segmentio/encoding/json" + jsonv2 "github.com/go-json-experiment/json" + "github.com/go-json-experiment/json/jsontext" + jsonv1 "github.com/go-json-experiment/json/v1" ) // defaultMaxDepth is the maximum JSON nesting depth accepted by [Unmarshal]. @@ -23,33 +26,37 @@ const defaultMaxDepth = 1000 // errMaxDepthExceeded is returned by [Unmarshal] when the input nests deeper than [defaultMaxDepth]. var errMaxDepthExceeded = fmt.Errorf("json: exceeded maximum nesting depth of %d", defaultMaxDepth) +var caseSensitiveV1 = jsonv2.JoinOptions( + jsonv1.DefaultOptionsV1(), + jsonv2.MatchCaseInsensitiveNames(false), +) + type Decoder struct { - dec *json.Decoder + dec *jsontext.Decoder } func NewDecoder(r io.Reader) *Decoder { - dec := json.NewDecoder(r) - dec.DontMatchCaseInsensitiveStructFields() + dec := jsontext.NewDecoder(r, caseSensitiveV1) return &Decoder{dec: dec} } func (d *Decoder) Decode(v any) error { - return d.dec.Decode(v) + return jsonv2.UnmarshalDecode(d.dec, v, caseSensitiveV1) } func Unmarshal(data []byte, v any) error { if err := checkMaxDepth(data, defaultMaxDepth); err != nil { return err } - return NewDecoder(bytes.NewReader(data)).Decode(v) + return jsonv2.Unmarshal(data, v, caseSensitiveV1) } func UnmarshalUseNumber(data []byte, v any) error { if err := checkMaxDepth(data, defaultMaxDepth); err != nil { return err } - dec := NewDecoder(bytes.NewReader(data)) - dec.dec.UseNumber() + dec := json.NewDecoder(bytes.NewReader(data)) + dec.UseNumber() return dec.Decode(v) } From 7311af9274711585dd9175520600d4f1d1b6ae2e Mon Sep 17 00:00:00 2001 From: Serob Nahapetyan Date: Fri, 25 Sep 2026 08:38:36 +0000 Subject: [PATCH 2/3] address review comment --- internal/json/json.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/json/json.go b/internal/json/json.go index 80e864dd3..dd6a7bc89 100644 --- a/internal/json/json.go +++ b/internal/json/json.go @@ -48,7 +48,7 @@ func Unmarshal(data []byte, v any) error { if err := checkMaxDepth(data, defaultMaxDepth); err != nil { return err } - return jsonv2.Unmarshal(data, v, caseSensitiveV1) + return NewDecoder(bytes.NewReader(data)).Decode(v) } func UnmarshalUseNumber(data []byte, v any) error { From 83fdb7d8b31a21bec5d885ca73c0199f080d8d1a Mon Sep 17 00:00:00 2001 From: Serob Nahapetyan Date: Mon, 26 Oct 2026 15:25:34 +0000 Subject: [PATCH 3/3] make UnmarshalUseNumber to be case sensitive as well --- internal/json/json.go | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/internal/json/json.go b/internal/json/json.go index dd6a7bc89..098e7a801 100644 --- a/internal/json/json.go +++ b/internal/json/json.go @@ -9,8 +9,10 @@ package json import ( "bytes" "encoding/json" + "errors" "fmt" "io" + "reflect" jsonv2 "github.com/go-json-experiment/json" "github.com/go-json-experiment/json/jsontext" @@ -31,17 +33,37 @@ var caseSensitiveV1 = jsonv2.JoinOptions( jsonv2.MatchCaseInsensitiveNames(false), ) +// caseSensitiveV1UseNumber adds UseNumber, which v2 has no option for: a JSON +// number decoded into an any becomes a json.Number, so large IDs stay exact. +var caseSensitiveV1UseNumber = jsonv2.JoinOptions( + caseSensitiveV1, + jsonv2.WithUnmarshalers(jsonv2.UnmarshalFromFunc(func(dec *jsontext.Decoder, v *any) error { + if dec.PeekKind() != '0' { + return errors.ErrUnsupported + } + if p := reflect.ValueOf(*v); p.Kind() == reflect.Pointer && !p.IsNil() { + return errors.ErrUnsupported + } + val, err := dec.ReadValue() + if err != nil { + return err + } + *v = json.Number(string(val)) + return nil + })), +) + type Decoder struct { - dec *jsontext.Decoder + dec *jsontext.Decoder + opts jsonv2.Options } func NewDecoder(r io.Reader) *Decoder { - dec := jsontext.NewDecoder(r, caseSensitiveV1) - return &Decoder{dec: dec} + return &Decoder{dec: jsontext.NewDecoder(r), opts: caseSensitiveV1} } func (d *Decoder) Decode(v any) error { - return jsonv2.UnmarshalDecode(d.dec, v, caseSensitiveV1) + return jsonv2.UnmarshalDecode(d.dec, v, d.opts) } func Unmarshal(data []byte, v any) error { @@ -51,11 +73,13 @@ func Unmarshal(data []byte, v any) error { return NewDecoder(bytes.NewReader(data)).Decode(v) } +func (d *Decoder) UseNumber() { d.opts = caseSensitiveV1UseNumber } + func UnmarshalUseNumber(data []byte, v any) error { if err := checkMaxDepth(data, defaultMaxDepth); err != nil { return err } - dec := json.NewDecoder(bytes.NewReader(data)) + dec := NewDecoder(bytes.NewReader(data)) dec.UseNumber() return dec.Decode(v) }