You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2026/07/29 17:11:22 UTC
(juneau) 04/19: feat: add JsonRpcResponse.notification/ok/errorResponse static helpers
This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git
commit e09b9fe29ac5349047eb88fa13d98d1c4de1edda
Author: James Bognar <ja...@apache.org>
AuthorDate: Tue Jul 28 16:14:55 2026 -0400
feat: add JsonRpcResponse.notification/ok/errorResponse static helpers
Ports the neutral response-construction helpers out of McpDispatcher's privates
onto the envelope bean itself. notification(Object) is a predicate, not a factory
(matching the original private static); errorResponse keeps its 4-arg form so the
internal-error path's error.data survives, with a 3-arg convenience overload.
Co-authored-by: Cursor <cu...@cursor.com>
---
.../juneau/bean/jsonrpc/JsonRpcResponse.java | 58 +++++++++++++
.../juneau/bean/jsonrpc/JsonRpcBeans_Test.java | 94 ++++++++++++++++++++++
2 files changed, 152 insertions(+)
diff --git a/juneau-bean/juneau-bean-jsonrpc/src/main/java/org/apache/juneau/bean/jsonrpc/JsonRpcResponse.java b/juneau-bean/juneau-bean-jsonrpc/src/main/java/org/apache/juneau/bean/jsonrpc/JsonRpcResponse.java
index 0e4a38628c..6fdf4b2f1a 100644
--- a/juneau-bean/juneau-bean-jsonrpc/src/main/java/org/apache/juneau/bean/jsonrpc/JsonRpcResponse.java
+++ b/juneau-bean/juneau-bean-jsonrpc/src/main/java/org/apache/juneau/bean/jsonrpc/JsonRpcResponse.java
@@ -27,6 +27,8 @@ import org.apache.juneau.marshall.*;
@Marshalled
public class JsonRpcResponse {
+ private static final String JSONRPC_2_0 = "2.0";
+
private String jsonrpc;
private Object id;
private Object result;
@@ -111,4 +113,60 @@ public class JsonRpcResponse {
error = value;
return this;
}
+
+ /**
+ * Tests whether a JSON-RPC id identifies a notification (a request that must not be answered).
+ *
+ * <p>
+ * A JSON-RPC request with no {@code id} is a notification; the server performs the work and returns
+ * no response body.
+ *
+ * @param id The request id. Can be <jk>null</jk>.
+ * @return <jk>true</jk> if the id is <jk>null</jk>.
+ */
+ public static boolean notification(Object id) {
+ return id == null;
+ }
+
+ /**
+ * Creates a JSON-RPC 2.0 success response.
+ *
+ * @param id The request id to correlate against. Can be <jk>null</jk>.
+ * @param result The result payload. Can be <jk>null</jk>.
+ * @return A new response object. Never <jk>null</jk>.
+ */
+ public static JsonRpcResponse ok(Object id, Object result) {
+ return new JsonRpcResponse()
+ .setJsonrpc(JSONRPC_2_0)
+ .setId(id)
+ .setResult(result);
+ }
+
+ /**
+ * Creates a JSON-RPC 2.0 error response with no structured error data.
+ *
+ * @param id The request id to correlate against. Can be <jk>null</jk>.
+ * @param code The JSON-RPC error code.
+ * @param message The error message. Can be <jk>null</jk>.
+ * @return A new response object. Never <jk>null</jk>.
+ */
+ public static JsonRpcResponse errorResponse(Object id, int code, String message) {
+ return errorResponse(id, code, message, null);
+ }
+
+ /**
+ * Creates a JSON-RPC 2.0 error response.
+ *
+ * @param id The request id to correlate against. Can be <jk>null</jk>.
+ * @param code The JSON-RPC error code.
+ * @param message The error message. Can be <jk>null</jk>.
+ * @param data Optional structured error data. Can be <jk>null</jk> to leave the property unset.
+ * @return A new response object. Never <jk>null</jk>.
+ */
+ public static JsonRpcResponse errorResponse(Object id, int code, String message, Object data) {
+ return new JsonRpcResponse()
+ .setJsonrpc(JSONRPC_2_0)
+ .setId(id)
+ .setError(new JsonRpcError().setCode(code).setMessage(message).setData(data));
+ }
}
diff --git a/juneau-bean/juneau-bean-jsonrpc/src/test/java/org/apache/juneau/bean/jsonrpc/JsonRpcBeans_Test.java b/juneau-bean/juneau-bean-jsonrpc/src/test/java/org/apache/juneau/bean/jsonrpc/JsonRpcBeans_Test.java
new file mode 100644
index 0000000000..2fe0997f13
--- /dev/null
+++ b/juneau-bean/juneau-bean-jsonrpc/src/test/java/org/apache/juneau/bean/jsonrpc/JsonRpcBeans_Test.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.bean.jsonrpc;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.apache.juneau.marshall.collections.*;
+import org.apache.juneau.marshall.json.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * Coverage for the JSON-RPC 2.0 envelope beans and {@link JsonRpcResponse}'s static helpers.
+ */
+class JsonRpcBeans_Test {
+
+ private static void assertRoundTrip(Object bean, Class<?> type) {
+ var a = JsonSerializer.DEFAULT.write(bean);
+ var b = JsonSerializer.DEFAULT.write(JsonParser.DEFAULT.read(a, type));
+ assertEquals(a, b, () -> "Round-trip JSON mismatch for " + type.getName() + ": " + a);
+ }
+
+ @Test
+ void a01_request_roundTrip() {
+ var a = new JsonRpcRequest().setJsonrpc("2.0").setId("abc").setMethod("tools/list").setParams(JsonMap.of("cursor", "1"));
+ assertRoundTrip(a, JsonRpcRequest.class);
+ }
+
+ @Test
+ void a02_response_resultRoundTrip() {
+ var a = new JsonRpcResponse().setJsonrpc("2.0").setId(42).setResult(JsonMap.of("x", 1));
+ assertRoundTrip(a, JsonRpcResponse.class);
+ }
+
+ @Test
+ void a03_response_errorRoundTrip() {
+ var a = new JsonRpcResponse().setJsonrpc("2.0").setId(7)
+ .setError(new JsonRpcError().setCode(-32600).setMessage("Invalid Request").setData(JsonMap.of("detail", "x")));
+ assertRoundTrip(a, JsonRpcResponse.class);
+ }
+
+ @Test
+ void a04_error_roundTrip() {
+ assertRoundTrip(new JsonRpcError().setCode(-32603).setMessage("boom").setData(JsonMap.of("type", "X")), JsonRpcError.class);
+ }
+
+ @Test
+ void b01_notification_isNullId() {
+ assertTrue(JsonRpcResponse.notification(null));
+ assertFalse(JsonRpcResponse.notification(1));
+ assertFalse(JsonRpcResponse.notification("abc"));
+ }
+
+ @Test
+ void b02_ok_setsVersionIdAndResult() {
+ var a = JsonRpcResponse.ok(1, JsonMap.of("k", "v"));
+ assertEquals("2.0", a.getJsonrpc());
+ assertEquals(1, a.getId());
+ assertNotNull(a.getResult());
+ assertNull(a.getError());
+ }
+
+ @Test
+ void b03_errorResponse_fourArg_carriesData() {
+ var a = JsonRpcResponse.errorResponse(1, -32603, "boom", JsonMap.of("type", "X"));
+ assertEquals("2.0", a.getJsonrpc());
+ assertEquals(1, a.getId());
+ assertNull(a.getResult());
+ assertEquals(-32603, a.getError().getCode());
+ assertEquals("boom", a.getError().getMessage());
+ assertNotNull(a.getError().getData());
+ }
+
+ @Test
+ void b04_errorResponse_threeArg_leavesDataNull() {
+ var a = JsonRpcResponse.errorResponse(null, -32600, "Request envelope is null");
+ assertNull(a.getId());
+ assertEquals(-32600, a.getError().getCode());
+ assertNull(a.getError().getData());
+ }
+}