From 55d8fc58abb4f8135ed18406c912e3a9d1e8138d Mon Sep 17 00:00:00 2001 From: Ettore Di Giacinto Date: Mon, 28 Sep 2026 07:26:26 +0000 Subject: [PATCH] fix(mcp): send empty properties for tools without arguments An MCP tool that takes no arguments has no "properties" in its input schema, so the unmarshal leaves the property map nil. Before #81 the tool was sent as a jsonschema.Definition, whose MarshalJSON encodes a nil map as {}. mcpToolParameters has no such hook, so the tool went out with "properties": null. vLLM validates every tool schema and rejects the whole request with a 400 ("None is not of type 'object'"), so one argument-less MCP tool broke every call to such a server. Strict mode was not affected: it already fills in an empty properties object. Tool() now sends an empty object when the map is nil. Co-Authored-By: Claude Opus 5.5 --- mcp.go | 9 ++++++++- mcp_params_test.go | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 mcp_params_test.go diff --git a/mcp.go b/mcp.go index 2b95887..f05573d 100644 --- a/mcp.go +++ b/mcp.go @@ -21,6 +21,13 @@ type mcpTool struct { } func (t *mcpTool) Tool() openai.Tool { + // A tool without arguments has no properties, and the unmarshal leaves + // the map nil. jsonschema.Definition encoded that as {}, but this + // struct would send null, which vLLM and others reject. + props := t.props + if props == nil { + props = map[string]jsonschema.Definition{} + } return openai.Tool{ Type: openai.ToolTypeFunction, Function: &openai.FunctionDefinition{ @@ -28,7 +35,7 @@ func (t *mcpTool) Tool() openai.Tool { Description: t.description, Parameters: mcpToolParameters{ Type: jsonschema.Object, - Properties: t.props, + Properties: props, Required: t.inputSchema.Required, AdditionalProperties: t.inputSchema.AdditionalProperties, }, diff --git a/mcp_params_test.go b/mcp_params_test.go new file mode 100644 index 0000000..3f5ab13 --- /dev/null +++ b/mcp_params_test.go @@ -0,0 +1,34 @@ +package cogito + +import ( + "encoding/json" + "testing" +) + +// An MCP tool that takes no arguments has no "properties" in its input +// schema, so the unmarshal leaves props nil. OpenAI-compatible servers +// such as vLLM validate the schema and reject "properties": null, which +// fails the whole request, not only this tool. +func TestMCPToolWithoutArgumentsSendsEmptyProperties(t *testing.T) { + tool := &mcpTool{ + name: "list_things", + inputSchema: toolInputSchema{Type: "object"}, + } + + dat, err := json.Marshal(tool.Tool().Function.Parameters) + if err != nil { + t.Fatalf("marshal parameters: %v", err) + } + + var params map[string]any + if err := json.Unmarshal(dat, ¶ms); err != nil { + t.Fatalf("unmarshal parameters: %v", err) + } + props, ok := params["properties"].(map[string]any) + if !ok { + t.Fatalf("properties must be an object, got %s", dat) + } + if len(props) != 0 { + t.Fatalf("properties must be empty, got %s", dat) + } +}