You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
BREAKING CHANGE
Change the API for adding tools, prompts, resources and resource
templates to a server.
The ServerXXX types are gone along with the plural Add methods
(AddTools, AddPrompts etc.)
Instead there are single Add methods (AddTool, AddPrompt).
In addition, instead of NewServerTool, there is AddTool[In, Out].
Fixes#73.
For convenience, we provide `Server.Run` to handle the common case of running a session until the client disconnects:
@@ -603,14 +602,14 @@ type ClientOptions struct {
603
602
604
603
### Tools
605
604
606
-
A `Tool` is a logical MCP tool, generated from the MCP spec, and a `ServerTool` is a tool bound to a tool handler.
605
+
A `Tool` is a logical MCP tool, generated from the MCP spec.
607
606
608
-
A tool handler accepts `CallToolParams` and returns a `CallToolResult`. However, since we want to bind tools to Go input types, it is convenient in associated APIs to make `CallToolParams` generic, with a type parameter `TArgs` for the tool argument type. This allows tool APIs to manage the marshalling and unmarshalling of tool inputs for their caller. The bound `ServerTool` type expects a `json.RawMessage` for its tool arguments, but the `NewServerTool` constructor described below provides a mechanism to bind a typed handler.
607
+
A tool handler accepts `CallToolParams` and returns a `CallToolResult`. However, since we want to bind tools to Go input types, it is convenient in associated APIs to have a generic version of `CallToolParams`, with a type parameter `In` for the tool argument type, as well as a generic version of for `CallToolResult`. This allows tool APIs to manage the marshalling and unmarshalling of tool inputs for their caller.
609
608
610
609
```go
611
-
typeCallToolParams[TArgs any] struct {
610
+
typeCallToolParamsFor[In any] struct {
612
611
Meta Meta `json:"_meta,omitempty"`
613
-
Arguments TArgs `json:"arguments,omitempty"`
612
+
Arguments In`json:"arguments,omitempty"`
614
613
Name string`json:"name"`
615
614
}
616
615
@@ -621,23 +620,31 @@ type Tool struct {
621
620
Name string`json:"name"`
622
621
}
623
622
624
-
type ToolHandler[TArgs] func(context.Context, *ServerSession, *CallToolParams[TArgs]) (*CallToolResult, error)
623
+
type ToolHandlerFor[In, Out any] func(context.Context, *ServerSession, *CallToolParamsFor[In]) (*CallToolResultFor[Out], error)
624
+
type ToolHandler = ToolHandlerFor[map[string]any, any]
625
+
```
625
626
626
-
type ServerTool struct {
627
-
Tool Tool
628
-
Handler ToolHandler[json.RawMessage]
629
-
}
627
+
Add tools to a server with the `AddTool` method or function. The function is generic and infers schemas from the handler
628
+
arguments:
629
+
630
+
```go
631
+
func (s *Server) AddTool(t *Tool, h ToolHandler)
632
+
func AddTool[In, Out any](s *Server, t *Tool, h ToolHandlerFor[In, Out])
@@ -650,53 +657,30 @@ A tool's input schema, expressed as a [JSON Schema](https://json-schema.org), pr
650
657
651
658
Both of these have their advantages and disadvantages. Reflection is nice, because it allows you to bind directly to a Go API, and means that the JSON schema of your API is compatible with your Go types by construction. It also means that concerns like parsing and validation can be handled automatically. However, it can become cumbersome to express the full breadth of JSON schema using Go types or struct tags, and sometimes you want to express things that aren’t naturally modeled by Go types, like unions. Explicit schemas are simple and readable, and give the caller full control over their tool definition, but involve significant boilerplate.
652
659
653
-
We have found that a hybrid model works well, where the _initial_ schema is derived using reflection, but any customization on top of that schema is applied using variadic options. We achieve this using a `NewServerTool` helper, which generates the schema from the input type, and wraps the handler to provide parsing and validation. The schema (and potentially other features) can be customized using ToolOptions.
654
-
655
-
```go
656
-
// NewServerTool creates a Tool using reflection on the given handler.
We provide both ways. The `jsonschema.For[T]` function will infer a schema, and it is called by the `AddTool` generic function.
661
+
Users can also call it themselves, or build a schema directly as a struct literal. They can still use the `AddTool` function to
662
+
create a typed handler, since `AddTool` doesn't touch schemas that are already present.
658
663
659
-
type ToolOption interface { /* ... */ }
660
-
```
661
664
662
-
`NewServerTool` determines the input schema for a Tool from the `TArgs` type. Each struct field that would be marshaled by `encoding/json.Marshal` becomes a property of the schema. The property is required unless the field's `json` tag specifies "omitempty" or "omitzero" (new in Go 1.24). For example, given this struct:
665
+
If the tool's `InputSchema` is nil, it is inferred from the `In` type parameter. If the `OutputSchema` is nil, it is inferred from the `Out` type parameter (unless `Out` is `any`).
663
666
667
+
For example, given this handler:
664
668
```go
665
-
struct {
666
-
Name string`json:"name"`
667
-
Count int`json:"count,omitempty"`
668
-
Choices []string
669
-
Password []byte`json:"-"`
669
+
type AddParams struct {
670
+
X int`json:"x"`
671
+
Y int`json:"y"`
670
672
}
671
-
```
672
-
673
-
"name" and "Choices" are required, while "count" is optional.
674
-
675
-
As of this writing, the only `ToolOption` is `Input`, which allows customizing the input schema of the tool using schema options. These schema options are recursive, in the sense that they may also be applied to properties.
The most recent JSON Schema spec defines over 40 keywords. Providing them all as options would bloat the API despite the fact that most would be very rarely used. For less common keywords, use the `Schema` option to set the schema explicitly:
@@ -757,25 +734,11 @@ type ResourceHandler func(context.Context, *ServerSession, *ReadResourceParams)
757
734
758
735
The arguments include the `ServerSession` so the handler can observe the client's roots. The handler should return the resource contents in a `ReadResourceResult`, calling either `NewTextResourceContents` or `NewBlobResourceContents`. If the handler omits the URI or MIME type, the server will populate them from the resource.
759
736
760
-
The `ServerResource` and `ServerResourceTemplate` types hold the association between the resource and its handler:
761
-
762
-
```go
763
-
type ServerResource struct {
764
-
Resource Resource
765
-
Handler ResourceHandler
766
-
}
767
-
768
-
type ServerResourceTemplate struct {
769
-
Template ResourceTemplate
770
-
Handler ResourceHandler
771
-
}
772
-
```
773
-
774
-
To add a resource or resource template to a server, users call the `AddResources` and `AddResourceTemplates` methods with one or more `ServerResource`s or `ServerResourceTemplate`s. We also provide methods to remove them.
737
+
To add a resource or resource template to a server, users call the `AddResource` and `AddResourceTemplate` methods. We also provide methods to remove them.
Server sessions also support the spec methods `ListResources` and `ListResourceTemplates`, and the corresponding iterator methods `Resources` and `ResourceTemplates`.
0 commit comments