Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
f8eac9d
fix: default empty, nil, or null arguments to empty object to prevent…
wibisanabama Jun 15, 2026
33b8ce3
Merge branch 'main' into fix-get-me-empty-arguments
wibisanabama Jun 17, 2026
1d1925c
Merge branch 'main' into fix-get-me-empty-arguments
wibisanabama Jun 18, 2026
2c84699
Merge branch 'main' into fix-get-me-empty-arguments
wibisanabama Jun 19, 2026
57a334d
Merge branch 'main' into fix-get-me-empty-arguments
wibisanabama Jun 26, 2026
d527f44
Merge branch 'main' into fix-get-me-empty-arguments
wibisanabama Jun 27, 2026
4c79826
Merge branch 'main' into fix-get-me-empty-arguments
wibisanabama Jun 30, 2026
8ef5d72
Merge remote-tracking branch 'upstream/main' into fix-get-me-empty-ar…
wibisanabama Jul 4, 2026
8492c46
Merge branch 'main' into fix-get-me-empty-arguments
wibisanabama Jul 7, 2026
bfa6294
Merge branch 'main' into fix-get-me-empty-arguments
wibisanabama Jul 8, 2026
df4b774
Merge branch 'main' into fix-get-me-empty-arguments
wibisanabama Jul 9, 2026
f0fdaa8
Merge branch 'main' into fix-get-me-empty-arguments
wibisanabama Jul 9, 2026
c0a1f3d
Merge branch 'main' into fix-get-me-empty-arguments
wibisanabama Jul 11, 2026
40eb730
Merge branch 'main' into fix-get-me-empty-arguments
wibisanabama Jul 18, 2026
7165d5e
Merge branch 'main' into fix-get-me-empty-arguments
wibisanabama Jul 19, 2026
a7058d9
Merge branch 'main' into fix-get-me-empty-arguments
wibisanabama Jul 21, 2026
59fb6f9
Merge branch 'main' into fix-get-me-empty-arguments
wibisanabama Jul 22, 2026
63e8d98
Merge branch 'main' into fix-get-me-empty-arguments
wibisanabama Jul 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion pkg/inventory/server_tool.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,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
91 changes: 56 additions & 35 deletions pkg/inventory/server_tool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,49 +81,70 @@ func TestNewServerToolWithContextHandler_ValidArguments_Succeeds(t *testing.T) {
assert.Equal(t, "success: octocat/hello-world", textContent.Text)
}

func TestServerToolRegisterFuncAppliesMiddleware(t *testing.T) {
tool := NewServerTool(
mcp.Tool{
Name: "wrapped_tool",
InputSchema: &jsonschema.Schema{Type: "object"},
},
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) (*mcp.CallToolResult, error) {
func(_ context.Context, _ *mcp.CallToolRequest, args expectedArgs) (*mcp.CallToolResult, any, error) {
return &mcp.CallToolResult{
Content: []mcp.Content{&mcp.TextContent{Text: "handler"}},
}, nil
Content: []mcp.Content{
&mcp.TextContent{Text: "success: " + args.Owner + "/" + args.Repo},
},
}, nil, nil
},
)

middlewareCalled := make(chan struct{}, 1)
middleware := func(next mcp.ToolHandler) mcp.ToolHandler {
return func(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
middlewareCalled <- struct{}{}
return next(ctx, req)
}
}

server := mcp.NewServer(&mcp.Implementation{Name: "test-server", Version: "v0.0.1"}, nil)
tool.RegisterFunc(server, nil, middleware)
st, ct := mcp.NewInMemoryTransports()
ss, err := server.Connect(context.Background(), st, nil)
require.NoError(t, err)
t.Cleanup(func() { _ = ss.Close() })
handler := tool.HandlerFunc(nil)

client := mcp.NewClient(&mcp.Implementation{Name: "test-client", Version: "v0.0.1"}, nil)
cs, err := client.Connect(context.Background(), ct, nil)
require.NoError(t, err)
t.Cleanup(func() { _ = cs.Close() })
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: /",
},
}

result, err := cs.CallTool(context.Background(), &mcp.CallToolParams{Name: "wrapped_tool"})
require.NoError(t, err)
select {
case <-middlewareCalled:
default:
t.Fatal("tool middleware was not called")
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)
})
}
require.Len(t, result.Content, 1)
assert.Equal(t, "handler", result.Content[0].(*mcp.TextContent).Text)
}

func TestAnnotateHeaderParams(t *testing.T) {
Expand Down