Skip to main content

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) and JsonRpcResponse.errorResponse(id, code, message[, data]) build correctly-shaped responses (including the jsonrpc version token) in one call.
  • Fluent setters — Every bean uses setX(...) returning this for ergonomic construction.
  • No external dependencies — Depends only on juneau-marshall.
  • Zero protocol logic — Pure DTOs; no validation, no method dispatch, no transport.

Coverage​

BeanPurpose
JsonRpcRequestInbound JSON-RPC request envelope (jsonrpc, id, method, params).
JsonRpcResponseOutbound 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).
JsonRpcErrorJSON-RPC error structure (code, message, data).
McpExceptionRuntimeException 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);
  • juneau-bean-mcp-v20250618 — The Model Context Protocol 2025-06-18 wire 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-server and these beans.

Resources​