52 lines
1.7 KiB
Go
52 lines
1.7 KiB
Go
package mcp
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
)
|
|
|
|
// Tool is one MCP tool: its advertised schema plus the Invoke func that parses
|
|
// arguments, does the work, and returns a JSON-encodable payload. Invoke's error
|
|
// is surfaced to the client as a tool error (isError: true), not a protocol
|
|
// error. The context carries request scope (deadline, cancellation, trace span);
|
|
// it flows from the HTTP request through to Invoke.
|
|
type Tool struct {
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
InputSchema json.RawMessage `json:"inputSchema"`
|
|
Invoke func(ctx context.Context, args json.RawMessage) (any, error) `json:"-"`
|
|
}
|
|
|
|
// Text wraps a payload as a successful tools/call result: one JSON text block.
|
|
func Text(v any) any {
|
|
b, err := json.Marshal(v)
|
|
if err != nil {
|
|
return ErrorResult(err)
|
|
}
|
|
return map[string]any{
|
|
"content": []map[string]any{{"type": "text", "text": string(b)}},
|
|
}
|
|
}
|
|
|
|
// ErrorResult wraps an operational error as an isError tools/call result (a tool
|
|
// failure the model can read, not a JSON-RPC protocol error).
|
|
func ErrorResult(err error) any {
|
|
return map[string]any{
|
|
"isError": true,
|
|
"content": []map[string]any{{"type": "text", "text": err.Error()}},
|
|
}
|
|
}
|
|
|
|
// Decode unmarshals tool arguments into v, treating empty/null args as a no-op
|
|
// (zero value). Returns a wrapped error suitable for returning from Invoke.
|
|
func Decode(args json.RawMessage, v any) error {
|
|
if len(args) == 0 || string(args) == "null" {
|
|
return nil
|
|
}
|
|
if err := json.Unmarshal(args, v); err != nil {
|
|
return fmt.Errorf("invalid arguments: %w", err)
|
|
}
|
|
return nil
|
|
}
|