Expand description
JSON-RPC 2.0 message types. Lifted out of lattice-lsp (IDE-protocol
Risk 3) so a second peer-protocol crate (lattice-claude-code) can
reuse the wire shape without an ide -> lsp crate edge. The types are
transport-agnostic; each peer’s codec writes the bytes.
JSON-RPC 2.0 message types – the wire shape of every LSP
exchange. Transport-agnostic: these types serialize through
serde_json::to_string, the codec writes the bytes.
Also the wire shape of the Claude Code IDE peer (lattice-claude-code)
and the MCP server in lattice-ai; none of them owns the types, so none
of them depends on another to share them. Framing (LSP’s Content-Length
headers, a WebSocket frame) is each peer’s codec’s business.
§Examples
use lattice_protocol::{Message, Request, RequestId, Response, ResponseError};
use serde_json::json;
// Outgoing: build, then serialize for the codec.
let req = Request::new(RequestId::from_u64(1), "initialize", Some(json!({})));
let bytes = Message::Request(req).to_json().unwrap();
assert!(bytes.starts_with(br#"{"jsonrpc":"2.0","id":1,"method":"initialize""#));
// Incoming: decode by shape — `id` + `result` is a response.
let wire = br#"{"jsonrpc":"2.0","id":1,"result":{"capabilities":{}}}"#;
let Message::Response(resp) = Message::from_json(wire).unwrap() else {
panic!("expected a response");
};
assert_eq!(resp.id, RequestId::Number(1));
assert!(resp.error.is_none());
// `method` without `id` is a notification.
let note = br#"{"jsonrpc":"2.0","method":"$/progress","params":{}}"#;
assert!(matches!(Message::from_json(note), Ok(Message::Notification(_))));
// Answering a server-initiated request we cannot serve.
let reply = Response::err(
RequestId::String("cfg-1".into()),
ResponseError {
code: lattice_protocol::jsonrpc::error_codes::METHOD_NOT_FOUND,
message: "unsupported".into(),
data: None,
},
);
let json = String::from_utf8(Message::Response(reply).to_json().unwrap()).unwrap();
assert!(json.contains(r#""id":"cfg-1""#) && !json.contains("result"));§Why we keep params / result as serde_json::Value
lsp-types has typed structs for every method. We could
parameterize on <P, R> but that would push the typing into
every actor and complicate dynamic dispatch (one mailbox
handling 30+ method shapes). Instead the codec yields
serde_json::Value-bearing messages and the actor downcasts
per method via serde_json::from_value::<T>(...). That
confines the type discipline to the message-handling layer
where it belongs.
§Id correlation
Every outgoing request gets a fresh RequestId::Number from
the actor’s monotonic counter. Incoming responses are matched
against an id → oneshot map; matching is a HashMap lookup.
Server-initiated requests (e.g. workspace/configuration) use
their own id; we echo it back on the response unchanged.
Modules§
- error_
codes - Standard JSON-RPC 2.0 error codes plus the LSP-defined ones. Used by the actor to build error responses to server-initiated requests we can’t fulfil.
Structs§
- Notification
- One JSON-RPC notification: fire-and-forget, no response. LSP
uses these for
textDocument/didChange,textDocument/publishDiagnostics,$/progress, and the like. - Request
- One JSON-RPC request that expects a response.
paramsis optional per JSON-RPC; LSP methods that take no parameters should sendparams: null(or omit). We always include it asnullto keep the wire layout uniform. - Response
- One JSON-RPC response. Either
resultorerroris set; never both. - Response
Error - JSON-RPC error envelope.
codeis one of the standard integers from the JSON-RPC spec (error_codes), a JSON-RPC implementation-defined server error (-32099 ..= -32000), or one of LSP’s own codes (-32899 ..= -32800);messageis human-readable;datais whatever the server attaches.
Enums§
- Message
- One incoming or outgoing JSON-RPC message. The codec yields
Messagefrom the wire; the actor matches on the variant. - Message
Decode Error - Decode failures from
Message::from_json. - Request
Id - Per JSON-RPC, an id is a string, a number, or null. LSP
servers in the wild send all three; clients (us) only emit
Number, but we accept all on the read path.
Constants§
- JSONRPC_
VERSION - JSON-RPC 2.0 protocol version literal. Always exactly this string on the wire; we preserve it as-is so a non-conforming server can be flagged early.