mcp: correct the JSON used for unstructured content

Recently, I refactored to fix validation and default application for
structured output. However, I used the wrong byte slice for the
unstructured output. Fix it to use the same bytes as structured output,
with a test.

Fixes #475
This commit is contained in:
Rob Findley 2025-09-17 21:05:52 +00:00 • committed by Robert Findley
commit a3e753b8d8
2 changed files with 32 additions and 3 deletions

View file

@ -292,7 +292,7 @@ func toolForErr[In, Out any](t *Tool, h ToolHandlerFor[In, Out]) (*Tool, ToolHan
// https://modelcontextprotocol.io/specification/2025-06-18/server/tools#structured-content.
if res.Content == nil {
res.Content = []Content{&TextContent{
Text: string(outbytes),
Text: string(outJSON),
}}
}
}

View file

@ -512,8 +512,7 @@ func testToolForSchema[In, Out any](t *testing.T, tool *Tool, in string, out Out
Arguments: json.RawMessage(in),
},
}
_, err = goth(context.Background(), ctr)
result, err := goth(context.Background(), ctr)
if wantErrContaining != "" {
if err == nil {
t.Errorf("got nil error, want error containing %q", wantErrContaining)
@ -525,8 +524,18 @@ func testToolForSchema[In, Out any](t *testing.T, tool *Tool, in string, out Out
} else if err != nil {
t.Errorf("got error %v, want no error", err)
}
if gott.OutputSchema != nil && err == nil && !result.IsError {
// Check that structured content matches exactly.
unstructured := result.Content[0].(*TextContent).Text
structured := string(result.StructuredContent.(json.RawMessage))
if diff := cmp.Diff(unstructured, structured); diff != "" {
t.Errorf("Unstructured content does not match structured content exactly (-unstructured +structured):\n%s", diff)
}
}
}
// TODO: move this to tool_test.go
func TestToolForSchemas(t *testing.T) {
// Validate that toolForErr handles schemas properly.
type in struct {
@ -558,4 +567,24 @@ func TestToolForSchemas(t *testing.T) {
// Tool sets output schema: that is what's used, and validation happens.
testToolForSchema[in, any](t, &Tool{OutputSchema: outSchema2}, `{"p":3}`, out{true},
inSchema, outSchema2, `want "integer"`)
// Check a slightly more complicated case.
type weatherOutput struct {
Summary string
AsOf time.Time
Source string
}
testToolForSchema[any](t, &Tool{}, `{}`, weatherOutput{},
&schema{Type: "object"},
&schema{
Type: "object",
Required: []string{"Summary", "AsOf", "Source"},
AdditionalProperties: falseSchema,
Properties: map[string]*schema{
"Summary": {Type: "string"},
"AsOf": {Type: "string"},
"Source": {Type: "string"},
},
},
"")
}