fix(go.sum): update ResolveSpec dependency to v1.0.87
This commit is contained in:
+242
@@ -0,0 +1,242 @@
|
||||
// Copyright 2018 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 jsonrpc2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
internaljson "github.com/modelcontextprotocol/go-sdk/internal/json"
|
||||
|
||||
"github.com/modelcontextprotocol/go-sdk/internal/mcpgodebug"
|
||||
)
|
||||
|
||||
// ID is a Request identifier, which is defined by the spec to be a string, integer, or null.
|
||||
// https://www.jsonrpc.org/specification#request_object
|
||||
type ID struct {
|
||||
value any
|
||||
}
|
||||
|
||||
// MakeID coerces the given Go value to an ID. The value should be the
|
||||
// default JSON marshaling of a Request identifier: nil, float64, or string.
|
||||
//
|
||||
// Returns an error if the value type was not a valid Request ID type.
|
||||
//
|
||||
// TODO: ID can't be a json.Marshaler/Unmarshaler, because we want to omitzero.
|
||||
// Simplify this package by making ID json serializable once we can rely on
|
||||
// omitzero.
|
||||
func MakeID(v any) (ID, error) {
|
||||
switch v := v.(type) {
|
||||
case nil:
|
||||
return ID{}, nil
|
||||
case float64:
|
||||
return Int64ID(int64(v)), nil
|
||||
case string:
|
||||
return StringID(v), nil
|
||||
}
|
||||
return ID{}, fmt.Errorf("%w: invalid ID type %T", ErrParse, v)
|
||||
}
|
||||
|
||||
// Message is the interface to all jsonrpc2 message types.
|
||||
// They share no common functionality, but are a closed set of concrete types
|
||||
// that are allowed to implement this interface. The message types are *Request
|
||||
// and *Response.
|
||||
type Message interface {
|
||||
// marshal builds the wire form from the API form.
|
||||
// It is private, which makes the set of Message implementations closed.
|
||||
marshal(to *wireCombined)
|
||||
}
|
||||
|
||||
// Request is a Message sent to a peer to request behavior.
|
||||
// If it has an ID it is a call, otherwise it is a notification.
|
||||
type Request struct {
|
||||
// ID of this request, used to tie the Response back to the request.
|
||||
// This will be nil for notifications.
|
||||
ID ID
|
||||
// Method is a string containing the method name to invoke.
|
||||
Method string
|
||||
// Params is either a struct or an array with the parameters of the method.
|
||||
Params json.RawMessage
|
||||
// Extra is additional information that does not appear on the wire. It can be
|
||||
// used to pass information from the application to the underlying transport.
|
||||
Extra any
|
||||
}
|
||||
|
||||
// Response is a Message used as a reply to a call Request.
|
||||
// It will have the same ID as the call it is a response to.
|
||||
type Response struct {
|
||||
// result is the content of the response.
|
||||
Result json.RawMessage
|
||||
// err is set only if the call failed.
|
||||
Error error
|
||||
// id of the request this is a response to.
|
||||
ID ID
|
||||
// Extra is additional information that does not appear on the wire. It can be
|
||||
// used to pass information from the underlying transport to the application.
|
||||
Extra any
|
||||
}
|
||||
|
||||
// StringID creates a new string request identifier.
|
||||
func StringID(s string) ID { return ID{value: s} }
|
||||
|
||||
// Int64ID creates a new integer request identifier.
|
||||
func Int64ID(i int64) ID { return ID{value: i} }
|
||||
|
||||
// IsValid returns true if the ID is a valid identifier.
|
||||
// The default value for ID will return false.
|
||||
func (id ID) IsValid() bool { return id.value != nil }
|
||||
|
||||
// Raw returns the underlying value of the ID.
|
||||
func (id ID) Raw() any { return id.value }
|
||||
|
||||
// NewNotification constructs a new Notification message for the supplied
|
||||
// method and parameters.
|
||||
func NewNotification(method string, params any) (*Request, error) {
|
||||
p, merr := marshalToRaw(params)
|
||||
return &Request{Method: method, Params: p}, merr
|
||||
}
|
||||
|
||||
// NewCall constructs a new Call message for the supplied ID, method and
|
||||
// parameters.
|
||||
func NewCall(id ID, method string, params any) (*Request, error) {
|
||||
p, merr := marshalToRaw(params)
|
||||
return &Request{ID: id, Method: method, Params: p}, merr
|
||||
}
|
||||
|
||||
func (msg *Request) IsCall() bool { return msg.ID.IsValid() }
|
||||
|
||||
func (msg *Request) marshal(to *wireCombined) {
|
||||
to.ID = msg.ID.value
|
||||
to.Method = msg.Method
|
||||
to.Params = msg.Params
|
||||
}
|
||||
|
||||
// NewResponse constructs a new Response message that is a reply to the
|
||||
// supplied. If err is set result may be ignored.
|
||||
func NewResponse(id ID, result any, rerr error) (*Response, error) {
|
||||
r, merr := marshalToRaw(result)
|
||||
return &Response{ID: id, Result: r, Error: rerr}, merr
|
||||
}
|
||||
|
||||
func (msg *Response) marshal(to *wireCombined) {
|
||||
to.ID = msg.ID.value
|
||||
to.Error = toWireError(msg.Error)
|
||||
to.Result = msg.Result
|
||||
}
|
||||
|
||||
func toWireError(err error) *WireError {
|
||||
if err == nil {
|
||||
// no error, the response is complete
|
||||
return nil
|
||||
}
|
||||
if err, ok := err.(*WireError); ok {
|
||||
// already a wire error, just use it
|
||||
return err
|
||||
}
|
||||
result := &WireError{Message: err.Error()}
|
||||
var wrapped *WireError
|
||||
if errors.As(err, &wrapped) {
|
||||
// if we wrapped a wire error, keep the code from the wrapped error
|
||||
// but the message from the outer error
|
||||
result.Code = wrapped.Code
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func EncodeMessage(msg Message) ([]byte, error) {
|
||||
wire := wireCombined{VersionTag: wireVersion}
|
||||
msg.marshal(&wire)
|
||||
data, err := jsonMarshal(&wire)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("marshaling jsonrpc message: %w", err)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// EncodeIndent is like EncodeMessage, but honors indents.
|
||||
// TODO(rfindley): refactor so that this concern is handled independently.
|
||||
// Perhaps we should pass in a json.Encoder?
|
||||
func EncodeIndent(msg Message, prefix, indent string) ([]byte, error) {
|
||||
wire := wireCombined{VersionTag: wireVersion}
|
||||
msg.marshal(&wire)
|
||||
var buf bytes.Buffer
|
||||
enc := json.NewEncoder(&buf)
|
||||
enc.SetEscapeHTML(false)
|
||||
enc.SetIndent(prefix, indent)
|
||||
if err := enc.Encode(&wire); err != nil {
|
||||
return nil, fmt.Errorf("marshaling jsonrpc message: %w", err)
|
||||
}
|
||||
return bytes.TrimRight(buf.Bytes(), "\n"), nil
|
||||
}
|
||||
|
||||
func DecodeMessage(data []byte) (Message, error) {
|
||||
msg := wireCombined{}
|
||||
if err := internaljson.Unmarshal(data, &msg); err != nil {
|
||||
return nil, fmt.Errorf("unmarshaling jsonrpc message: %w", err)
|
||||
}
|
||||
if msg.VersionTag != wireVersion {
|
||||
return nil, fmt.Errorf("invalid message version tag %q; expected %q", msg.VersionTag, wireVersion)
|
||||
}
|
||||
id, err := MakeID(msg.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if msg.Method != "" {
|
||||
// has a method, must be a call
|
||||
return &Request{
|
||||
Method: msg.Method,
|
||||
ID: id,
|
||||
Params: msg.Params,
|
||||
}, nil
|
||||
}
|
||||
// no method, should be a response
|
||||
if !id.IsValid() {
|
||||
return nil, ErrInvalidRequest
|
||||
}
|
||||
resp := &Response{
|
||||
ID: id,
|
||||
Result: msg.Result,
|
||||
}
|
||||
// we have to check if msg.Error is nil to avoid a typed error
|
||||
if msg.Error != nil {
|
||||
resp.Error = msg.Error
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func marshalToRaw(obj any) (json.RawMessage, error) {
|
||||
if obj == nil {
|
||||
return nil, nil
|
||||
}
|
||||
data, err := jsonMarshal(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.RawMessage(data), nil
|
||||
}
|
||||
|
||||
// jsonescaping is a compatibility parameter that allows to restore
|
||||
// JSON escaping in the JSON marshaling, which stopped being the default
|
||||
// in the 1.4.0 version of the SDK. See the documentation for the
|
||||
// mcpgodebug package for instructions how to enable it.
|
||||
// The option will be removed in the 1.6.0 version of the SDK.
|
||||
var jsonescaping = mcpgodebug.Value("jsonescaping")
|
||||
|
||||
// jsonMarshal marshals obj to JSON like json.Marshal but without HTML escaping.
|
||||
func jsonMarshal(obj any) ([]byte, error) {
|
||||
if jsonescaping == "1" {
|
||||
return json.Marshal(obj)
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
enc := json.NewEncoder(&buf)
|
||||
enc.SetEscapeHTML(false)
|
||||
if err := enc.Encode(obj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// json.Encoder.Encode adds a trailing newline. Trim it to be consistent with json.Marshal.
|
||||
return bytes.TrimRight(buf.Bytes(), "\n"), nil
|
||||
}
|
||||
Reference in New Issue
Block a user