all: add examples of customizing tool schemas, and clarify documentation (#421)
Add example/server/toolschemas, which demonstrates various ways to customize tool schemas, and illustrates the differences between ToolHandler and ToolHandlerFor. Fixes #385 Fixes #386 For #368
This commit is contained in:
parent
5ccd97fafc
commit
40b6bd30f1
7 changed files with 309 additions and 100 deletions
105
README.md
105
README.md
|
|
@ -19,18 +19,6 @@ software development kit (SDK) for the Model Context Protocol (MCP).
|
||||||
> changes. We aim to tag v1.0.0 in September, 2025. See
|
> changes. We aim to tag v1.0.0 in September, 2025. See
|
||||||
> https://github.com/modelcontextprotocol/go-sdk/issues/328 for details.
|
> https://github.com/modelcontextprotocol/go-sdk/issues/328 for details.
|
||||||
|
|
||||||
## Design
|
|
||||||
|
|
||||||
The design doc for this SDK is at [design.md](./design/design.md), which was
|
|
||||||
initially reviewed at
|
|
||||||
[modelcontextprotocol/discussions/364](https://github.com/orgs/modelcontextprotocol/discussions/364).
|
|
||||||
|
|
||||||
Further design discussion should occur in
|
|
||||||
[issues](https://github.com/modelcontextprotocol/go-sdk/issues) (for concrete
|
|
||||||
proposals) or
|
|
||||||
[discussions](https://github.com/modelcontextprotocol/go-sdk/discussions) for
|
|
||||||
open-ended discussion. See [CONTRIBUTING.md](/CONTRIBUTING.md) for details.
|
|
||||||
|
|
||||||
## Package documentation
|
## Package documentation
|
||||||
|
|
||||||
The SDK consists of two importable packages:
|
The SDK consists of two importable packages:
|
||||||
|
|
@ -42,12 +30,53 @@ The SDK consists of two importable packages:
|
||||||
- The
|
- The
|
||||||
[`github.com/modelcontextprotocol/go-sdk/jsonrpc`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/jsonrpc) package is for users implementing
|
[`github.com/modelcontextprotocol/go-sdk/jsonrpc`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/jsonrpc) package is for users implementing
|
||||||
their own transports.
|
their own transports.
|
||||||
|
- The
|
||||||
|
[`github.com/modelcontextprotocol/go-sdk/auth`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth)
|
||||||
|
package provides some primitives for supporting oauth.
|
||||||
|
|
||||||
## Example
|
## Getting started
|
||||||
|
|
||||||
In this example, an MCP client communicates with an MCP server running in a
|
To get started creating an MCP server, create an `mcp.Server` instance, add
|
||||||
sidecar process:
|
features to it, and then run it over an `mcp.Transport`. For example, this
|
||||||
|
server adds a single simple tool, and then connects it to clients over
|
||||||
|
stdin/stdout:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Input struct {
|
||||||
|
Name string `json:"name" jsonschema:"the name of the person to greet"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Output struct {
|
||||||
|
Greeting string `json:"greeting" jsonschema:"the greeting to tell to the user"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func SayHi(ctx context.Context, req *mcp.CallToolRequest, input Input) (*mcp.CallToolResult, Output, error) {
|
||||||
|
return nil, Output{Greeting: "Hi " + input.Name}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Create a server with a single tool.
|
||||||
|
server := mcp.NewServer(&mcp.Implementation{Name: "greeter", Version: "v1.0.0"}, nil)
|
||||||
|
mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "say hi"}, SayHi)
|
||||||
|
// Run the server over stdin/stdout, until the client disconnects
|
||||||
|
if err := server.Run(context.Background(), &mcp.StdioTransport{}); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To communicate with that server, we can similarly create an `mcp.Client` and
|
||||||
|
connect it to the corresponding server, by running the server command and
|
||||||
|
communicating over its stdin/stdout:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
@ -92,42 +121,20 @@ func main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Here's an example of the corresponding server component, which communicates
|
The [`examples/`](/examples/) directory contains more example clients and
|
||||||
with its client over stdin/stdout:
|
servers.
|
||||||
|
|
||||||
```go
|
## Design
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
The design doc for this SDK is at [design.md](./design/design.md), which was
|
||||||
"context"
|
initially reviewed at
|
||||||
"log"
|
[modelcontextprotocol/discussions/364](https://github.com/orgs/modelcontextprotocol/discussions/364).
|
||||||
|
|
||||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
Further design discussion should occur in
|
||||||
)
|
[issues](https://github.com/modelcontextprotocol/go-sdk/issues) (for concrete
|
||||||
|
proposals) or
|
||||||
type HiParams struct {
|
[discussions](https://github.com/modelcontextprotocol/go-sdk/discussions) for
|
||||||
Name string `json:"name" jsonschema:"the name of the person to greet"`
|
open-ended discussion. See [CONTRIBUTING.md](/CONTRIBUTING.md) for details.
|
||||||
}
|
|
||||||
|
|
||||||
func SayHi(ctx context.Context, req *mcp.CallToolRequest, args HiParams) (*mcp.CallToolResult, any, error) {
|
|
||||||
return &mcp.CallToolResult{
|
|
||||||
Content: []mcp.Content{&mcp.TextContent{Text: "Hi " + args.Name}},
|
|
||||||
}, nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// Create a server with a single tool.
|
|
||||||
server := mcp.NewServer(&mcp.Implementation{Name: "greeter", Version: "v1.0.0"}, nil)
|
|
||||||
|
|
||||||
mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "say hi"}, SayHi)
|
|
||||||
// Run the server over stdin/stdout, until the client disconnects
|
|
||||||
if err := server.Run(context.Background(), &mcp.StdioTransport{}); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The [`examples/`](/examples/) directory contains more example clients and servers.
|
|
||||||
|
|
||||||
## Acknowledgements
|
## Acknowledgements
|
||||||
|
|
||||||
|
|
|
||||||
146
examples/server/toolschemas/main.go
Normal file
146
examples/server/toolschemas/main.go
Normal file
|
|
@ -0,0 +1,146 @@
|
||||||
|
// Copyright 2025 The Go MCP SDK Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by an MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// The toolschemas example demonstrates how to create tools using both the
|
||||||
|
// low-level [ToolHandler] and high level [ToolHandlerFor], as well as how to
|
||||||
|
// customize schemas in both cases.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/google/jsonschema-go/jsonschema"
|
||||||
|
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Input is the input into all the tools handlers below.
|
||||||
|
type Input struct {
|
||||||
|
Name string `json:"name" jsonschema:"the person to greet"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output is the structured output of the tool.
|
||||||
|
//
|
||||||
|
// Not every tool needs to have structured output.
|
||||||
|
type Output struct {
|
||||||
|
Greeting string `json:"greeting" jsonschema:"the greeting to send to the user"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// simpleGreeting is an [mcp.ToolHandlerFor] that only cares about input and output.
|
||||||
|
func simpleGreeting(_ context.Context, _ *mcp.CallToolRequest, input Input) (*mcp.CallToolResult, Output, error) {
|
||||||
|
return nil, Output{"Hi " + input.Name}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// manualGreeter handles the parsing and validation of input and output manually.
|
||||||
|
//
|
||||||
|
// Therfore, it needs to close over its resolved schemas, to use them in
|
||||||
|
// validation.
|
||||||
|
type manualGreeter struct {
|
||||||
|
inputSchema *jsonschema.Resolved
|
||||||
|
outputSchema *jsonschema.Resolved
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *manualGreeter) greet(_ context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||||
|
// errf produces a 'tool error', embedding the error in a CallToolResult.
|
||||||
|
errf := func(format string, args ...any) *mcp.CallToolResult {
|
||||||
|
return &mcp.CallToolResult{
|
||||||
|
Content: []mcp.Content{&mcp.TextContent{Text: fmt.Sprintf(format, args...)}},
|
||||||
|
IsError: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Handle the parsing and validation of input and output.
|
||||||
|
//
|
||||||
|
// Note that errors here are treated as tool errors, not protocol errors.
|
||||||
|
var input Input
|
||||||
|
if err := json.Unmarshal(req.Params.Arguments, &input); err != nil {
|
||||||
|
return errf("failed to unmarshal arguments: %v", err), nil
|
||||||
|
}
|
||||||
|
if err := t.inputSchema.Validate(input); err != nil {
|
||||||
|
return errf("invalid input: %v", err), nil
|
||||||
|
}
|
||||||
|
output := Output{Greeting: "Hi " + input.Name}
|
||||||
|
if err := t.outputSchema.Validate(output); err != nil {
|
||||||
|
return errf("tool produced invalid output: %v", err), nil
|
||||||
|
}
|
||||||
|
outputJSON, err := json.Marshal(output)
|
||||||
|
if err != nil {
|
||||||
|
return errf("output failed to marshal: %v", err), nil
|
||||||
|
}
|
||||||
|
return &mcp.CallToolResult{
|
||||||
|
Content: []mcp.Content{&mcp.TextContent{Text: string(outputJSON)}},
|
||||||
|
StructuredContent: output,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
server := mcp.NewServer(&mcp.Implementation{Name: "greeter"}, nil)
|
||||||
|
|
||||||
|
// Add the 'greeting' tool in a few different ways.
|
||||||
|
|
||||||
|
// First, we can just use [mcp.AddTool], and get the out-of-the-box handling
|
||||||
|
// it provides:
|
||||||
|
mcp.AddTool(server, &mcp.Tool{Name: "simple greeting"}, simpleGreeting)
|
||||||
|
|
||||||
|
// Next, we can create our schemas entirely manually, and add them using
|
||||||
|
// [mcp.Server.AddTool]. Since we're working manually, we can add some
|
||||||
|
// constraints on the length of the name.
|
||||||
|
//
|
||||||
|
// We don't need to do all this work: below, we use jsonschema.For to start
|
||||||
|
// from the default schema.
|
||||||
|
var (
|
||||||
|
manual manualGreeter
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
inputSchema := &jsonschema.Schema{
|
||||||
|
Type: "object",
|
||||||
|
Properties: map[string]*jsonschema.Schema{
|
||||||
|
"name": {Type: "string", MaxLength: jsonschema.Ptr(10)},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
manual.inputSchema, err = inputSchema.Resolve(nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
outputSchema := &jsonschema.Schema{
|
||||||
|
Type: "object",
|
||||||
|
Properties: map[string]*jsonschema.Schema{
|
||||||
|
"greeting": {Type: "string"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
manual.outputSchema, err = outputSchema.Resolve(nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
server.AddTool(&mcp.Tool{
|
||||||
|
Name: "manual greeting",
|
||||||
|
InputSchema: inputSchema,
|
||||||
|
OutputSchema: outputSchema,
|
||||||
|
}, manual.greet)
|
||||||
|
|
||||||
|
// Finally, note that we can also use custom schemas with a ToolHandlerFor.
|
||||||
|
// We can do this in two ways: by using one of the schema values constructed
|
||||||
|
// above, or by using jsonschema.For and adjusting the resulting schema.
|
||||||
|
mcp.AddTool(server, &mcp.Tool{
|
||||||
|
Name: "customized greeting 1",
|
||||||
|
InputSchema: inputSchema,
|
||||||
|
// OutputSchema will still be derived from Output.
|
||||||
|
}, simpleGreeting)
|
||||||
|
|
||||||
|
customSchema, err := jsonschema.For[Input](nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
customSchema.Properties["name"].MaxLength = jsonschema.Ptr(10)
|
||||||
|
mcp.AddTool(server, &mcp.Tool{
|
||||||
|
Name: "customized greeting 2",
|
||||||
|
InputSchema: customSchema,
|
||||||
|
}, simpleGreeting)
|
||||||
|
|
||||||
|
// Now run the server.
|
||||||
|
if err := server.Run(context.Background(), &mcp.StdioTransport{}); err != nil {
|
||||||
|
log.Printf("Server failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -18,18 +18,6 @@ software development kit (SDK) for the Model Context Protocol (MCP).
|
||||||
> changes. We aim to tag v1.0.0 in September, 2025. See
|
> changes. We aim to tag v1.0.0 in September, 2025. See
|
||||||
> https://github.com/modelcontextprotocol/go-sdk/issues/328 for details.
|
> https://github.com/modelcontextprotocol/go-sdk/issues/328 for details.
|
||||||
|
|
||||||
## Design
|
|
||||||
|
|
||||||
The design doc for this SDK is at [design.md](./design/design.md), which was
|
|
||||||
initially reviewed at
|
|
||||||
[modelcontextprotocol/discussions/364](https://github.com/orgs/modelcontextprotocol/discussions/364).
|
|
||||||
|
|
||||||
Further design discussion should occur in
|
|
||||||
[issues](https://github.com/modelcontextprotocol/go-sdk/issues) (for concrete
|
|
||||||
proposals) or
|
|
||||||
[discussions](https://github.com/modelcontextprotocol/go-sdk/discussions) for
|
|
||||||
open-ended discussion. See [CONTRIBUTING.md](/CONTRIBUTING.md) for details.
|
|
||||||
|
|
||||||
## Package documentation
|
## Package documentation
|
||||||
|
|
||||||
The SDK consists of two importable packages:
|
The SDK consists of two importable packages:
|
||||||
|
|
@ -41,21 +29,39 @@ The SDK consists of two importable packages:
|
||||||
- The
|
- The
|
||||||
[`github.com/modelcontextprotocol/go-sdk/jsonrpc`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/jsonrpc) package is for users implementing
|
[`github.com/modelcontextprotocol/go-sdk/jsonrpc`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/jsonrpc) package is for users implementing
|
||||||
their own transports.
|
their own transports.
|
||||||
|
- The
|
||||||
|
[`github.com/modelcontextprotocol/go-sdk/auth`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/auth)
|
||||||
|
package provides some primitives for supporting oauth.
|
||||||
|
|
||||||
## Example
|
## Getting started
|
||||||
|
|
||||||
In this example, an MCP client communicates with an MCP server running in a
|
To get started creating an MCP server, create an `mcp.Server` instance, add
|
||||||
sidecar process:
|
features to it, and then run it over an `mcp.Transport`. For example, this
|
||||||
|
server adds a single simple tool, and then connects it to clients over
|
||||||
%include client/client.go -
|
stdin/stdout:
|
||||||
|
|
||||||
Here's an example of the corresponding server component, which communicates
|
|
||||||
with its client over stdin/stdout:
|
|
||||||
|
|
||||||
%include server/server.go -
|
%include server/server.go -
|
||||||
|
|
||||||
The [`examples/`](/examples/) directory contains more example clients and servers.
|
To communicate with that server, we can similarly create an `mcp.Client` and
|
||||||
|
connect it to the corresponding server, by running the server command and
|
||||||
|
communicating over its stdin/stdout:
|
||||||
|
|
||||||
|
%include client/client.go -
|
||||||
|
|
||||||
|
The [`examples/`](/examples/) directory contains more example clients and
|
||||||
|
servers.
|
||||||
|
|
||||||
|
## Design
|
||||||
|
|
||||||
|
The design doc for this SDK is at [design.md](./design/design.md), which was
|
||||||
|
initially reviewed at
|
||||||
|
[modelcontextprotocol/discussions/364](https://github.com/orgs/modelcontextprotocol/discussions/364).
|
||||||
|
|
||||||
|
Further design discussion should occur in
|
||||||
|
[issues](https://github.com/modelcontextprotocol/go-sdk/issues) (for concrete
|
||||||
|
proposals) or
|
||||||
|
[discussions](https://github.com/modelcontextprotocol/go-sdk/discussions) for
|
||||||
|
open-ended discussion. See [CONTRIBUTING.md](/CONTRIBUTING.md) for details.
|
||||||
|
|
||||||
## Acknowledgements
|
## Acknowledgements
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,20 +12,21 @@ import (
|
||||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HiParams struct {
|
type Input struct {
|
||||||
Name string `json:"name" jsonschema:"the name of the person to greet"`
|
Name string `json:"name" jsonschema:"the name of the person to greet"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func SayHi(ctx context.Context, req *mcp.CallToolRequest, args HiParams) (*mcp.CallToolResult, any, error) {
|
type Output struct {
|
||||||
return &mcp.CallToolResult{
|
Greeting string `json:"greeting" jsonschema:"the greeting to tell to the user"`
|
||||||
Content: []mcp.Content{&mcp.TextContent{Text: "Hi " + args.Name}},
|
}
|
||||||
}, nil, nil
|
|
||||||
|
func SayHi(ctx context.Context, req *mcp.CallToolRequest, input Input) (*mcp.CallToolResult, Output, error) {
|
||||||
|
return nil, Output{Greeting: "Hi " + input.Name}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Create a server with a single tool.
|
// Create a server with a single tool.
|
||||||
server := mcp.NewServer(&mcp.Implementation{Name: "greeter", Version: "v1.0.0"}, nil)
|
server := mcp.NewServer(&mcp.Implementation{Name: "greeter", Version: "v1.0.0"}, nil)
|
||||||
|
|
||||||
mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "say hi"}, SayHi)
|
mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "say hi"}, SayHi)
|
||||||
// Run the server over stdin/stdout, until the client disconnects
|
// Run the server over stdin/stdout, until the client disconnects
|
||||||
if err := server.Run(context.Background(), &mcp.StdioTransport{}); err != nil {
|
if err := server.Run(context.Background(), &mcp.StdioTransport{}); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,9 @@ type CallToolParams struct {
|
||||||
Arguments any `json:"arguments,omitempty"`
|
Arguments any `json:"arguments,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CallToolParamsRaw is passed to tool handlers on the server.
|
// CallToolParamsRaw is passed to tool handlers on the server. Its arguments
|
||||||
|
// are not yet unmarshaled (hence "raw"), so that the handlers can perform
|
||||||
|
// unmarshaling themselves.
|
||||||
type CallToolParamsRaw struct {
|
type CallToolParamsRaw struct {
|
||||||
// This property is reserved by the protocol to allow clients and servers to
|
// This property is reserved by the protocol to allow clients and servers to
|
||||||
// attach additional metadata to their responses.
|
// attach additional metadata to their responses.
|
||||||
|
|
@ -58,29 +60,48 @@ type CallToolParamsRaw struct {
|
||||||
Arguments json.RawMessage `json:"arguments,omitempty"`
|
Arguments json.RawMessage `json:"arguments,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// The server's response to a tool call.
|
// A CallToolResult is the server's response to a tool call.
|
||||||
|
//
|
||||||
|
// The [ToolHandler] and [ToolHandlerFor] handler functions return this result,
|
||||||
|
// though [ToolHandlerFor] populates much of it automatically as documented at
|
||||||
|
// each field.
|
||||||
type CallToolResult struct {
|
type CallToolResult struct {
|
||||||
// This property is reserved by the protocol to allow clients and servers to
|
// This property is reserved by the protocol to allow clients and servers to
|
||||||
// attach additional metadata to their responses.
|
// attach additional metadata to their responses.
|
||||||
Meta `json:"_meta,omitempty"`
|
Meta `json:"_meta,omitempty"`
|
||||||
|
|
||||||
// A list of content objects that represent the unstructured result of the tool
|
// A list of content objects that represent the unstructured result of the tool
|
||||||
// call.
|
// call.
|
||||||
|
//
|
||||||
|
// When using a [ToolHandlerFor] with structured output, if Content is unset
|
||||||
|
// it will be populated with JSON text content corresponding to the
|
||||||
|
// structured output value.
|
||||||
Content []Content `json:"content"`
|
Content []Content `json:"content"`
|
||||||
// An optional JSON object that represents the structured result of the tool
|
|
||||||
// call.
|
// StructuredContent is an optional value that represents the structured
|
||||||
|
// result of the tool call. It must marshal to a JSON object.
|
||||||
|
//
|
||||||
|
// When using a [ToolHandlerFor] with structured output, you should not
|
||||||
|
// populate this field. It will be automatically populated with the typed Out
|
||||||
|
// value.
|
||||||
StructuredContent any `json:"structuredContent,omitempty"`
|
StructuredContent any `json:"structuredContent,omitempty"`
|
||||||
// Whether the tool call ended in an error.
|
|
||||||
|
// IsError reports whether the tool call ended in an error.
|
||||||
//
|
//
|
||||||
// If not set, this is assumed to be false (the call was successful).
|
// If not set, this is assumed to be false (the call was successful).
|
||||||
//
|
//
|
||||||
// Any errors that originate from the tool should be reported inside the result
|
// Any errors that originate from the tool should be reported inside the
|
||||||
// object, with isError set to true, not as an MCP protocol-level error
|
// Content field, with IsError set to true, not as an MCP protocol-level
|
||||||
// response. Otherwise, the LLM would not be able to see that an error occurred
|
// error response. Otherwise, the LLM would not be able to see that an error
|
||||||
// and self-correct.
|
// occurred and self-correct.
|
||||||
//
|
//
|
||||||
// However, any errors in finding the tool, an error indicating that the
|
// However, any errors in finding the tool, an error indicating that the
|
||||||
// server does not support tool calls, or any other exceptional conditions,
|
// server does not support tool calls, or any other exceptional conditions,
|
||||||
// should be reported as an MCP error response.
|
// should be reported as an MCP error response.
|
||||||
|
//
|
||||||
|
// When using a [ToolHandlerFor], this field is automatically set when the
|
||||||
|
// tool handler returns an error, and the error string is included as text in
|
||||||
|
// the Content field.
|
||||||
IsError bool `json:"isError,omitempty"`
|
IsError bool `json:"isError,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -321,22 +321,18 @@ func setSchema[T any](sfield **jsonschema.Schema, rfield **jsonschema.Resolved)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddTool adds a tool and typed tool handler to the server.
|
// AddTool adds a tool and typed tool handler to the server.
|
||||||
|
//
|
||||||
// If the tool's input schema is nil, it is set to the schema inferred from the
|
// If the tool's input schema is nil, it is set to the schema inferred from the
|
||||||
// In type parameter, using [jsonschema.For]. The In type parameter must be a
|
// In type parameter, using [jsonschema.For]. The In type argument must be a
|
||||||
// map or a struct, so that its inferred JSON Schema has type "object".
|
// map or a struct, so that its inferred JSON Schema has type "object".
|
||||||
//
|
//
|
||||||
// For tools that don't return structured output, Out should be 'any'.
|
// If the tool's output schema is nil, and the Out type is not 'any', the
|
||||||
// Otherwise, if the tool's output schema is nil the output schema is set to
|
// output schema is set to the schema inferred from the Out type argument,
|
||||||
// the schema inferred from Out, which must be a map or a struct.
|
// which also must be a map or struct.
|
||||||
//
|
//
|
||||||
// The In argument to the handler will contain the unmarshaled arguments from
|
// Unlike [Server.AddTool], AddTool does a lot automatically, and forces tools
|
||||||
// CallToolRequest.Params.Arguments. Most users can ignore the [CallToolRequest]
|
// to conform to the MCP spec. See [ToolHandlerFor] for a detailed description
|
||||||
// argument to the handler.
|
// of this automatic behavior.
|
||||||
//
|
|
||||||
// The handler's Out return value will be used to populate [CallToolResult.StructuredContent].
|
|
||||||
// If the handler returns a non-nil error, [CallToolResult.IsError] will be set to true,
|
|
||||||
// and [CallToolResult.Content] will be set to the text of the error.
|
|
||||||
// Most users can ignore the [CallToolResult] return value.
|
|
||||||
func AddTool[In, Out any](s *Server, t *Tool, h ToolHandlerFor[In, Out]) {
|
func AddTool[In, Out any](s *Server, t *Tool, h ToolHandlerFor[In, Out]) {
|
||||||
tt, hh, err := toolForErr(t, h)
|
tt, hh, err := toolForErr(t, h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
40
mcp/tool.go
40
mcp/tool.go
|
|
@ -14,14 +14,46 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// A ToolHandler handles a call to tools/call.
|
// A ToolHandler handles a call to tools/call.
|
||||||
// This is a low-level API, for use with [Server.AddTool].
|
//
|
||||||
// Most users will write a [ToolHandlerFor] and install it with [AddTool].
|
// This is a low-level API, for use with [Server.AddTool]. It does not do any
|
||||||
|
// pre- or post-processing of the request or result: the params contain raw
|
||||||
|
// arguments, no input validation is performed, and the result is returned to
|
||||||
|
// the user as-is, without any validation of the output.
|
||||||
|
//
|
||||||
|
// Most users will write a [ToolHandlerFor] and install it with the generic
|
||||||
|
// [AddTool] function.
|
||||||
|
//
|
||||||
|
// If ToolHandler returns an error, it is treated as a protocol error. By
|
||||||
|
// contrast, [ToolHandlerFor] automatically populates [CallToolResult.IsError]
|
||||||
|
// and [CallToolResult.Content] accordingly.
|
||||||
type ToolHandler func(context.Context, *CallToolRequest) (*CallToolResult, error)
|
type ToolHandler func(context.Context, *CallToolRequest) (*CallToolResult, error)
|
||||||
|
|
||||||
// A ToolHandlerFor handles a call to tools/call with typed arguments and results.
|
// A ToolHandlerFor handles a call to tools/call with typed arguments and results.
|
||||||
|
//
|
||||||
// Use [AddTool] to add a ToolHandlerFor to a server.
|
// Use [AddTool] to add a ToolHandlerFor to a server.
|
||||||
// Most users can ignore the [CallToolRequest] argument and [CallToolResult] return value.
|
//
|
||||||
type ToolHandlerFor[In, Out any] func(context.Context, *CallToolRequest, In) (*CallToolResult, Out, error)
|
// Unlike [ToolHandler], [ToolHandlerFor] provides significant functionality
|
||||||
|
// out of the box, and enforces that the tool conforms to the MCP spec:
|
||||||
|
// - The In type provides a default input schema for the tool, though it may
|
||||||
|
// be overridden in [AddTool].
|
||||||
|
// - The input value is automatically unmarshaled from req.Params.Arguments.
|
||||||
|
// - The input value is automatically validated against its input schema.
|
||||||
|
// Invalid input is rejected before getting to the handler.
|
||||||
|
// - If the Out type is not the empty interface [any], it provides the
|
||||||
|
// default output schema for the tool (which again may be overridden in
|
||||||
|
// [AddTool]).
|
||||||
|
// - The Out value is used to populate result.StructuredOutput.
|
||||||
|
// - If [CallToolResult.Content] is unset, it is populated with the JSON
|
||||||
|
// content of the output.
|
||||||
|
// - An error result is treated as a tool error, rather than a protocol
|
||||||
|
// error, and is therefore packed into CallToolResult.Content, with
|
||||||
|
// [IsError] set.
|
||||||
|
//
|
||||||
|
// For these reasons, most users can ignore the [CallToolRequest] argument and
|
||||||
|
// [CallToolResult] return values entirely. In fact, it is permissible to
|
||||||
|
// return a nil CallToolResult, if you only care about returning a output value
|
||||||
|
// or error. The effective result will be populated as described above.
|
||||||
|
type ToolHandlerFor[In, Out any] func(_ context.Context, request *CallToolRequest, input In) (result *CallToolResult, output Out, _ error)
|
||||||
|
|
||||||
// A serverTool is a tool definition that is bound to a tool handler.
|
// A serverTool is a tool definition that is bound to a tool handler.
|
||||||
type serverTool struct {
|
type serverTool struct {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue