Also validate that requests observe the lifecycle rules defined in the spec. Change-Id: I302282f81e053e76926e0bb295f7dbf8d526f02d Reviewed-on: https://go-review.googlesource.com/c/tools/+/667575 Reviewed-by: Jonathan Amsterdam <jba@google.com> Commit-Queue: Robert Findley <rfindley@google.com> Auto-Submit: Robert Findley <rfindley@google.com> TryBot-Bypass: Robert Findley <rfindley@google.com>
70 lines
2.2 KiB
Go
70 lines
2.2 KiB
Go
// 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.
|
|
|
|
package mcp
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
|
|
"github.com/modelcontextprotocol/go-sdk/internal/jsonschema"
|
|
"github.com/modelcontextprotocol/go-sdk/internal/protocol"
|
|
)
|
|
|
|
// A ToolHandler handles a call to tools/call.
|
|
type ToolHandler func(context.Context, *ClientConnection, json.RawMessage) (*protocol.CallToolResult, error)
|
|
|
|
// A Tool is a tool definition that is bound to a tool handler.
|
|
type Tool struct {
|
|
Definition protocol.Tool
|
|
Handler ToolHandler
|
|
}
|
|
|
|
// MakeTool is a helper to make a tool using reflection on the given handler.
|
|
//
|
|
// 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.
|
|
//
|
|
// It is the caller's responsibility that the handler request type can produce
|
|
// a valid schema, as documented by [jsonschema.ForType]; otherwise, MakeTool
|
|
// panics.
|
|
func MakeTool[TReq any](name, description string, handler func(context.Context, *ClientConnection, TReq) ([]Content, error)) *Tool {
|
|
schema, err := jsonschema.ForType[TReq]()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
wrapped := func(ctx context.Context, cc *ClientConnection, args json.RawMessage) (*protocol.CallToolResult, error) {
|
|
var v TReq
|
|
if err := unmarshalSchema(args, schema, &v); err != nil {
|
|
return nil, err
|
|
}
|
|
content, err := handler(ctx, cc, v)
|
|
if err != nil {
|
|
return &protocol.CallToolResult{
|
|
Content: marshalContent([]Content{TextContent{Text: err.Error()}}),
|
|
IsError: true,
|
|
}, nil
|
|
}
|
|
res := &protocol.CallToolResult{
|
|
Content: marshalContent(content),
|
|
}
|
|
return res, nil
|
|
}
|
|
return &Tool{
|
|
Definition: protocol.Tool{
|
|
Name: name,
|
|
Description: description,
|
|
InputSchema: schema,
|
|
},
|
|
Handler: wrapped,
|
|
}
|
|
}
|
|
|
|
// unmarshalSchema unmarshals data into v and validates the result according to
|
|
// the given schema.
|
|
func unmarshalSchema(data json.RawMessage, _ *jsonschema.Schema, v any) error {
|
|
// TODO: use reflection to create the struct type to unmarshal into.
|
|
// Separate validation from assignment.
|
|
return json.Unmarshal(data, v)
|
|
}
|