Documentation
¶
Overview ¶
Package jsonrpc2 is a minimal implementation of the JSON RPC 2 spec. https://www.jsonrpc.org/specification It is intended to be compatible with other implementations at the wire level.
Index ¶
- Variables
- func Async(ctx context.Context)
- func EncodeIndent(msg Message, prefix, indent string) ([]byte, error)
- func EncodeMessage(msg Message) ([]byte, error)
- func NewError(code int64, message string) error
- type AsyncCall
- type Connection
- func (c *Connection) Call(ctx context.Context, method string, params any) *AsyncCall
- func (c *Connection) Cancel(id ID)
- func (c *Connection) Close() error
- func (c *Connection) Notify(ctx context.Context, method string, params any) (err error)
- func (c *Connection) Retire(ac *AsyncCall, err error)
- func (c *Connection) Wait() error
- type ConnectionConfig
- type Handler
- type HandlerFunc
- type ID
- type Message
- type Preempter
- type Reader
- type Request
- type Response
- type WireError
- type Writer
Constants ¶
This section is empty.
Variables ¶
var ( // ErrParse is used when invalid JSON was received by the server. ErrParse = NewError(-32700, "parse error") // ErrInvalidRequest is used when the JSON sent is not a valid Request object. ErrInvalidRequest = NewError(-32600, "invalid request") // ErrMethodNotFound should be returned by the handler when the method does // not exist / is not available. ErrMethodNotFound = NewError(-32601, "method not found") // ErrInvalidParams should be returned by the handler when method // parameter(s) were invalid. ErrInvalidParams = NewError(-32602, "invalid params") // ErrInternal indicates a failure to process a call correctly ErrInternal = NewError(-32603, "internal error") // ErrUnknown should be used for all non coded errors. ErrUnknown = NewError(-32001, "unknown error") // ErrServerClosing is returned for calls that arrive while the server is closing. ErrServerClosing = NewError(-32004, "server is closing") // ErrClientClosing is a dummy error returned for calls initiated while the client is closing. ErrClientClosing = NewError(-32003, "client is closing") // ErrRejected may be wrapped to return errors from calls to Writer.Write // that signal that the request was rejected by the transport layer as // invalid. // // Such failures do not indicate that the connection is broken, but rather // should be returned to the caller to indicate that the specific request is // invalid in the current context. ErrRejected = NewError(-32005, "rejected by transport") )
var ErrNotHandled = errors.New("JSON RPC not handled")
ErrNotHandled is returned from a Handler or Preempter to indicate it did not handle the request.
If a Handler returns ErrNotHandled, the server replies with ErrMethodNotFound.
Functions ¶
func Async ¶ added in v0.3.0
Async, signals that the current jsonrpc2 request may be handled asynchronously to subsequent requests, when ctx is the request context.
Async must be called at most once on each request's context (and its descendants).
func EncodeIndent ¶
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 EncodeMessage ¶
Types ¶
type AsyncCall ¶
type AsyncCall struct {
// contains filtered or unexported fields
}
type Connection ¶
type Connection struct {
// contains filtered or unexported fields
}
Connection manages the jsonrpc2 protocol, connecting responses back to their calls. Connection is bidirectional; it does not have a designated server or client end.
Note that the word 'Connection' is overloaded: the mcp.Connection represents the bidirectional stream of messages between client an server. The jsonrpc2.Connection layers RPC logic on top of that stream, dispatching RPC handlers, and correlating requests with responses from the peer.
Some of the complexity of the Connection type is grown out of its usage in gopls: it could probably be simplified based on our usage in MCP.
func NewConnection ¶
func NewConnection(ctx context.Context, cfg ConnectionConfig) *Connection
NewConnection creates a new Connection object and starts processing incoming messages.
func (*Connection) Call ¶
Call invokes the target method and returns an object that can be used to await the response. The params will be marshaled to JSON before sending over the wire, and will be handed to the method invoked. You do not have to wait for the response, it can just be ignored if not needed. If sending the call failed, the response will be ready and have the error in it.
func (*Connection) Cancel ¶
func (c *Connection) Cancel(id ID)
Cancel cancels the Context passed to the Handle call for the inbound message with the given ID.
Cancel will not complain if the ID is not a currently active message, and it will not cause any messages that have not arrived yet with that ID to be cancelled.
func (*Connection) Close ¶
func (c *Connection) Close() error
Close stops accepting new requests, waits for in-flight requests and enqueued Handle calls to complete, and then closes the underlying stream.
After the start of a Close, notification requests (that lack IDs and do not receive responses) will continue to be passed to the Preempter, but calls with IDs will receive immediate responses with ErrServerClosing, and no new requests (not even notifications!) will be enqueued to the Handler.
func (*Connection) Notify ¶
Notify invokes the target method but does not wait for a response. The params will be marshaled to JSON before sending over the wire, and will be handed to the method invoked.
func (*Connection) Retire ¶ added in v1.2.0
func (c *Connection) Retire(ac *AsyncCall, err error)
Retire stops tracking the call, and reports err as its terminal error.
Retire is safe to call multiple times: if the call is already no longer tracked, Retire is a no op.
func (*Connection) Wait ¶
func (c *Connection) Wait() error
Wait blocks until the connection is fully closed, but does not close it.
type ConnectionConfig ¶
type ConnectionConfig struct {
Reader Reader // required
Writer Writer // required
Closer io.Closer // required
Preempter Preempter // optional
Bind func(*Connection) Handler // required
OnDone func() // optional
OnInternalError func(error) // optional
}
A ConnectionConfig configures a bidirectional jsonrpc2 connection.
type Handler ¶
type Handler interface {
// Handle is invoked sequentially for each incoming request that has not
// already been handled by a Preempter.
//
// If the Request has a nil ID, Handle must return a nil result,
// and any error may be logged but will not be reported to the caller.
//
// If the Request has a non-nil ID, Handle must return either a
// non-nil, JSON-marshalable result, or a non-nil error.
//
// The Context passed to Handle will be canceled if the
// connection is broken or the request is canceled or completed.
// (If Handle returns ErrAsyncResponse, ctx will remain uncanceled
// until either Cancel or Respond is called for the request's ID.)
Handle(ctx context.Context, req *Request) (result any, err error)
}
Handler handles messages on a connection.
type HandlerFunc ¶
A HandlerFunc implements the Handler interface for a standalone Handle function.
type ID ¶
type ID struct {
// contains filtered or unexported fields
}
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
func MakeID ¶
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.
type Message ¶
type Message interface {
// contains filtered or unexported methods
}
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.
func DecodeMessage ¶
type Preempter ¶
type Preempter interface {
// Preempt is invoked for each incoming request before it is queued for handling.
//
// If Preempt returns ErrNotHandled, the request will be queued,
// and eventually passed to a Handle call.
//
// Otherwise, the result and error are processed as if returned by Handle.
//
// Preempt must not block. (The Context passed to it is for Values only.)
Preempt(ctx context.Context, req *Request) (result any, err error)
}
Preempter handles messages on a connection before they are queued to the main handler. Primarily this is used for cancel handlers or notifications for which out of order processing is not an issue.
type Reader ¶
type Reader interface {
// Read gets the next message from the stream.
Read(context.Context) (Message, error)
}
Reader abstracts the transport mechanics from the JSON RPC protocol. A Connection reads messages from the reader it was provided on construction, and assumes that each call to Read fully transfers a single message, or returns an error.
A reader is not safe for concurrent use, it is expected it will be used by a single Connection in a safe manner.
type Request ¶
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
}
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.
func NewNotification ¶
NewNotification constructs a new Notification message for the supplied method and parameters.
type Response ¶
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
}
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 WireError ¶
type WireError struct {
// Code is an error code indicating the type of failure.
Code int64 `json:"code"`
// Message is a short description of the error.
Message string `json:"message"`
// Data is optional structured data containing additional information about the error.
Data json.RawMessage `json:"data,omitempty"`
}
WireError represents a structured error in a Response.
type Writer ¶
type Writer interface {
// Write sends a message to the stream.
Write(context.Context, Message) error
}
Writer abstracts the transport mechanics from the JSON RPC protocol. A Connection writes messages using the writer it was provided on construction, and assumes that each call to Write fully transfers a single message, or returns an error.
A writer must be safe for concurrent use, as writes may occur concurrently in practice: libraries may make calls or respond to requests asynchronously.