Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 5 additions & 5 deletions mcp/mcp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func TestEndToEnd(t *testing.T) {
Name: "greet",
Description: "say hi",
}, sayHi)
AddTool(s, &Tool{Name: "fail", InputSchema: &jsonschema.Schema{}},
AddTool(s, &Tool{Name: "fail", InputSchema: &jsonschema.Schema{Type: "object"}},
func(context.Context, *CallToolRequest, map[string]any) (*CallToolResult, any, error) {
return nil, nil, errTestFailure
})
Expand Down Expand Up @@ -247,7 +247,7 @@ func TestEndToEnd(t *testing.T) {
t.Errorf("tools/call 'fail' mismatch (-want +got):\n%s", diff)
}

s.AddTool(&Tool{Name: "T", InputSchema: &jsonschema.Schema{}}, nopHandler)
s.AddTool(&Tool{Name: "T", InputSchema: &jsonschema.Schema{Type: "object"}}, nopHandler)
waitForNotification(t, "tools")
s.RemoveTools("T")
waitForNotification(t, "tools")
Expand Down Expand Up @@ -674,7 +674,7 @@ func TestCancellation(t *testing.T) {
return nil, nil, nil
}
cs, _ := basicConnection(t, func(s *Server) {
AddTool(s, &Tool{Name: "slow", InputSchema: &jsonschema.Schema{}}, slowRequest)
AddTool(s, &Tool{Name: "slow", InputSchema: &jsonschema.Schema{Type: "object"}}, slowRequest)
})
defer cs.Close()

Expand Down Expand Up @@ -961,8 +961,8 @@ func TestAddTool_DuplicateNoPanicAndNoDuplicate(t *testing.T) {
// Use two distinct Tool instances with the same name but different
// descriptions to ensure the second replaces the first
// This case was written specifically to reproduce a bug where duplicate tools where causing jsonschema errors
t1 := &Tool{Name: "dup", Description: "first", InputSchema: &jsonschema.Schema{}}
t2 := &Tool{Name: "dup", Description: "second", InputSchema: &jsonschema.Schema{}}
t1 := &Tool{Name: "dup", Description: "first", InputSchema: &jsonschema.Schema{Type: "object"}}
t2 := &Tool{Name: "dup", Description: "second", InputSchema: &jsonschema.Schema{Type: "object"}}
s.AddTool(t1, nopHandler)
s.AddTool(t2, nopHandler)
})
Expand Down
9 changes: 9 additions & 0 deletions mcp/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ func (s *Server) AddTool(t *Tool, h ToolHandler) {
// discovered until runtime, when the LLM sent bad data.
panic(fmt.Errorf("AddTool %q: missing input schema", t.Name))
}
if t.InputSchema.Type != "object" {
panic(fmt.Errorf(`AddTool %q: input schema must have type "object"`, t.Name))
}
st := &serverTool{tool: t, handler: h}
// Assume there was a change, since add replaces existing tools.
// (It's possible a tool was replaced with an identical one, but not worth checking.)
Expand Down Expand Up @@ -190,6 +193,12 @@ func ToolFor[In, Out any](t *Tool, h ToolHandlerFor[In, Out]) (*Tool, ToolHandle
// TODO(v0.3.0): test
func toolForErr[In, Out any](t *Tool, h ToolHandlerFor[In, Out]) (*Tool, ToolHandler, error) {
tt := *t

// Special handling for an "any" input: treat as an empty object.
if reflect.TypeFor[In]() == reflect.TypeFor[any]() && t.InputSchema == nil {
tt.InputSchema = &jsonschema.Schema{Type: "object"}
}

var inputResolved *jsonschema.Resolved
if _, err := setSchema[In](&tt.InputSchema, &inputResolved); err != nil {
return nil, nil, fmt.Errorf("input schema: %w", err)
Expand Down
2 changes: 1 addition & 1 deletion mcp/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ func TestServerPaginateVariousPageSizes(t *testing.T) {
}

func TestServerCapabilities(t *testing.T) {
tool := &Tool{Name: "t", InputSchema: &jsonschema.Schema{}}
tool := &Tool{Name: "t", InputSchema: &jsonschema.Schema{Type: "object"}}
testCases := []struct {
name string
configureServer func(s *Server)
Expand Down
6 changes: 3 additions & 3 deletions mcp/streamable_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ func testClientReplay(t *testing.T, test clientReplayTest) {
// proxy-killing action.
serverReadyToKillProxy := make(chan struct{})
serverClosed := make(chan struct{})
AddTool(server, &Tool{Name: "multiMessageTool", InputSchema: &jsonschema.Schema{}},
AddTool(server, &Tool{Name: "multiMessageTool", InputSchema: &jsonschema.Schema{Type: "object"}},
func(ctx context.Context, req *CallToolRequest, args map[string]any) (*CallToolResult, any, error) {
// Send one message to the request context, and another to a background
// context (which will end up on the hanging GET).
Expand Down Expand Up @@ -353,7 +353,7 @@ func TestServerInitiatedSSE(t *testing.T) {
t.Fatalf("client.Connect() failed: %v", err)
}
defer clientSession.Close()
AddTool(server, &Tool{Name: "testTool", InputSchema: &jsonschema.Schema{}},
AddTool(server, &Tool{Name: "testTool", InputSchema: &jsonschema.Schema{Type: "object"}},
func(context.Context, *CallToolRequest, map[string]any) (*CallToolResult, any, error) {
return &CallToolResult{}, nil, nil
})
Expand Down Expand Up @@ -658,7 +658,7 @@ func TestStreamableServerTransport(t *testing.T) {
// behavior, if any.
server := NewServer(&Implementation{Name: "testServer", Version: "v1.0.0"}, nil)
server.AddTool(
&Tool{Name: "tool", InputSchema: &jsonschema.Schema{}},
&Tool{Name: "tool", InputSchema: &jsonschema.Schema{Type: "object"}},
func(ctx context.Context, req *CallToolRequest) (*CallToolResult, error) {
if test.tool != nil {
test.tool(t, ctx, req.Session)
Expand Down