internal/mcp: reorganize the tool API, keeping generics
CallToolParams.Arguments is any. Clients can send whatever they want. Tool authors who create their own ToolHandler (bypassing NewTool) will receive the arguments as a map[string]any which has been validated against the input schema. Tool authors who call NewTool can choose the type that the arguments unmarshal into. There is no double-unmarshaling and no downcasting. There is no way for tool authors to avoid the unmarshal, and no way to avoid the validation unless the omit an input schema, which is probably bad for LLMs. If tool authors want this optimization, we can provide it later. Change-Id: Ieb201e56c7fb8a23bb1a93b27e946630ec9e79ad Reviewed-on: https://go-review.googlesource.com/c/tools/+/679595 TryBot-Bypass: Jonathan Amsterdam <jba@google.com> Auto-Submit: Jonathan Amsterdam <jba@google.com> Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
parent
4c543514c4
commit
f211cc8e4a
1 changed files with 328 additions and 228 deletions
|
|
@ -6,8 +6,6 @@ package mcp
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"iter"
|
||||
"slices"
|
||||
"sync"
|
||||
|
|
@ -288,36 +286,15 @@ func (cs *ClientSession) ListTools(ctx context.Context, params *ListToolsParams)
|
|||
}
|
||||
|
||||
// CallTool calls the tool with the given name and arguments.
|
||||
// Pass a [CallToolOptions] to provide additional request fields.
|
||||
func (cs *ClientSession) CallTool(ctx context.Context, params *CallToolParams[json.RawMessage]) (*CallToolResult, error) {
|
||||
// The arguments can be any value that marshals into a JSON object.
|
||||
func (cs *ClientSession) CallTool(ctx context.Context, params *CallToolParams) (*CallToolResult, error) {
|
||||
if params.Arguments == nil {
|
||||
// Avoid sending nil over the wire.
|
||||
params.Arguments = map[string]any{}
|
||||
}
|
||||
return handleSend[*CallToolResult](ctx, cs, methodCallTool, params)
|
||||
}
|
||||
|
||||
// CallTool is a helper to call a tool with any argument type. It returns an
|
||||
// error if params.Arguments fails to marshal to JSON.
|
||||
func CallTool[TArgs any](ctx context.Context, cs *ClientSession, params *CallToolParams[TArgs]) (*CallToolResult, error) {
|
||||
wireParams, err := toWireParams(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cs.CallTool(ctx, wireParams)
|
||||
}
|
||||
|
||||
func toWireParams[TArgs any](params *CallToolParams[TArgs]) (*CallToolParams[json.RawMessage], error) {
|
||||
data, err := json.Marshal(params.Arguments)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal arguments: %v", err)
|
||||
}
|
||||
// The field mapping here must be kept up to date with the CallToolParams.
|
||||
// This is partially enforced by TestToWireParams, which verifies that all
|
||||
// comparable fields are mapped.
|
||||
return &CallToolParams[json.RawMessage]{
|
||||
Meta: params.Meta,
|
||||
Name: params.Name,
|
||||
Arguments: data,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (cs *ClientSession) SetLevel(ctx context.Context, params *SetLevelParams) error {
|
||||
_, err := handleSend[*emptyResult](ctx, cs, methodSetLevel, params)
|
||||
return err
|
||||
|
|
|
|||
Loading…
Reference in a new issue