API
Version 0.1.3. Every public type, function and value, from the sources.
moonjrpc
Messages and the method table. No I/O.
suberrorFailed
pub(all) suberror Failed {
Failed(Failure)
TimedOut(name~ : String, limit~ : Int32)
Closed(name~ : String)
TooLong(limit~ : Int32, got~ : Int64)
Malformed(String)
}
What goes wrong with a call. Failed is the other side's error object, and a handler raises it to answer with one. limit~ is the bound that was passed and got~ what came.
structFailure
pub(all) struct Failure {
code : Int32
message : String
data : Json?
}
The error object of a response.
enumId
pub(all) enum Id {
Num(Double, repr~ : String?)
Text(String)
Null
}
A request's identifier. A number keeps the text it was written in, as core's Json does, so an identifier Double cannot hold goes back out exactly as it came in.
fnId::int
pub fn Id::int(n : Int64) -> Id
An identifier from an integer.
fnId::key
pub fn Id::key(self : Id) -> String
The same text for the same identifier, whichever side wrote it: a map key for matching replies to calls.
enumMessage
pub(all) enum Message {
Call(id~ : Id, name~ : String, params~ : Json?)
Notice(name~ : String, params~ : Json?)
Reply(id~ : Id, result~ : Json)
Refusal(id~ : Id, failure~ : Failure)
}
One JSON-RPC 2.0 message. A Refusal is a response carrying an error.
fnMessage::invalid
pub fn Message::invalid(json : Json) -> Message
The answer to something that is not a valid message: -32600, with its identifier if one can be read and null otherwise.
fnMessage::unreadable
pub fn Message::unreadable() -> Message
The answer to text that is not JSON: -32700, with a null identifier.
structMethods
struct Methods
The method table: what a side does with the calls and notices it receives. Registering a name again replaces it.
fnMethods::new
pub fn Methods::new(on_error? : (String, Error) -> Unit) -> Methods
A table with nothing in it. on_error hears the errors a client is not told about: a handler's failure other than Failed, which goes out as -32603 with no detail, and any failure of a notice, which has no one to go to.
fnMethods::call
pub fn Methods::call(self : Methods, name : String, run : async (Json?) -> Json) -> Unit
Answer calls to name with what run returns. The parameters arrive as they came.
fnMethods::notice
pub fn Methods::notice(self : Methods, name : String, run : async (Json?) -> Unit) -> Unit
Run run for each notice named name. Nothing goes back.
fnMethods::typed
pub fn[P : @json.FromJson, R : ToJson] Methods::typed(self : Methods, name : String, run : async (P) -> R) -> Unit
A call whose parameters and result go through FromJson and ToJson. Parameters that do not decode are -32602, with the decoder's reason as the error's data.
fnMethods::answer
pub async fn Methods::answer(self : Methods, message : Message) -> Message?
What to send back for a message: the response to a call, nothing for the rest. A method nobody registered is -32601 and a raised Failed goes out as it is; any other error is -32603 with no detail, because what a handler trips over is not the caller's to read.
fnMethods::body
pub async fn Methods::body(self : Methods, text : StringView, batch~ : Bool = true) -> String?
One body in, one body out, by the specification: text that is not JSON is -32700, a value that is not a message is -32600, an array is a batch, and when nothing needs answering there is nothing to send. batch=false is for protocols that forbid arrays, as ACP does: an array is then -32600.
fnfail
pub fn[T] fail(code : Int32, message : String, data? : Json) -> T raise Failed
Raise an error object: fail(invalid_params, "no such file").
letinternal_error
pub let internal_error : Int32
The handler failed in a way the caller is not told about.
letinvalid_params
pub let invalid_params : Int32
The parameters are not what the method takes.
letinvalid_request
pub let invalid_request : Int32
The value is not a valid message.
letmethod_not_found
pub let method_not_found : Int32
Nobody registered that method.
letparse_error
pub let parse_error : Int32
The text is not JSON.
moonjrpc/peer
Two-way calls over any transport, and the two framings.
traitFrames
pub(open) trait Frames {
async fn read(Self) -> String?
async fn write(Self, String) -> Unit
}
How one message's text travels: a line, a WebSocket frame, an HTTP body. Implement this to carry JSON-RPC over anything.
structPeer
struct Peer
One end of a conversation in which either side may call the other. Messages are taken in the order they arrive: a notice finishes before the next message is looked at, a call starts in its turn and runs on in a task of its own, and a reply wakes its caller in its turn. So the updates a side sends before answering are handled before its caller sees the answer. A notice handler that waits on Peer::call waits for a reply that cannot be looked at until the handler returns. Start a task for that instead.
fnPeer::new
pub fn Peer::new(frames : &Frames, methods~ : @moonjrpc.Methods = @moonjrpc.Methods::new(), timeout_ms~ : Int32? = Some(timeout_ms), batch~ : Bool = true) -> Peer
timeout_ms=None waits as long as it takes. batch=false answers an array with -32600, for protocols that forbid batches.
fnPeer::call
pub async fn Peer::call(self : Peer, name : String, params? : Json, timeout_ms~ : Int32? = self.timeout_ms) -> Json
Send a call and wait for its answer. The other side's error object raises Failed; no answer in time raises TimedOut, and the side going away raises Closed. Telling the other side the call was given up on is for the protocol to do: MCP, ACP and LSP each have their own notice for it.
fnPeer::notify
pub async fn Peer::notify(self : Peer, name : String, params? : Json) -> Unit
Send a notice. It raises Closed once the other side has gone away.
fnPeer::run
pub async fn Peer::run(self : Peer) -> Unit
Read until the other side closes. Calls still running are waited for, so a client that writes its requests and closes its end still gets its answers; cancel this task to stop sooner.
fncontent_length
pub fn content_length(reader : &@io.Reader, writer : &@io.Writer, limit~ : Int32 = limit) -> &Frames
A Content-Length header, a blank line, the body: the base protocol of LSP. A message over limit bytes raises TooLong.
letlimit
pub let limit : Int32
The longest message lines and content_length take: 64 MiB. vscode-jsonrpc and the MCP SDKs set no bound, gRPC's default is 4 MiB; no common value exists, so this takes the safe side without refusing what a large tool result needs.
fnlines
pub fn lines(reader : &@io.Reader, writer : &@io.Writer, limit~ : Int32 = limit) -> &Frames
One message to a line: the stdio transport of MCP and ACP. Blank lines are skipped, a last line with no newline still counts, and a line over limit bytes raises TooLong.
lettimeout_ms
pub let timeout_ms : Int32
How long Peer::call waits: 60 seconds, the MCP TypeScript SDK's DEFAULT_REQUEST_TIMEOUT_MSEC.