Skip to content

Commit 17595e2

Browse files
fix(inventory): gate typed output on supported protocol range
Select modern typed output for SDK-supported ISO-date protocol versions from 2026-07-28 onward. Missing, malformed, and unsupported future versions retain legacy behavior. Cover a later supported version through the selector list seam and verify the public path against the SDK supported set. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent 3b9f6a9 commit 17595e2

3 files changed

Lines changed: 51 additions & 8 deletions

File tree

‎docs/typed-tool-schemas.md‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,10 @@ subschemas. Default removal visits each child once per parent. The advertised
4343
schema can retain its defaults; the constructor caches the runtime schema
4444
without mutating either caller-owned schema.
4545

46-
The output schema and `structuredContent` are exposed only for the exact
47-
protocol version `2026-07-28`. Older, absent, and unrecognized versions retain
48-
the legacy text result. Normal `Inventory.RegisterTools` and
46+
The output schema and `structuredContent` are exposed only for a negotiated,
47+
SDK-supported protocol version `2026-07-28` or later. Unknown versions are
48+
treated as legacy, including unsupported future dates; older, absent, and
49+
malformed versions also retain the legacy text result. Normal `Inventory.RegisterTools` and
4950
`ServerTool.RegisterFunc` registrations select behavior per request. Use
5051
`RegisterToolsForProtocolEra` or `RegisterFuncForProtocolEra` only when the
5152
protocol era is already known before server construction, such as a stateless

‎pkg/inventory/typed_output_test.go‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,40 @@ func TestCachedExplicitSchemaWarmPathsPreserveOwnership(t *testing.T) {
171171
assert.Zero(t, allocations, "cache-owned warm schemas must avoid canonical serialization and annotation cloning")
172172
}
173173

174+
func TestProtocolEraForSupportedVersions(t *testing.T) {
175+
supported := []string{"2027-01-01", ProtocolVersionMultiRoundTrip, "2025-11-25", "2027-02-30", "garbage"}
176+
for _, tc := range []struct {
177+
name string
178+
version string
179+
want ProtocolEra
180+
}{
181+
{name: "exact threshold", version: ProtocolVersionMultiRoundTrip, want: ProtocolEraModern},
182+
{name: "supported later", version: "2027-01-01", want: ProtocolEraModern},
183+
{name: "older", version: "2025-11-25", want: ProtocolEraLegacy},
184+
{name: "unsupported future", version: "2099-01-01", want: ProtocolEraLegacy},
185+
{name: "invalid date even if listed", version: "2027-02-30", want: ProtocolEraLegacy},
186+
{name: "garbage even if listed", version: "garbage", want: ProtocolEraLegacy},
187+
{name: "absent", want: ProtocolEraLegacy},
188+
} {
189+
t.Run(tc.name, func(t *testing.T) {
190+
assert.Equal(t, tc.want, protocolEraForSupportedVersion(tc.version, supported))
191+
})
192+
}
193+
assert.Equal(t, ProtocolEraLegacy, protocolEraForSupportedVersion(ProtocolVersionMultiRoundTrip, nil),
194+
"a valid modern date must still be supported")
195+
196+
for _, version := range mcp.SupportedProtocolVersions() {
197+
want := ProtocolEraLegacy
198+
if version >= ProtocolVersionMultiRoundTrip {
199+
want = ProtocolEraModern
200+
}
201+
assert.Equal(t, want, ProtocolEraForVersion(version), "SDK-supported version %q", version)
202+
}
203+
for _, version := range []string{"", "garbage", "2027-02-30", "2099-01-01"} {
204+
assert.Equal(t, ProtocolEraLegacy, ProtocolEraForVersion(version), "unknown version %q", version)
205+
}
206+
}
207+
174208
func TestCachedSchemaDeepCopiesMutableMetadata(t *testing.T) {
175209
constant := any(map[string]any{"nested": []string{"const"}})
176210
nullConstant := any(nil)

‎pkg/inventory/typed_schema.go‎

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"sort"
1010
"strings"
1111
"sync"
12+
"time"
1213

1314
"github.com/google/jsonschema-go/jsonschema"
1415
"github.com/modelcontextprotocol/go-sdk/mcp"
@@ -26,13 +27,20 @@ const (
2627
ProtocolEraModern
2728
)
2829

29-
// ProtocolEraForVersion selects modern behavior only for the exact supported
30-
// version. Unknown and future versions remain on the legacy contract.
30+
// ProtocolEraForVersion selects modern behavior for SDK-supported protocol
31+
// versions from 2026-07-28 onward. Unknown versions remain legacy.
3132
func ProtocolEraForVersion(version string) ProtocolEra {
32-
if version == ProtocolVersionMultiRoundTrip {
33-
return ProtocolEraModern
33+
return protocolEraForSupportedVersion(version, mcp.SupportedProtocolVersions())
34+
}
35+
36+
func protocolEraForSupportedVersion(version string, supported []string) ProtocolEra {
37+
if _, err := time.Parse(time.DateOnly, version); err != nil {
38+
return ProtocolEraLegacy
39+
}
40+
if version < ProtocolVersionMultiRoundTrip || !slices.Contains(supported, version) {
41+
return ProtocolEraLegacy
3442
}
35-
return ProtocolEraLegacy
43+
return ProtocolEraModern
3644
}
3745

3846
// SchemaEnum adds an enum to a property of an inferred schema. Path is a

0 commit comments

Comments
 (0)