Skip to content

Commit b924391

Browse files
authored
fix(tool): Return the OutputSchema from the tool definition (#571)
This commit returns the `OutputSchema` from the tool definition as per the MCP spec: https://modelcontextprotocol.io/specification/2025-06-18/server/tools#output-schema The behaviour of `WithOutputSchema()` also changed as `RawOutputSchema` is not longer populated, but `OutputSchema` is from the `T` generic type. The only way now to set `RawOutputSchema` is through the `WithRawOutputSchema()` method.
1 parent 4e353ac commit b924391

File tree

2 files changed

+23
-4
lines changed

2 files changed

+23
-4
lines changed

mcp/tools.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,8 @@ type Tool struct {
565565
InputSchema ToolInputSchema `json:"inputSchema"`
566566
// Alternative to InputSchema - allows arbitrary JSON Schema to be provided
567567
RawInputSchema json.RawMessage `json:"-"` // Hide this from JSON marshaling
568+
// A JSON Schema object defining the expected output returned by the tool .
569+
OutputSchema ToolOutputSchema `json:"outputSchema,omitempty"`
568570
// Optional JSON Schema defining expected output structure
569571
RawOutputSchema json.RawMessage `json:"-"` // Hide this from JSON marshaling
570572
// Optional properties describing tool behavior
@@ -601,23 +603,32 @@ func (t Tool) MarshalJSON() ([]byte, error) {
601603

602604
// Add output schema if present
603605
if t.RawOutputSchema != nil {
606+
if t.OutputSchema.Type != "" {
607+
return nil, fmt.Errorf("tool %s has both OutputSchema and RawOutputSchema set: %w", t.Name, errToolSchemaConflict)
608+
}
604609
m["outputSchema"] = t.RawOutputSchema
610+
} else {
611+
m["outputSchema"] = t.OutputSchema
605612
}
606613

607614
m["annotations"] = t.Annotations
608615

609616
return json.Marshal(m)
610617
}
611618

612-
type ToolInputSchema struct {
619+
// ToolArgumentsSchema represents a JSON Schema for tool arguments.
620+
type ToolArgumentsSchema struct {
613621
Defs map[string]any `json:"$defs,omitempty"`
614622
Type string `json:"type"`
615623
Properties map[string]any `json:"properties,omitempty"`
616624
Required []string `json:"required,omitempty"`
617625
}
618626

627+
type ToolInputSchema ToolArgumentsSchema // For retro-compatibility
628+
type ToolOutputSchema ToolArgumentsSchema
629+
619630
// MarshalJSON implements the json.Marshaler interface for ToolInputSchema.
620-
func (tis ToolInputSchema) MarshalJSON() ([]byte, error) {
631+
func (tis ToolArgumentsSchema) MarshalJSON() ([]byte, error) {
621632
m := make(map[string]any)
622633
m["type"] = tis.Type
623634

@@ -780,7 +791,15 @@ func WithOutputSchema[T any]() ToolOption {
780791
return
781792
}
782793

783-
t.RawOutputSchema = json.RawMessage(mcpSchema)
794+
// Retrieve the schema from raw JSON
795+
if err := json.Unmarshal(mcpSchema, &t.OutputSchema); err != nil {
796+
// Skip and maintain backward compatibility
797+
return
798+
}
799+
800+
// Always set the type to "object" as of the current MCP spec
801+
// https://modelcontextprotocol.io/specification/2025-06-18/server/tools#output-schema
802+
t.OutputSchema.Type = "object"
784803
}
785804
}
786805

mcp/tools_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -593,7 +593,7 @@ func TestToolWithOutputSchema(t *testing.T) {
593593
)
594594

595595
// Check that RawOutputSchema was set
596-
assert.NotNil(t, tool.RawOutputSchema)
596+
assert.NotNil(t, tool.OutputSchema)
597597

598598
// Marshal and verify structure
599599
data, err := json.Marshal(tool)

0 commit comments

Comments
 (0)