mcp: panic if AddTool method is given a nil InputSchema (#157)

The spec requires an input schema for every tool.
This commit is contained in:
Jonathan Amsterdam 2025-07-23 16:02:00 -04:00 • committed by GitHub
commit 8dd9a819bb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 14 additions and 8 deletions

View file

@ -659,7 +659,7 @@ func TestCancellation(t *testing.T) {
return nil, nil
}
_, cs := basicConnection(t, func(s *Server) {
s.AddTool(&Tool{Name: "slow"}, slowRequest)
s.AddTool(&Tool{Name: "slow", InputSchema: &jsonschema.Schema{}}, slowRequest)
})
defer cs.Close()

View file

@ -12,7 +12,6 @@ import (
"encoding/json"
"fmt"
"iter"
"log"
"maps"
"net/url"
"path/filepath"
@ -132,13 +131,18 @@ func (s *Server) RemovePrompts(names ...string) {
}
// AddTool adds a [Tool] to the server, or replaces one with the same name.
// The tool's input schema must be non-nil.
// The Tool argument must not be modified after this call.
//
// The tool's input schema must be non-nil. For a tool that takes no input,
// or one where any input is valid, set [Tool.InputSchema] to the empty schema,
// &jsonschema.Schema{}.
func (s *Server) AddTool(t *Tool, h ToolHandler) {
// TODO(jba): This is a breaking behavior change. Add before v0.2.0?
if t.InputSchema == nil {
log.Printf("mcp: tool %q has a nil input schema. This will panic in a future release.", t.Name)
// panic(fmt.Sprintf("adding tool %q: nil input schema", t.Name))
// This prevents the tool author from forgetting to write a schema where
// one should be provided. If we papered over this by supplying the empty
// schema, then every input would be validated and the problem wouldn't be
// discovered until runtime, when the LLM sent bad data.
panic(fmt.Sprintf("adding tool %q: nil input schema", t.Name))
}
if err := addToolErr(s, t, h); err != nil {
panic(err)

View file

@ -11,6 +11,7 @@ import (
"testing"
"github.com/google/go-cmp/cmp"
"github.com/modelcontextprotocol/go-sdk/jsonschema"
)
type testItem struct {
@ -230,6 +231,7 @@ func TestServerPaginateVariousPageSizes(t *testing.T) {
}
func TestServerCapabilities(t *testing.T) {
tool := &Tool{Name: "t", InputSchema: &jsonschema.Schema{}}
testCases := []struct {
name string
configureServer func(s *Server)
@ -299,7 +301,7 @@ func TestServerCapabilities(t *testing.T) {
{
name: "With tools",
configureServer: func(s *Server) {
s.AddTool(&Tool{Name: "t"}, nil)
s.AddTool(tool, nil)
},
wantCapabilities: &serverCapabilities{
Completions: &completionCapabilities{},
@ -313,7 +315,7 @@ func TestServerCapabilities(t *testing.T) {
s.AddPrompt(&Prompt{Name: "p"}, nil)
s.AddResource(&Resource{URI: "file:///r"}, nil)
s.AddResourceTemplate(&ResourceTemplate{URITemplate: "file:///rt"}, nil)
s.AddTool(&Tool{Name: "t"}, nil)
s.AddTool(tool, nil)
},
serverOpts: ServerOptions{
SubscribeHandler: func(ctx context.Context, sp *SubscribeParams) error {