Skip to content

Commit 5dd0975

Browse files
Working
1 parent a4656ac commit 5dd0975

File tree

5 files changed

+180
-17
lines changed

5 files changed

+180
-17
lines changed

cmd/main.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,22 @@ package main
22

33
import "github.com/metoro-io/mcp-golang"
44

5-
type HelloType struct {
6-
Hello string `json:"hello" mcp:"description:'This is hello, you need to pass it'"`
5+
type Content struct {
6+
Title string `json:"hello" jsonschema:"description=The title to submit"`
7+
Description *string `json:"world,omitempty" jsonschema:"description=The description to submit"`
78
}
89
type MyFunctionsArguments struct {
9-
Foo string `json:"foo" mcp:"description:'This is foo, you need to pass it'"`
10-
Bar HelloType `json:"bar" mcp:"description:'This is bar, you need to pass it'"`
10+
Submitter string `json:"foo" jsonschema:"description=The name of the person using the tool"`
11+
Content Content `json:"bar" jsonschema:"description=The content of the message"`
1112
}
1213

1314
func main() {
1415
done := make(chan struct{})
1516

1617
s := mcp.NewServer(mcp.NewStdioServerTransport())
17-
s.Tool("test", "Test tool's description", func(arguments MyFunctionsArguments) (mcp.ToolResponse, error) {
18-
h := arguments.Bar.Hello
18+
s.Tool("hello", "Say hello to a person", func(arguments MyFunctionsArguments) (mcp.ToolResponse, error) {
1919
// ... handle the tool logic
20-
return mcp.ToolResponse{Result: h}, nil
20+
return mcp.ToolResponse{Result: "Submitted " + arguments.Content.Title}, nil
2121
})
2222

2323
//(*s.Tools["test"]).Handler(mcp.CallToolRequestParamsArguments{

go.mod

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,16 @@ go 1.23.4
44

55
require (
66
github.com/google/uuid v1.6.0
7+
github.com/invopop/jsonschema v0.12.0
78
github.com/stretchr/testify v1.9.0
89
)
910

1011
require (
12+
github.com/bahlo/generic-list-go v0.2.0 // indirect
13+
github.com/buger/jsonparser v1.1.1 // indirect
1114
github.com/davecgh/go-spew v1.1.1 // indirect
15+
github.com/mailru/easyjson v0.7.7 // indirect
1216
github.com/pmezard/go-difflib v1.0.0 // indirect
17+
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
1318
gopkg.in/yaml.v3 v3.0.1 // indirect
1419
)

go.sum

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
1+
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
2+
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
3+
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
4+
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
15
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
26
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
37
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
48
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
9+
github.com/invopop/jsonschema v0.12.0 h1:6ovsNSuvn9wEQVOyc72aycBMVQFKz7cPdMJn10CvzRI=
10+
github.com/invopop/jsonschema v0.12.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
11+
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
12+
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
13+
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
514
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
615
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
716
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
817
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
18+
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
19+
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
920
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
1021
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
1122
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

server.go

Lines changed: 156 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package mcp
33
import (
44
"encoding/json"
55
"fmt"
6+
"github.com/invopop/jsonschema"
67
"reflect"
78
"regexp"
89
"strconv"
@@ -20,17 +21,16 @@ import (
2021
type Server struct {
2122
transport Transport
2223
Tools map[string]*ToolType
23-
serverCapabilities ServerCapabilities
2424
serverInstructions *string
2525
serverName string
2626
serverVersion string
2727
}
2828

2929
type ToolType struct {
30-
Name string
31-
Description string
32-
Handler func(CallToolRequestParamsArguments) (ToolResponse, error)
33-
Arguments interface{}
30+
Name string
31+
Description string
32+
Handler func(CallToolRequestParamsArguments) (ToolResponse, error)
33+
ToolInputSchema *jsonschema.Schema
3434
}
3535

3636
type ToolResponse struct {
@@ -62,6 +62,31 @@ func (s *Server) Tool(name string, description string, handler any) error {
6262

6363
argumentType := handlerType.In(0)
6464

65+
reflector := jsonschema.Reflector{
66+
BaseSchemaID: "",
67+
Anonymous: true,
68+
AssignAnchor: false,
69+
AllowAdditionalProperties: true,
70+
RequiredFromJSONSchemaTags: false,
71+
DoNotReference: true,
72+
ExpandedStruct: true,
73+
FieldNameTag: "",
74+
IgnoredTypes: nil,
75+
Lookup: nil,
76+
Mapper: nil,
77+
Namer: nil,
78+
KeyNamer: nil,
79+
AdditionalFields: nil,
80+
CommentMap: nil,
81+
}
82+
83+
inputSchema := reflector.ReflectFromType(argumentType)
84+
//marshalJSON, err := inputSchema.MarshalJSON()
85+
//if err != nil {
86+
// return err
87+
//}
88+
//println(string(marshalJSON))
89+
6590
wrappedHandler := func(arguments CallToolRequestParamsArguments) (ToolResponse, error) {
6691
// We're going to json serialize the arguments and unmarshal them into the correct type
6792
jsonArgs, err := json.Marshal(arguments)
@@ -97,21 +122,95 @@ func (s *Server) Tool(name string, description string, handler any) error {
97122
}
98123

99124
s.Tools[name] = &ToolType{
100-
Name: name,
101-
Description: description,
102-
Handler: wrappedHandler,
125+
Name: name,
126+
Description: description,
127+
Handler: wrappedHandler,
128+
ToolInputSchema: inputSchema,
103129
}
104130

105131
return nil
106132
}
107133

134+
//func getToolInputSchema(argumentType reflect.Type) (ToolInputSchema, error) {
135+
// var schema ToolInputSchema
136+
// switch argumentType.Kind() {
137+
// case reflect.Ptr:
138+
// argumentType = dereferenceReflectType(argumentType)
139+
// return getToolInputSchema(argumentType)
140+
// case reflect.Array, reflect.Slice:
141+
// // We need to get the type of the elements
142+
// elementType := argumentType.Elem()
143+
//
144+
// case reflect.Struct:
145+
// // If it's a struct then we need to get the schema for each field
146+
// schema.Required = []string{}
147+
// m := make(map[string]interface{})
148+
// for i := 0; i < argumentType.NumField(); i++ {
149+
// field := argumentType.Field(i)
150+
// // If it's not a pointer then add it to the required fields
151+
// if field.Type.Kind() != reflect.Ptr {
152+
// schema.Required = append(schema.Required, field.Name)
153+
// }
154+
// // Dereference the type
155+
// t := dereferenceReflectType(field.Type)
156+
// fieldSchema, err := getToolInputSchema(t)
157+
// if err != nil {
158+
// return ToolInputSchema{}, err
159+
// }
160+
// m[field.Name] = fieldSchema.Properties
161+
// }
162+
// schema.Properties = m
163+
// default:
164+
// if !isStandardJSONSchemaType(argumentType) {
165+
// return ToolInputSchema{}, fmt.Errorf("unknown type: %s", argumentType.String())
166+
// }
167+
// // If it's not a struct or a pointer then it's a standard JSON schema type
168+
// t, err := convertGoTypeToJSONSchemaType(argumentType)
169+
// if err != nil {
170+
// return ToolInputSchema{}, err
171+
// }
172+
// schema.Type = t
173+
// }
174+
// return schema, nil
175+
//}
176+
//
177+
//func convertGoTypeToJSONSchemaType(argumentType reflect.Type) (string, error) {
178+
// switch argumentType.Kind() {
179+
// case reflect.Array, reflect.Slice:
180+
// return "array", nil
181+
// case reflect.Map, reflect.Struct:
182+
// return "object", nil
183+
// case reflect.String:
184+
// return "string", nil
185+
// case reflect.Bool:
186+
// return "boolean", nil
187+
// case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
188+
// return "integer", nil
189+
// case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
190+
// return "integer", nil
191+
// case reflect.Float32, reflect.Float64:
192+
// return "number", nil
193+
// default:
194+
// return "", fmt.Errorf("unknown type: %s", argumentType.String())
195+
// }
196+
//}
197+
//
198+
//func isStandardJSONSchemaType(t reflect.Type) bool {
199+
// switch t.String() {
200+
// case "string", "bool", "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "float32", "float64", "array", "slice", "map", "ptr":
201+
// return true
202+
// default:
203+
// return false
204+
// }
205+
//}
206+
108207
func (s *Server) Serve() error {
109208
protocol := NewProtocol(nil)
110209

111210
protocol.SetRequestHandler("initialize", func(req JSONRPCRequest, _ RequestHandlerExtra) (interface{}, error) {
112211
return InitializeResult{
113212
Meta: nil,
114-
Capabilities: s.serverCapabilities,
213+
Capabilities: s.generateCapabilities(),
115214
Instructions: s.serverInstructions,
116215
ProtocolVersion: "2024-11-05",
117216
ServerInfo: Implementation{
@@ -121,9 +220,57 @@ func (s *Server) Serve() error {
121220
}, nil
122221
})
123222

223+
// Definition for a tool the client can call.
224+
type ToolRetType struct {
225+
// A human-readable description of the tool.
226+
Description *string `json:"description,omitempty" yaml:"description,omitempty" mapstructure:"description,omitempty"`
227+
228+
// A JSON Schema object defining the expected parameters for the tool.
229+
InputSchema interface{} `json:"inputSchema" yaml:"inputSchema" mapstructure:"inputSchema"`
230+
231+
// The name of the tool.
232+
Name string `json:"name" yaml:"name" mapstructure:"name"`
233+
}
234+
235+
protocol.SetRequestHandler("tools/list", func(req JSONRPCRequest, _ RequestHandlerExtra) (interface{}, error) {
236+
m := map[string]interface{}{
237+
"tools": func() []ToolRetType {
238+
var tools []ToolRetType
239+
for _, tool := range s.Tools {
240+
tools = append(tools, ToolRetType{
241+
Name: tool.Name,
242+
Description: &tool.Description,
243+
InputSchema: tool.ToolInputSchema,
244+
})
245+
}
246+
return tools
247+
}(),
248+
}
249+
marshalled, err := json.Marshal(m)
250+
if err != nil {
251+
return nil, err
252+
}
253+
println(string(marshalled))
254+
return m, nil
255+
})
256+
124257
return protocol.Connect(s.transport)
125258
}
126259

260+
func (s *Server) generateCapabilities() ServerCapabilities {
261+
f := false
262+
return ServerCapabilities{
263+
Tools: func() *ServerCapabilitiesTools {
264+
if s.Tools == nil {
265+
return nil
266+
}
267+
return &ServerCapabilitiesTools{
268+
ListChanged: &f,
269+
}
270+
}(),
271+
}
272+
}
273+
127274
func validateHandler(handler any) error {
128275
handlerValue := reflect.ValueOf(handler)
129276
handlerType := handlerValue.Type()

types.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)