Skip to content
Closed
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
35 changes: 31 additions & 4 deletions examples/hello/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ package main

import (
"context"
"encoding/json"
"flag"
"fmt"
"log"
"net/http"
"net/url"
"os"

"github.com/modelcontextprotocol/go-sdk/jsonschema"
"github.com/modelcontextprotocol/go-sdk/mcp"
)

Expand All @@ -22,14 +24,39 @@ type HiArgs struct {
Name string `json:"name"`
}

func SayHi(ctx context.Context, ss *mcp.ServerSession, params *mcp.CallToolParamsFor[HiArgs]) (*mcp.CallToolResultFor[struct{}], error) {
return &mcp.CallToolResultFor[struct{}]{
func (h *HiArgs) Schema() (*jsonschema.Schema, error) {
return jsonschema.For[HiArgs]()
}

func (h *HiArgs) SetParams(raw json.RawMessage) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When would this be any different? In other words, can we drop the interface method and just do this automatically?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking that some users might want to control the parsing more specifically, so the pure generic version was not flexible enough, it made the assumption that you would always parse the args into the type in this way - and that may not always be the case.

return json.Unmarshal(raw, h)
}

type HiResult struct {
Message string
}

func (h *HiResult) Result() (*mcp.CallToolResult, error) {
return &mcp.CallToolResult{
Content: []mcp.Content{
&mcp.TextContent{Text: "Hi " + params.Arguments.Name},
&mcp.TextContent{Text: h.Message},
},
}, nil
}

func SayHi(ctx context.Context, ss *mcp.ServerSession, params *mcp.CallToolParamsFor[json.RawMessage]) (*HiResult, error) {
var args HiArgs
if params.Arguments != nil {
if err := args.SetParams(params.Arguments); err != nil {
return nil, err
}
}

return &HiResult{
Message: "Hi " + args.Name,
}, nil
}

func PromptHi(ctx context.Context, ss *mcp.ServerSession, params *mcp.GetPromptParams) (*mcp.GetPromptResult, error) {
return &mcp.GetPromptResult{
Description: "Code review prompt",
Expand All @@ -43,7 +70,7 @@ func main() {
flag.Parse()

server := mcp.NewServer("greeter", "v0.0.1", nil)
server.AddTools(mcp.NewServerTool("greet", "say hi", SayHi, mcp.Input(
server.AddTools(mcp.NewServerTool[*HiArgs, *HiResult]("greet", "say hi", SayHi, mcp.Input(
mcp.Property("name", mcp.Description("the name to say hi to")),
)))
server.AddPrompts(&mcp.ServerPrompt{
Expand Down
45 changes: 37 additions & 8 deletions examples/sse/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ package main

import (
"context"
"encoding/json"
"flag"
"log"
"net/http"

"github.com/modelcontextprotocol/go-sdk/jsonschema"
"github.com/modelcontextprotocol/go-sdk/mcp"
)

Expand All @@ -19,12 +21,39 @@ type SayHiParams struct {
Name string `json:"name"`
}

func SayHi(ctx context.Context, cc *mcp.ServerSession, params *mcp.CallToolParamsFor[SayHiParams]) (*mcp.CallToolResultFor[any], error) {
return &mcp.CallToolResultFor[any]{
Content: []mcp.Content{
&mcp.TextContent{Text: "Hi " + params.Arguments.Name},
},
}, nil
func (s *SayHiParams) Schema() (*jsonschema.Schema, error) {
return jsonschema.For[SayHiParams]()
}

func (s *SayHiParams) SetParams(raw json.RawMessage) error {
return json.Unmarshal(raw, s)
}

type SayHiResult struct{}

func (s *SayHiResult) Result() (*mcp.CallToolResult, error) {
return &mcp.CallToolResult{}, nil
}

func SayHi(ctx context.Context, cc *mcp.ServerSession, params *mcp.CallToolParamsFor[json.RawMessage]) (*SayHiResult, error) {
var args SayHiParams
if params.Arguments != nil {
if err := args.SetParams(params.Arguments); err != nil {
return nil, err
}
}

result := &SayHiResult{}
toolResult, err := result.Result()
if err != nil {
return nil, err
}

toolResult.Content = []mcp.Content{
&mcp.TextContent{Text: "Hi " + args.Name},
}

return result, nil
}

func main() {
Expand All @@ -35,10 +64,10 @@ func main() {
}

server1 := mcp.NewServer("greeter1", "v0.0.1", nil)
server1.AddTools(mcp.NewServerTool("greet1", "say hi", SayHi))
server1.AddTools(mcp.NewServerTool[*SayHiParams, *SayHiResult]("greet1", "say hi", SayHi))

server2 := mcp.NewServer("greeter2", "v0.0.1", nil)
server2.AddTools(mcp.NewServerTool("greet2", "say hello", SayHi))
server2.AddTools(mcp.NewServerTool[*SayHiParams, *SayHiResult]("greet2", "say hello", SayHi))

log.Printf("MCP servers serving at %s\n", *httpAddr)
handler := mcp.NewSSEHandler(func(request *http.Request) *mcp.Server {
Expand Down
41 changes: 34 additions & 7 deletions internal/readme/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,55 @@ package main

import (
"context"
"encoding/json"
"log"

"github.com/modelcontextprotocol/go-sdk/jsonschema"
"github.com/modelcontextprotocol/go-sdk/mcp"
)

type HiParams struct {
Name string `json:"name"`
}

func SayHi(ctx context.Context, cc *mcp.ServerSession, params *mcp.CallToolParamsFor[HiParams]) (*mcp.CallToolResultFor[any], error) {
return &mcp.CallToolResultFor[any]{
Content: []mcp.Content{&mcp.TextContent{Text: "Hi " + params.Name}},
}, nil
func (p *HiParams) Schema() (*jsonschema.Schema, error) {
return jsonschema.For[HiParams]()
}

func (p *HiParams) SetParams(raw json.RawMessage) error {
return json.Unmarshal(raw, p)
}

type HiResult struct{}

func (r *HiResult) Result() (*mcp.CallToolResult, error) {
return &mcp.CallToolResult{}, nil
}

func SayHi(ctx context.Context, cc *mcp.ServerSession, params *mcp.CallToolParamsFor[json.RawMessage]) (*HiResult, error) {
var args HiParams
if params.Arguments != nil {
if err := args.SetParams(params.Arguments); err != nil {
return nil, err
}
}

result := &HiResult{}
toolResult, err := result.Result()
if err != nil {
return nil, err
}

toolResult.Content = []mcp.Content{&mcp.TextContent{Text: "Hi " + args.Name}}

return result, nil
}

func main() {
// Create a server with a single tool.
server := mcp.NewServer("greeter", "v1.0.0", nil)
server.AddTools(
mcp.NewServerTool("greet", "say hi", SayHi, mcp.Input(
mcp.Property("name", mcp.Description("the name of the person to greet")),
)),
mcp.NewServerTool[*HiParams, *HiResult]("greet", "say hi", SayHi),
)
// Run the server over stdin/stdout, until the client disconnects
if err := server.Run(context.Background(), mcp.NewStdioTransport()); err != nil {
Expand Down
46 changes: 43 additions & 3 deletions mcp/client_list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package mcp_test

import (
"context"
"encoding/json"
"iter"
"testing"

Expand All @@ -15,16 +16,55 @@ import (
"github.com/modelcontextprotocol/go-sdk/mcp"
)

type testSayHiParams struct {
Name string `json:"name"`
}

func (p *testSayHiParams) Schema() (*jsonschema.Schema, error) {
return jsonschema.For[testSayHiParams]()
}

func (p *testSayHiParams) SetParams(raw json.RawMessage) error {
return json.Unmarshal(raw, p)
}

type testSayHiResult struct{}

func (r *testSayHiResult) Result() (*mcp.CallToolResult, error) {
return &mcp.CallToolResult{}, nil
}

func testSayHi(ctx context.Context, cc *mcp.ServerSession, params *mcp.CallToolParamsFor[json.RawMessage]) (*testSayHiResult, error) {
var args testSayHiParams
if params.Arguments != nil {
if err := args.SetParams(params.Arguments); err != nil {
return nil, err
}
}

result := &testSayHiResult{}
toolResult, err := result.Result()
if err != nil {
return nil, err
}

toolResult.Content = []mcp.Content{
&mcp.TextContent{Text: "Hi " + args.Name},
}

return result, nil
}

func TestList(t *testing.T) {
ctx := context.Background()
clientSession, serverSession, server := createSessions(ctx)
defer clientSession.Close()
defer serverSession.Close()

t.Run("tools", func(t *testing.T) {
toolA := mcp.NewServerTool("apple", "apple tool", SayHi)
toolB := mcp.NewServerTool("banana", "banana tool", SayHi)
toolC := mcp.NewServerTool("cherry", "cherry tool", SayHi)
toolA := mcp.NewServerTool[*testSayHiParams, *testSayHiResult]("apple", "apple tool", testSayHi)
toolB := mcp.NewServerTool[*testSayHiParams, *testSayHiResult]("banana", "banana tool", testSayHi)
toolC := mcp.NewServerTool[*testSayHiParams, *testSayHiResult]("cherry", "cherry tool", testSayHi)
tools := []*mcp.ServerTool{toolA, toolB, toolC}
wantTools := []*mcp.Tool{toolA.Tool, toolB.Tool, toolC.Tool}
server.AddTools(tools...)
Expand Down
2 changes: 1 addition & 1 deletion mcp/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func runServer() {
ctx := context.Background()

server := mcp.NewServer("greeter", "v0.0.1", nil)
server.AddTools(mcp.NewServerTool("greet", "say hi", SayHi))
server.AddTools(mcp.NewServerTool[*SayHiParams, *SayHiResult]("greet", "say hi", SayHi))

if err := server.Run(ctx, mcp.NewStdioTransport()); err != nil {
log.Fatal(err)
Expand Down
44 changes: 35 additions & 9 deletions mcp/features_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package mcp

import (
"context"
"encoding/json"
"slices"
"testing"

Expand All @@ -18,18 +19,43 @@ type SayHiParams struct {
Name string `json:"name"`
}

func SayHi(ctx context.Context, cc *ServerSession, params *CallToolParamsFor[SayHiParams]) (*CallToolResultFor[any], error) {
return &CallToolResultFor[any]{
func (p *SayHiParams) Schema() (*jsonschema.Schema, error) {
return jsonschema.For[SayHiParams]()
}

func (p *SayHiParams) SetParams(raw json.RawMessage) error {
return json.Unmarshal(raw, p)
}

type SayHiResult struct {
Message string
}

func (r *SayHiResult) Result() (*CallToolResult, error) {
return &CallToolResult{
Content: []Content{
&TextContent{Text: "Hi " + params.Name},
&TextContent{Text: r.Message},
},
}, nil
}

func SayHi(ctx context.Context, cc *ServerSession, params *CallToolParamsFor[json.RawMessage]) (*SayHiResult, error) {
var args SayHiParams
if params.Arguments != nil {
if err := args.SetParams(params.Arguments); err != nil {
return nil, err
}
}

return &SayHiResult{
Message: "Hi " + args.Name,
}, nil
}

func TestFeatureSetOrder(t *testing.T) {
toolA := NewServerTool("apple", "apple tool", SayHi).Tool
toolB := NewServerTool("banana", "banana tool", SayHi).Tool
toolC := NewServerTool("cherry", "cherry tool", SayHi).Tool
toolA := NewServerTool[*SayHiParams, *SayHiResult]("apple", "apple tool", SayHi).Tool
toolB := NewServerTool[*SayHiParams, *SayHiResult]("banana", "banana tool", SayHi).Tool
toolC := NewServerTool[*SayHiParams, *SayHiResult]("cherry", "cherry tool", SayHi).Tool

testCases := []struct {
tools []*Tool
Expand All @@ -52,9 +78,9 @@ func TestFeatureSetOrder(t *testing.T) {
}

func TestFeatureSetAbove(t *testing.T) {
toolA := NewServerTool("apple", "apple tool", SayHi).Tool
toolB := NewServerTool("banana", "banana tool", SayHi).Tool
toolC := NewServerTool("cherry", "cherry tool", SayHi).Tool
toolA := NewServerTool[*SayHiParams, *SayHiResult]("apple", "apple tool", SayHi).Tool
toolB := NewServerTool[*SayHiParams, *SayHiResult]("banana", "banana tool", SayHi).Tool
toolC := NewServerTool[*SayHiParams, *SayHiResult]("cherry", "cherry tool", SayHi).Tool

testCases := []struct {
tools []*Tool
Expand Down
Loading