internal/mcp: rename NewXXX->NewServerXXX
The NewTool and NewPrompt constructors were unidiomatic, because they returned a ServerTool and ServerPrompt, respectively, rather than a Tool and Prompt. Rename to be consistent with the constructed object name. Change-Id: Ife3f16f783368401f792660c4272e1bb5ae61638 Reviewed-on: https://go-review.googlesource.com/c/tools/+/682375 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Jonathan Amsterdam <jba@google.com>
This commit is contained in:
parent
d2f2f87bbe
commit
9453d6bf6c
14 changed files with 76 additions and 74 deletions
|
|
@ -22,9 +22,9 @@ func TestList(t *testing.T) {
|
|||
defer serverSession.Close()
|
||||
|
||||
t.Run("tools", func(t *testing.T) {
|
||||
toolA := mcp.NewTool("apple", "apple tool", SayHi)
|
||||
toolB := mcp.NewTool("banana", "banana tool", SayHi)
|
||||
toolC := mcp.NewTool("cherry", "cherry tool", SayHi)
|
||||
toolA := mcp.NewServerTool("apple", "apple tool", SayHi)
|
||||
toolB := mcp.NewServerTool("banana", "banana tool", SayHi)
|
||||
toolC := mcp.NewServerTool("cherry", "cherry tool", SayHi)
|
||||
tools := []*mcp.ServerTool{toolA, toolB, toolC}
|
||||
wantTools := []*mcp.Tool{toolA.Tool, toolB.Tool, toolC.Tool}
|
||||
server.AddTools(tools...)
|
||||
|
|
@ -88,9 +88,9 @@ func TestList(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("prompts", func(t *testing.T) {
|
||||
promptA := mcp.NewPrompt("apple", "apple prompt", testPromptHandler[struct{}])
|
||||
promptB := mcp.NewPrompt("banana", "banana prompt", testPromptHandler[struct{}])
|
||||
promptC := mcp.NewPrompt("cherry", "cherry prompt", testPromptHandler[struct{}])
|
||||
promptA := mcp.NewServerPrompt("apple", "apple prompt", testPromptHandler[struct{}])
|
||||
promptB := mcp.NewServerPrompt("banana", "banana prompt", testPromptHandler[struct{}])
|
||||
promptC := mcp.NewServerPrompt("cherry", "cherry prompt", testPromptHandler[struct{}])
|
||||
wantPrompts := []*mcp.Prompt{promptA.Prompt, promptB.Prompt, promptC.Prompt}
|
||||
prompts := []*mcp.ServerPrompt{promptA, promptB, promptC}
|
||||
server.AddPrompts(prompts...)
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ func runServer() {
|
|||
ctx := context.Background()
|
||||
|
||||
server := mcp.NewServer("greeter", "v0.0.1", nil)
|
||||
server.AddTools(mcp.NewTool("greet", "say hi", SayHi))
|
||||
server.AddTools(mcp.NewServerTool("greet", "say hi", SayHi))
|
||||
|
||||
if err := server.Run(ctx, mcp.NewStdIOTransport()); err != nil {
|
||||
log.Fatal(err)
|
||||
|
|
|
|||
|
|
@ -27,9 +27,9 @@ func SayHi(ctx context.Context, cc *ServerSession, params *CallToolParamsFor[Say
|
|||
}
|
||||
|
||||
func TestFeatureSetOrder(t *testing.T) {
|
||||
toolA := NewTool("apple", "apple tool", SayHi).Tool
|
||||
toolB := NewTool("banana", "banana tool", SayHi).Tool
|
||||
toolC := NewTool("cherry", "cherry tool", SayHi).Tool
|
||||
toolA := NewServerTool("apple", "apple tool", SayHi).Tool
|
||||
toolB := NewServerTool("banana", "banana tool", SayHi).Tool
|
||||
toolC := NewServerTool("cherry", "cherry tool", SayHi).Tool
|
||||
|
||||
testCases := []struct {
|
||||
tools []*Tool
|
||||
|
|
@ -52,9 +52,9 @@ func TestFeatureSetOrder(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestFeatureSetAbove(t *testing.T) {
|
||||
toolA := NewTool("apple", "apple tool", SayHi).Tool
|
||||
toolB := NewTool("banana", "banana tool", SayHi).Tool
|
||||
toolC := NewTool("cherry", "cherry tool", SayHi).Tool
|
||||
toolA := NewServerTool("apple", "apple tool", SayHi).Tool
|
||||
toolB := NewServerTool("banana", "banana tool", SayHi).Tool
|
||||
toolC := NewServerTool("cherry", "cherry tool", SayHi).Tool
|
||||
|
||||
testCases := []struct {
|
||||
tools []*Tool
|
||||
|
|
|
|||
|
|
@ -433,7 +433,7 @@ var (
|
|||
errTestFailure = errors.New("mcp failure")
|
||||
|
||||
tools = map[string]*ServerTool{
|
||||
"greet": NewTool("greet", "say hi", sayHi),
|
||||
"greet": NewServerTool("greet", "say hi", sayHi),
|
||||
"fail": {
|
||||
Tool: &Tool{Name: "fail"},
|
||||
Handler: func(context.Context, *ServerSession, *CallToolParamsFor[map[string]any]) (*CallToolResult, error) {
|
||||
|
|
@ -443,7 +443,7 @@ var (
|
|||
}
|
||||
|
||||
prompts = map[string]*ServerPrompt{
|
||||
"code_review": NewPrompt("code_review", "do a code review",
|
||||
"code_review": NewServerPrompt("code_review", "do a code review",
|
||||
func(_ context.Context, _ *ServerSession, params struct{ Code string }, _ *GetPromptParams) (*GetPromptResult, error) {
|
||||
return &GetPromptResult{
|
||||
Description: "Code review prompt",
|
||||
|
|
@ -452,7 +452,7 @@ var (
|
|||
},
|
||||
}, nil
|
||||
}),
|
||||
"fail": NewPrompt("fail", "", func(_ context.Context, _ *ServerSession, args struct{}, _ *GetPromptParams) (*GetPromptResult, error) {
|
||||
"fail": NewServerPrompt("fail", "", func(_ context.Context, _ *ServerSession, args struct{}, _ *GetPromptParams) (*GetPromptResult, error) {
|
||||
return nil, errTestFailure
|
||||
}),
|
||||
}
|
||||
|
|
@ -556,7 +556,7 @@ func basicConnection(t *testing.T, tools ...*ServerTool) (*ServerSession, *Clien
|
|||
}
|
||||
|
||||
func TestServerClosing(t *testing.T) {
|
||||
cc, cs := basicConnection(t, NewTool("greet", "say hi", sayHi))
|
||||
cc, cs := basicConnection(t, NewServerTool("greet", "say hi", sayHi))
|
||||
defer cs.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@ type ServerPrompt struct {
|
|||
Handler PromptHandler
|
||||
}
|
||||
|
||||
// NewPrompt is a helper that uses reflection to create a prompt for the given handler.
|
||||
// NewServerPrompt is a helper that uses reflection to create a prompt for the
|
||||
// given handler.
|
||||
//
|
||||
// The arguments for the prompt are extracted from the request type for the
|
||||
// handler. The handler request type must be a struct consisting only of fields
|
||||
|
|
@ -32,9 +33,10 @@ type ServerPrompt struct {
|
|||
// definition correspond to the JSON names of the request fields, and any
|
||||
// fields that are not marked "omitempty" are considered required.
|
||||
//
|
||||
// The handler is passed [GetPromptParams] so it can have access to prompt parameters other than name and arguments.
|
||||
// At present, there are no such parameters.
|
||||
func NewPrompt[In any](name, description string, handler func(context.Context, *ServerSession, In, *GetPromptParams) (*GetPromptResult, error), opts ...PromptOption) *ServerPrompt {
|
||||
// The handler is passed [GetPromptParams] so it can have access to prompt
|
||||
// parameters other than name and arguments. At present, there are no such
|
||||
// parameters.
|
||||
func NewServerPrompt[In any](name, description string, handler func(context.Context, *ServerSession, In, *GetPromptParams) (*GetPromptResult, error), opts ...PromptOption) *ServerPrompt {
|
||||
schema, err := jsonschema.For[In]()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
|
|
|||
|
|
@ -12,26 +12,26 @@ import (
|
|||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
)
|
||||
|
||||
// testPromptHandler is used for type inference in TestNewPrompt.
|
||||
// testPromptHandler is used for type inference in TestNewServerPrompt.
|
||||
func testPromptHandler[T any](context.Context, *mcp.ServerSession, T, *mcp.GetPromptParams) (*mcp.GetPromptResult, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func TestNewPrompt(t *testing.T) {
|
||||
func TestNewServerPrompt(t *testing.T) {
|
||||
tests := []struct {
|
||||
prompt *mcp.ServerPrompt
|
||||
want []*mcp.PromptArgument
|
||||
}{
|
||||
{
|
||||
mcp.NewPrompt("empty", "", testPromptHandler[struct{}]),
|
||||
mcp.NewServerPrompt("empty", "", testPromptHandler[struct{}]),
|
||||
nil,
|
||||
},
|
||||
{
|
||||
mcp.NewPrompt("add_arg", "", testPromptHandler[struct{}], mcp.Argument("x")),
|
||||
mcp.NewServerPrompt("add_arg", "", testPromptHandler[struct{}], mcp.Argument("x")),
|
||||
[]*mcp.PromptArgument{{Name: "x"}},
|
||||
},
|
||||
{
|
||||
mcp.NewPrompt("combo", "", testPromptHandler[struct {
|
||||
mcp.NewServerPrompt("combo", "", testPromptHandler[struct {
|
||||
Name string `json:"name"`
|
||||
Country string `json:"country,omitempty"`
|
||||
State string
|
||||
|
|
@ -47,7 +47,7 @@ func TestNewPrompt(t *testing.T) {
|
|||
}
|
||||
for _, test := range tests {
|
||||
if diff := cmp.Diff(test.want, test.prompt.Prompt.Arguments); diff != "" {
|
||||
t.Errorf("NewPrompt(%v) mismatch (-want +got):\n%s", test.prompt.Prompt.Name, diff)
|
||||
t.Errorf("NewServerPrompt(%v) mismatch (-want +got):\n%s", test.prompt.Prompt.Name, diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -116,7 +116,6 @@ func (s *Server) RemovePrompts(names ...string) {
|
|||
// The arguments must not be modified after this call.
|
||||
//
|
||||
// AddTools panics if errors are detected.
|
||||
// Call [AddToolsErr] to obtain an error instead.
|
||||
func (s *Server) AddTools(tools ...*ServerTool) {
|
||||
if err := s.addToolsErr(tools...); err != nil {
|
||||
panic(err)
|
||||
|
|
@ -132,7 +131,7 @@ func (s *Server) addToolsErr(tools ...*ServerTool) error {
|
|||
// Wrap the user's Handlers with rawHandlers that take a json.RawMessage.
|
||||
for _, st := range tools {
|
||||
if st.rawHandler == nil {
|
||||
// This ServerTool was not created with NewTool.
|
||||
// This ServerTool was not created with NewServerTool.
|
||||
if st.Handler == nil {
|
||||
return fmt.Errorf("AddTools: tool %q has no handler", st.Tool.Name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ func ExampleServer() {
|
|||
clientTransport, serverTransport := mcp.NewInMemoryTransports()
|
||||
|
||||
server := mcp.NewServer("greeter", "v0.0.1", nil)
|
||||
server.AddTools(mcp.NewTool("greet", "say hi", SayHi))
|
||||
server.AddTools(mcp.NewServerTool("greet", "say hi", SayHi))
|
||||
|
||||
serverSession, err := server.Connect(ctx, serverTransport)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -71,8 +71,8 @@ func roundTrip[T any](t *testing.T, v T) T {
|
|||
}
|
||||
|
||||
// TODO(jba): this shouldn't be in this file, but tool_test.go doesn't have access to unexported symbols.
|
||||
func TestNewToolValidate(t *testing.T) {
|
||||
// Check that the tool returned from NewTool properly validates its input schema.
|
||||
func TestNewServerToolValidate(t *testing.T) {
|
||||
// Check that the tool returned from NewServerTool properly validates its input schema.
|
||||
|
||||
type req struct {
|
||||
I int
|
||||
|
|
@ -85,7 +85,7 @@ func TestNewToolValidate(t *testing.T) {
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
tool := NewTool("test", "test", dummyHandler)
|
||||
tool := NewServerTool("test", "test", dummyHandler)
|
||||
// Need to add the tool to a server to get resolved schemas.
|
||||
// s := NewServer("", "", nil)
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ func Add(ctx context.Context, cc *mcp.ServerSession, params *mcp.CallToolParamsF
|
|||
|
||||
func ExampleSSEHandler() {
|
||||
server := mcp.NewServer("adder", "v0.0.1", nil)
|
||||
server.AddTools(mcp.NewTool("add", "add two numbers", Add))
|
||||
server.AddTools(mcp.NewServerTool("add", "add two numbers", Add))
|
||||
|
||||
handler := mcp.NewSSEHandler(func(*http.Request) *mcp.Server { return server })
|
||||
httpServer := httptest.NewServer(handler)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ func TestSSEServer(t *testing.T) {
|
|||
t.Run(fmt.Sprintf("closeServerFirst=%t", closeServerFirst), func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
server := NewServer("testServer", "v1.0.0", nil)
|
||||
server.AddTools(NewTool("greet", "say hi", sayHi))
|
||||
server.AddTools(NewServerTool("greet", "say hi", sayHi))
|
||||
|
||||
sseHandler := NewSSEHandler(func(*http.Request) *Server { return server })
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ func TestStreamableTransports(t *testing.T) {
|
|||
|
||||
// 1. Create a server with a simple "greet" tool.
|
||||
server := NewServer("testServer", "v1.0.0", nil)
|
||||
server.AddTools(NewTool("greet", "say hi", sayHi))
|
||||
server.AddTools(NewServerTool("greet", "say hi", sayHi))
|
||||
|
||||
// 2. Start an httptest.Server with the StreamableHTTPHandler.
|
||||
handler := NewStreamableHTTPHandler(func(req *http.Request) *Server { return server }, nil)
|
||||
|
|
@ -290,7 +290,7 @@ func TestStreamableServerTransport(t *testing.T) {
|
|||
// Create a server containing a single tool, which runs the test tool
|
||||
// behavior, if any.
|
||||
server := NewServer("testServer", "v1.0.0", nil)
|
||||
tool := NewTool("tool", "test tool", func(ctx context.Context, ss *ServerSession, params *CallToolParamsFor[any]) (*CallToolResultFor[any], error) {
|
||||
tool := NewServerTool("tool", "test tool", func(ctx context.Context, ss *ServerSession, params *CallToolParamsFor[any]) (*CallToolResultFor[any], error) {
|
||||
if test.tool != nil {
|
||||
test.tool(t, ctx, ss)
|
||||
}
|
||||
|
|
|
|||
18
mcp/tool.go
18
mcp/tool.go
|
|
@ -30,30 +30,30 @@ type rawToolHandler = func(context.Context, *ServerSession, *CallToolParamsFor[j
|
|||
type ServerTool struct {
|
||||
Tool *Tool
|
||||
Handler ToolHandler
|
||||
// Set in NewTool or Server.AddToolsErr.
|
||||
// Set in NewServerTool or Server.AddToolsErr.
|
||||
rawHandler rawToolHandler
|
||||
// Resolved tool schemas. Set in Server.AddToolsErr.
|
||||
inputResolved, outputResolved *jsonschema.Resolved
|
||||
}
|
||||
|
||||
// NewTool is a helper to make a tool using reflection on the given type parameters.
|
||||
// When the tool is called, CallToolParams.Arguments will be of type TReq.
|
||||
// NewServerTool is a helper to make a tool using reflection on the given type parameters.
|
||||
// When the tool is called, CallToolParams.Arguments will be of type In.
|
||||
//
|
||||
// If provided, variadic [ToolOption] values may be used to customize the tool.
|
||||
//
|
||||
// The input schema for the tool is extracted from the request type for the
|
||||
// handler, and used to unmmarshal and validate requests to the handler. This
|
||||
// schema may be customized using the [Input] option.
|
||||
func NewTool[In, Out any](name, description string, handler ToolHandlerFor[In, Out], opts ...ToolOption) *ServerTool {
|
||||
st, err := newToolErr[In, Out](name, description, handler, opts...)
|
||||
func NewServerTool[In, Out any](name, description string, handler ToolHandlerFor[In, Out], opts ...ToolOption) *ServerTool {
|
||||
st, err := newServerToolErr[In, Out](name, description, handler, opts...)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("NewTool(%q): %w", name, err))
|
||||
panic(fmt.Errorf("NewServerTool(%q): %w", name, err))
|
||||
}
|
||||
return st
|
||||
}
|
||||
|
||||
func newToolErr[In, Out any](name, description string, handler ToolHandlerFor[In, Out], opts ...ToolOption) (*ServerTool, error) {
|
||||
// TODO: check that TReq is a struct.
|
||||
func newServerToolErr[In, Out any](name, description string, handler ToolHandlerFor[In, Out], opts ...ToolOption) (*ServerTool, error) {
|
||||
// TODO: check that In is a struct.
|
||||
ischema, err := jsonschema.For[In]()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -106,7 +106,7 @@ func newToolErr[In, Out any](name, description string, handler ToolHandlerFor[In
|
|||
return t, nil
|
||||
}
|
||||
|
||||
// newRawHandler creates a rawToolHandler for tools not created through NewTool.
|
||||
// newRawHandler creates a rawToolHandler for tools not created through NewServerTool.
|
||||
// It unmarshals the arguments into a map[string]any and validates them against the
|
||||
// schema, then calls the ServerTool's handler.
|
||||
func newRawHandler(st *ServerTool) rawToolHandler {
|
||||
|
|
|
|||
|
|
@ -15,18 +15,18 @@ import (
|
|||
"github.com/modelcontextprotocol/go-sdk/jsonschema"
|
||||
)
|
||||
|
||||
// testToolHandler is used for type inference in TestNewTool.
|
||||
// testToolHandler is used for type inference in TestNewServerTool.
|
||||
func testToolHandler[T any](context.Context, *ServerSession, *CallToolParamsFor[T]) (*CallToolResultFor[any], error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func TestNewTool(t *testing.T) {
|
||||
func TestNewServerTool(t *testing.T) {
|
||||
tests := []struct {
|
||||
tool *ServerTool
|
||||
want *jsonschema.Schema
|
||||
}{
|
||||
{
|
||||
NewTool("basic", "", testToolHandler[struct {
|
||||
NewServerTool("basic", "", testToolHandler[struct {
|
||||
Name string `json:"name"`
|
||||
}]),
|
||||
&jsonschema.Schema{
|
||||
|
|
@ -39,7 +39,7 @@ func TestNewTool(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
NewTool("enum", "", testToolHandler[struct{ Name string }], Input(
|
||||
NewServerTool("enum", "", testToolHandler[struct{ Name string }], Input(
|
||||
Property("Name", Enum("x", "y", "z")),
|
||||
)),
|
||||
&jsonschema.Schema{
|
||||
|
|
@ -52,7 +52,7 @@ func TestNewTool(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
NewTool("required", "", testToolHandler[struct {
|
||||
NewServerTool("required", "", testToolHandler[struct {
|
||||
Name string `json:"name"`
|
||||
Language string `json:"language"`
|
||||
X int `json:"x,omitempty"`
|
||||
|
|
@ -72,7 +72,7 @@ func TestNewTool(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
NewTool("set_schema", "", testToolHandler[struct {
|
||||
NewServerTool("set_schema", "", testToolHandler[struct {
|
||||
X int `json:"x,omitempty"`
|
||||
Y int `json:"y,omitempty"`
|
||||
}], Input(
|
||||
|
|
@ -85,7 +85,7 @@ func TestNewTool(t *testing.T) {
|
|||
}
|
||||
for _, test := range tests {
|
||||
if diff := cmp.Diff(test.want, test.tool.Tool.InputSchema, cmpopts.IgnoreUnexported(jsonschema.Schema{})); diff != "" {
|
||||
t.Errorf("NewTool(%v) mismatch (-want +got):\n%s", test.tool.Tool.Name, diff)
|
||||
t.Errorf("NewServerTool(%v) mismatch (-want +got):\n%s", test.tool.Tool.Name, diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue