From 9ff77120a59445df617ed15186e46c647912742c Mon Sep 17 00:00:00 2001 From: Alejandro Borbolla Date: Tue, 2 Sep 2025 17:05:41 +0200 Subject: [PATCH 1/2] fix(tool): Do not return empty `outputSchema` If the output schema is not specify, we do not return it as we break the MCP specification that are expecting the following format: ```json "outputSchema": { "type": "object", "properties": {}, "required": [] } ``` --- mcp/tools.go | 2 +- mcp/tools_test.go | 60 +++++++++++++++++++++++++++++++++-------------- 2 files changed, 43 insertions(+), 19 deletions(-) diff --git a/mcp/tools.go b/mcp/tools.go index 185aefa69..493e8c778 100644 --- a/mcp/tools.go +++ b/mcp/tools.go @@ -607,7 +607,7 @@ func (t Tool) MarshalJSON() ([]byte, error) { return nil, fmt.Errorf("tool %s has both OutputSchema and RawOutputSchema set: %w", t.Name, errToolSchemaConflict) } m["outputSchema"] = t.RawOutputSchema - } else { + } else if t.OutputSchema.Type != "" { // If no output schema is specified, do not return anything m["outputSchema"] = t.OutputSchema } diff --git a/mcp/tools_test.go b/mcp/tools_test.go index 270bf64b0..c8deacfea 100644 --- a/mcp/tools_test.go +++ b/mcp/tools_test.go @@ -586,27 +586,51 @@ func TestToolWithOutputSchema(t *testing.T) { Email string `json:"email,omitempty" jsonschema_description:"Email address"` } - tool := NewTool("test_tool", - WithDescription("Test tool with output schema"), - WithOutputSchema[TestOutput](), - WithString("input", Required()), - ) - - // Check that RawOutputSchema was set - assert.NotNil(t, tool.OutputSchema) + tests := []struct { + name string + tool Tool + expectedOutputSchema bool + }{ + { + name: "default behavior", + tool: NewTool("test_tool", + WithDescription("Test tool with output schema"), + WithOutputSchema[TestOutput](), + WithString("input", Required()), + ), + expectedOutputSchema: true, + }, + { + name: "no output schema is set", + tool: NewTool("test_tool", + WithDescription("Test tool with output schema"), + WithString("input", Required()), + ), + expectedOutputSchema: false, + }, + } - // Marshal and verify structure - data, err := json.Marshal(tool) - assert.NoError(t, err) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Marshal and verify structure + data, err := json.Marshal(tt.tool) + assert.NoError(t, err) - var toolData map[string]any - err = json.Unmarshal(data, &toolData) - assert.NoError(t, err) + var toolData map[string]any + err = json.Unmarshal(data, &toolData) + assert.NoError(t, err) - // Verify outputSchema exists - outputSchema, exists := toolData["outputSchema"] - assert.True(t, exists) - assert.NotNil(t, outputSchema) + // Verify outputSchema exists + outputSchema, exists := toolData["outputSchema"] + if tt.expectedOutputSchema { + assert.True(t, exists) + assert.NotNil(t, outputSchema) + } else { + assert.False(t, exists) + assert.Nil(t, outputSchema) + } + }) + } } // TestNewToolResultStructured tests that the NewToolResultStructured function From 4fee9866541000918c16af16c6c7a1a0946635b3 Mon Sep 17 00:00:00 2001 From: Alejandro Borbolla Date: Tue, 2 Sep 2025 17:17:58 +0200 Subject: [PATCH 2/2] fix(tool): typo in test --- mcp/tools_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mcp/tools_test.go b/mcp/tools_test.go index c8deacfea..4bb07aa5d 100644 --- a/mcp/tools_test.go +++ b/mcp/tools_test.go @@ -603,7 +603,7 @@ func TestToolWithOutputSchema(t *testing.T) { { name: "no output schema is set", tool: NewTool("test_tool", - WithDescription("Test tool with output schema"), + WithDescription("Test tool with no output schema"), WithString("input", Required()), ), expectedOutputSchema: false,