Skip to content
Open
9 changes: 8 additions & 1 deletion pkg/inventory/server_tool.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,15 @@ func NewServerToolWithContextHandler[In any, Out any](tool mcp.Tool, toolset Too
// HandlerFunc ignores deps - deps are retrieved from context at call time
HandlerFunc: func(_ any) mcp.ToolHandler {
return func(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
argumentsRaw := []byte("{}")
if req.Params != nil && len(req.Params.Arguments) > 0 {
argumentsRaw = req.Params.Arguments
}
if string(argumentsRaw) == "null" {
argumentsRaw = []byte("{}")
}
var arguments In
if err := json.Unmarshal(req.Params.Arguments, &arguments); err != nil {
if err := json.Unmarshal(argumentsRaw, &arguments); err != nil {
return &mcp.CallToolResult{
Content: []mcp.Content{
&mcp.TextContent{Text: fmt.Sprintf("invalid arguments: %s", err)},
Expand Down
67 changes: 67 additions & 0 deletions pkg/inventory/server_tool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,70 @@ func TestNewServerToolWithContextHandler_ValidArguments_Succeeds(t *testing.T) {
require.True(t, ok)
assert.Equal(t, "success: octocat/hello-world", textContent.Text)
}

func TestNewServerToolWithContextHandler_EmptyArguments_Succeeds(t *testing.T) {
type expectedArgs struct {
Owner string `json:"owner,omitempty"`
Repo string `json:"repo,omitempty"`
}

tool := NewServerToolWithContextHandler(
mcp.Tool{Name: "test_tool"},
testToolsetMetadata("test"),
func(_ context.Context, _ *mcp.CallToolRequest, args expectedArgs) (*mcp.CallToolResult, any, error) {
return &mcp.CallToolResult{
Content: []mcp.Content{
&mcp.TextContent{Text: "success: " + args.Owner + "/" + args.Repo},
},
}, nil, nil
},
)

handler := tool.HandlerFunc(nil)

testCases := []struct {
name string
arguments json.RawMessage
expected string
}{
{
name: "nil arguments",
arguments: nil,
expected: "success: /",
},
{
name: "empty arguments",
arguments: json.RawMessage(``),
expected: "success: /",
},
{
name: "null arguments",
arguments: json.RawMessage(`null`),
expected: "success: /",
},
{
name: "empty object arguments",
arguments: json.RawMessage(`{}`),
expected: "success: /",
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
result, err := handler(context.Background(), &mcp.CallToolRequest{
Params: &mcp.CallToolParamsRaw{
Name: "test_tool",
Arguments: tc.arguments,
},
})

require.NoError(t, err)
require.NotNil(t, result)
assert.False(t, result.IsError)
textContent, ok := result.Content[0].(*mcp.TextContent)
require.True(t, ok)
assert.Equal(t, tc.expected, textContent.Text)
})
}
}