juneau-bean-jsonrpc
The juneau-bean-jsonrpc module provides Java beans modelling the revision-neutral JSON-RPC 2.0 envelope: request/response/error framing plus a runtime exception for handler-side error propagation. It carries no protocol-specific knowledge and depends only on juneau-marshall, making it the shared framing layer beneath protocol-specific bean modules such as juneau-bean-mcp-v20250618.
Overview
JSON-RPC 2.0 is a stateless, lightweight remote procedure call protocol encoded as JSON. This module ships the envelope shapes needed to read and write JSON-RPC requests and responses, without any knowledge of what methods exist or what their parameters mean — that's left to higher-level modules like juneau-bean-mcp-v20250618.
The beans are Juneau @Marshalled-annotated POJOs, so they round-trip through any Juneau serializer/parser (JSON, JSON5, MessagePack, CBOR, YAML, and more).
Key Features
- Round-trip serialization — Every bean parses back from its serialized form into an identical object across JSON, JSON5, MessagePack, CBOR, YAML, and other Juneau formats.
- Static factories —
JsonRpcResponse.ok(id, result)andJsonRpcResponse.errorResponse(id, code, message[, data])build correctly-shaped responses (including thejsonrpcversion token) in one call. - Fluent setters — Every bean uses
setX(...)returningthisfor ergonomic construction. - No external dependencies — Depends only on
juneau-marshall. - Zero protocol logic — Pure DTOs; no validation, no method dispatch, no transport.
Coverage
| Bean | Purpose |
|---|---|
JsonRpcRequest | Inbound JSON-RPC request envelope (jsonrpc, id, method, params). |
JsonRpcResponse | Outbound JSON-RPC response envelope (jsonrpc, id, result, error). Also carries three public static helpers used by dispatch code: ok(id, result) and errorResponse(id, code, message[, data]) (response factories, which set the jsonrpc version token for you), plus notification(id) (a boolean predicate testing id == null). |
JsonRpcError | JSON-RPC error structure (code, message, data). |
McpException | RuntimeException carrying JSON-RPC error fields (code, data) for handler-side propagation; toJsonRpcError() converts it to a JsonRpcError. |
This module carries no MCP-specific knowledge — it's the shared framing layer beneath protocol-specific bean modules such as juneau-bean-mcp-v20250618.
Basic Usage
Building a JSON-RPC request
import org.apache.juneau.bean.jsonrpc.*;
import org.apache.juneau.marshall.collections.*;
import org.apache.juneau.marshall.json.*;
JsonRpcRequest req = new JsonRpcRequest()
.setJsonrpc("2.0")
.setId(1)
.setMethod("tools/call")
.setParams(JsonMap.of(
"name", "echo",
"arguments", JsonMap.of("text", "hello")
));
String wire = JsonSerializer.DEFAULT.write(req);
JsonRpcRequest back = JsonParser.DEFAULT.read(wire, JsonRpcRequest.class);
Building success and error responses
JsonRpcResponse.ok(...) and JsonRpcResponse.errorResponse(...) set the jsonrpc version token automatically:
import org.apache.juneau.bean.jsonrpc.*;
import org.apache.juneau.marshall.collections.*;
import org.apache.juneau.marshall.json.*;
JsonRpcResponse success = JsonRpcResponse.ok(1, JsonMap.of("echoed", "hello"));
String wire = JsonSerializer.DEFAULT.write(success);
// {"jsonrpc":"2.0","id":1,"result":{"echoed":"hello"}}
JsonRpcResponse failure = JsonRpcResponse.errorResponse(1, -32602, "Invalid arguments", JsonMap.of("field", "name"));
// {"jsonrpc":"2.0","id":1,"error":{"code":-32602,"message":"Invalid arguments","data":{"field":"name"}}}
Detecting notifications
A JSON-RPC request with no id is a notification — the server performs the work and returns no response body. JsonRpcResponse.notification(id) tests for this:
if (JsonRpcResponse.notification(req.getId())) {
// no response should be sent
}
Mapping handler errors to JSON-RPC errors
McpException is a RuntimeException carrying JSON-RPC code, message, and optional data. Handlers can throw it directly; calling code can convert via toJsonRpcError():
import org.apache.juneau.bean.jsonrpc.*;
import org.apache.juneau.marshall.collections.*;
try {
throw new McpException(-32602, "Invalid arguments", JsonMap.of("field", "name"));
} catch (McpException e) {
JsonRpcResponse resp = new JsonRpcResponse()
.setJsonrpc("2.0")
.setId(1)
.setError(e.toJsonRpcError());
}
Or, more directly, via the static factory on JsonRpcResponse itself:
JsonRpcResponse resp = JsonRpcResponse.errorResponse(1, -32602, "Invalid arguments", JsonMap.of("field", "name"));
Multiple Wire Formats
Because the beans are pure POJOs with @Marshalled annotations, every Juneau serializer/parser works:
import org.apache.juneau.bean.jsonrpc.*;
import org.apache.juneau.marshall.cbor.*;
import org.apache.juneau.marshall.marshaller.*;
import org.apache.juneau.marshall.msgpack.*;
JsonRpcResponse resp = JsonRpcResponse.ok(1, "pong");
byte[] cbor = Cbor.of(resp);
byte[] msgpack = MsgPackSerializer.DEFAULT.write(resp);
String yaml = Yaml.of(resp);
Related Modules
- juneau-bean-mcp-v20250618 — The Model Context Protocol
2025-06-18wire beans, which build their JSON-RPC traffic on top of this envelope. - juneau-rest-server-mcp — A stateless JSON-RPC REST-server endpoint built on
juneau-rest-serverand these beans.