Skip to content

Commit af2f6e5

Browse files
committed
improve the json rpc error content
Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
1 parent a360b7d commit af2f6e5

File tree

5 files changed

+92
-44
lines changed

5 files changed

+92
-44
lines changed

mcp-core/src/main/java/io/modelcontextprotocol/spec/McpError.java

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,12 @@ public JSONRPCError getJsonRpcError() {
3838

3939
@Override
4040
public String toString() {
41-
var message = super.toString();
41+
var builder = new StringBuilder(super.toString());
4242
if (jsonRpcError != null) {
43-
return message + jsonRpcError.toString();
43+
builder.append("\n");
44+
builder.append(jsonRpcError.toString());
4445
}
45-
return message;
46+
return builder.toString();
4647
}
4748

4849
public static Builder builder(int errorCode) {
@@ -87,4 +88,29 @@ public static Throwable findRootCause(Throwable throwable) {
8788
return rootCause;
8889
}
8990

91+
public static String aggregateExceptionMessages(Throwable throwable) {
92+
Assert.notNull(throwable, "throwable must not be null");
93+
94+
StringBuilder messages = new StringBuilder();
95+
Throwable current = throwable;
96+
97+
while (current != null) {
98+
if (messages.length() > 0) {
99+
messages.append("\n Caused by: ");
100+
}
101+
102+
messages.append(current.getClass().getSimpleName());
103+
if (current.getMessage() != null) {
104+
messages.append(": ").append(current.getMessage());
105+
}
106+
107+
if (current.getCause() == current) {
108+
break;
109+
}
110+
current = current.getCause();
111+
}
112+
113+
return messages.toString();
114+
}
115+
90116
}

mcp-core/src/main/java/io/modelcontextprotocol/spec/McpServerSession.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ else if (message instanceof McpSchema.JSONRPCRequest request) {
227227
McpSchema.JSONRPCResponse.JSONRPCError jsonRpcError = (error instanceof McpError mcpError
228228
&& mcpError.getJsonRpcError() != null) ? mcpError.getJsonRpcError()
229229
: new McpSchema.JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.INTERNAL_ERROR,
230-
McpError.findRootCause(error).getMessage(), null);
230+
error.getMessage(), McpError.aggregateExceptionMessages(error));
231231
var errorResponse = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), null,
232232
jsonRpcError);
233233
// TODO: Should the error go to SSE or back as POST return?
@@ -290,7 +290,7 @@ private Mono<McpSchema.JSONRPCResponse> handleIncomingRequest(McpSchema.JSONRPCR
290290
&& mcpError.getJsonRpcError() != null) ? mcpError.getJsonRpcError()
291291
// TODO: add error message through the data field
292292
: new McpSchema.JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.INTERNAL_ERROR,
293-
McpError.findRootCause(error).getMessage(), null);
293+
error.getMessage(), McpError.aggregateExceptionMessages(error));
294294
return Mono.just(
295295
new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), null, jsonRpcError));
296296
});

mcp-core/src/main/java/io/modelcontextprotocol/spec/McpStreamableServerSession.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ public Mono<Void> responseStream(McpSchema.JSONRPCRequest jsonrpcRequest, McpStr
182182
McpSchema.JSONRPCResponse.JSONRPCError jsonRpcError = (e instanceof McpError mcpError
183183
&& mcpError.getJsonRpcError() != null) ? mcpError.getJsonRpcError()
184184
: new McpSchema.JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.INTERNAL_ERROR,
185-
McpError.findRootCause(e).getMessage(), null);
185+
e.getMessage(), McpError.aggregateExceptionMessages(e));
186186

187187
var errorResponse = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, jsonrpcRequest.id(),
188188
null, jsonRpcError);

mcp-core/src/test/java/io/modelcontextprotocol/server/AbstractMcpClientServerIntegrationTests.java

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,9 @@ void testCreateMessageSuccess(String clientType) {
146146
CreateMessageResult.StopReason.STOP_SEQUENCE);
147147
};
148148

149-
CallToolResult callResponse = new McpSchema.CallToolResult(List.of(new McpSchema.TextContent("CALL RESPONSE")),
150-
null);
149+
CallToolResult callResponse = McpSchema.CallToolResult.builder()
150+
.addContent(new McpSchema.TextContent("CALL RESPONSE"))
151+
.build();
151152

152153
AtomicReference<CreateMessageResult> samplingResult = new AtomicReference<>();
153154

@@ -224,8 +225,9 @@ void testCreateMessageWithRequestTimeoutSuccess(String clientType) throws Interr
224225

225226
// Server
226227

227-
CallToolResult callResponse = new McpSchema.CallToolResult(List.of(new McpSchema.TextContent("CALL RESPONSE")),
228-
null);
228+
CallToolResult callResponse = McpSchema.CallToolResult.builder()
229+
.addContent(new McpSchema.TextContent("CALL RESPONSE"))
230+
.build();
229231

230232
AtomicReference<CreateMessageResult> samplingResult = new AtomicReference<>();
231233

@@ -300,8 +302,9 @@ void testCreateMessageWithRequestTimeoutFail(String clientType) throws Interrupt
300302
CreateMessageResult.StopReason.STOP_SEQUENCE);
301303
};
302304

303-
CallToolResult callResponse = new McpSchema.CallToolResult(List.of(new McpSchema.TextContent("CALL RESPONSE")),
304-
null);
305+
CallToolResult callResponse = McpSchema.CallToolResult.builder()
306+
.addContent(new McpSchema.TextContent("CALL RESPONSE"))
307+
.build();
305308

306309
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
307310
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
@@ -393,8 +396,9 @@ void testCreateElicitationSuccess(String clientType) {
393396
Map.of("message", request.message()));
394397
};
395398

396-
CallToolResult callResponse = new McpSchema.CallToolResult(List.of(new McpSchema.TextContent("CALL RESPONSE")),
397-
null);
399+
CallToolResult callResponse = McpSchema.CallToolResult.builder()
400+
.addContent(new McpSchema.TextContent("CALL RESPONSE"))
401+
.build();
398402

399403
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
400404
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
@@ -448,8 +452,9 @@ void testCreateElicitationWithRequestTimeoutSuccess(String clientType) {
448452
return new ElicitResult(ElicitResult.Action.ACCEPT, Map.of("message", request.message()));
449453
};
450454

451-
CallToolResult callResponse = new McpSchema.CallToolResult(List.of(new McpSchema.TextContent("CALL RESPONSE")),
452-
null);
455+
CallToolResult callResponse = McpSchema.CallToolResult.builder()
456+
.addContent(new McpSchema.TextContent("CALL RESPONSE"))
457+
.build();
453458

454459
AtomicReference<ElicitResult> resultRef = new AtomicReference<>();
455460

@@ -520,7 +525,7 @@ void testCreateElicitationWithRequestTimeoutFail(String clientType) {
520525
return new ElicitResult(ElicitResult.Action.ACCEPT, Map.of("message", request.message()));
521526
};
522527

523-
CallToolResult callResponse = new CallToolResult(List.of(new McpSchema.TextContent("CALL RESPONSE")), null);
528+
CallToolResult callResponse = CallToolResult.builder().addContent(new TextContent("CALL RESPONSE")).build();
524529

525530
AtomicReference<ElicitResult> resultRef = new AtomicReference<>();
526531

@@ -761,7 +766,9 @@ void testToolCallSuccess(String clientType) {
761766
var clientBuilder = clientBuilders.get(clientType);
762767

763768
var responseBodyIsNullOrBlank = new AtomicBoolean(false);
764-
var callResponse = new McpSchema.CallToolResult(List.of(new McpSchema.TextContent("CALL RESPONSE")), null);
769+
var callResponse = McpSchema.CallToolResult.builder()
770+
.addContent(new McpSchema.TextContent("CALL RESPONSE; ctx=importantValue"))
771+
.build();
765772
McpServerFeatures.SyncToolSpecification tool1 = McpServerFeatures.SyncToolSpecification.builder()
766773
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
767774
.callHandler((exchange, request) -> {
@@ -832,8 +839,7 @@ void testThrowingToolCallIsCaughtBeforeTimeout(String clientType) {
832839
assertThat(initResult).isNotNull();
833840

834841
// We expect the tool call to fail immediately with the exception raised by
835-
// the offending tool
836-
// instead of getting back a timeout.
842+
// the offending tool instead of getting back a timeout.
837843
assertThatExceptionOfType(McpError.class)
838844
.isThrownBy(() -> mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of())))
839845
.withMessageContaining("Timeout on blocking read");
@@ -853,8 +859,9 @@ void testToolCallSuccessWithTranportContextExtraction(String clientType) {
853859
var transportContextIsEmpty = new AtomicBoolean(false);
854860
var responseBodyIsNullOrBlank = new AtomicBoolean(false);
855861

856-
var expectedCallResponse = new McpSchema.CallToolResult(
857-
List.of(new McpSchema.TextContent("CALL RESPONSE; ctx=value")), null);
862+
var expectedCallResponse = McpSchema.CallToolResult.builder()
863+
.addContent(new McpSchema.TextContent("CALL RESPONSE; ctx=value"))
864+
.build();
858865
McpServerFeatures.SyncToolSpecification tool1 = McpServerFeatures.SyncToolSpecification.builder()
859866
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
860867
.callHandler((exchange, request) -> {
@@ -872,8 +879,9 @@ void testToolCallSuccessWithTranportContextExtraction(String clientType) {
872879
e.printStackTrace();
873880
}
874881

875-
return new McpSchema.CallToolResult(
876-
List.of(new McpSchema.TextContent("CALL RESPONSE; ctx=" + ctxValue)), null);
882+
return McpSchema.CallToolResult.builder()
883+
.addContent(new McpSchema.TextContent("CALL RESPONSE; ctx=" + ctxValue))
884+
.build();
877885
})
878886
.build();
879887

@@ -906,7 +914,10 @@ void testToolListChangeHandlingSuccess(String clientType) {
906914

907915
var clientBuilder = clientBuilders.get(clientType);
908916

909-
var callResponse = new McpSchema.CallToolResult(List.of(new McpSchema.TextContent("CALL RESPONSE")), null);
917+
var callResponse = McpSchema.CallToolResult.builder()
918+
.addContent(new McpSchema.TextContent("CALL RESPONSE"))
919+
.build();
920+
910921
McpServerFeatures.SyncToolSpecification tool1 = McpServerFeatures.SyncToolSpecification.builder()
911922
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
912923
.callHandler((exchange, request) -> {

mcp-test/src/main/java/io/modelcontextprotocol/AbstractMcpClientServerIntegrationTests.java

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,9 @@ void testCreateMessageSuccess(String clientType) {
150150
CreateMessageResult.StopReason.STOP_SEQUENCE);
151151
};
152152

153-
CallToolResult callResponse = new McpSchema.CallToolResult(List.of(new McpSchema.TextContent("CALL RESPONSE")),
154-
null);
153+
CallToolResult callResponse = McpSchema.CallToolResult.builder()
154+
.addContent(new McpSchema.TextContent("CALL RESPONSE"))
155+
.build();
155156

156157
AtomicReference<CreateMessageResult> samplingResult = new AtomicReference<>();
157158

@@ -228,8 +229,9 @@ void testCreateMessageWithRequestTimeoutSuccess(String clientType) throws Interr
228229

229230
// Server
230231

231-
CallToolResult callResponse = new McpSchema.CallToolResult(List.of(new McpSchema.TextContent("CALL RESPONSE")),
232-
null);
232+
CallToolResult callResponse = McpSchema.CallToolResult.builder()
233+
.addContent(new McpSchema.TextContent("CALL RESPONSE"))
234+
.build();
233235

234236
AtomicReference<CreateMessageResult> samplingResult = new AtomicReference<>();
235237

@@ -304,8 +306,9 @@ void testCreateMessageWithRequestTimeoutFail(String clientType) throws Interrupt
304306
CreateMessageResult.StopReason.STOP_SEQUENCE);
305307
};
306308

307-
CallToolResult callResponse = new McpSchema.CallToolResult(List.of(new McpSchema.TextContent("CALL RESPONSE")),
308-
null);
309+
CallToolResult callResponse = McpSchema.CallToolResult.builder()
310+
.addContent(new McpSchema.TextContent("CALL RESPONSE"))
311+
.build();
309312

310313
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
311314
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
@@ -397,8 +400,9 @@ void testCreateElicitationSuccess(String clientType) {
397400
Map.of("message", request.message()));
398401
};
399402

400-
CallToolResult callResponse = new McpSchema.CallToolResult(List.of(new McpSchema.TextContent("CALL RESPONSE")),
401-
null);
403+
CallToolResult callResponse = McpSchema.CallToolResult.builder()
404+
.addContent(new McpSchema.TextContent("CALL RESPONSE"))
405+
.build();
402406

403407
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
404408
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
@@ -452,8 +456,9 @@ void testCreateElicitationWithRequestTimeoutSuccess(String clientType) {
452456
return new ElicitResult(ElicitResult.Action.ACCEPT, Map.of("message", request.message()));
453457
};
454458

455-
CallToolResult callResponse = new McpSchema.CallToolResult(List.of(new McpSchema.TextContent("CALL RESPONSE")),
456-
null);
459+
CallToolResult callResponse = McpSchema.CallToolResult.builder()
460+
.addContent(new McpSchema.TextContent("CALL RESPONSE"))
461+
.build();
457462

458463
AtomicReference<ElicitResult> resultRef = new AtomicReference<>();
459464

@@ -524,7 +529,7 @@ void testCreateElicitationWithRequestTimeoutFail(String clientType) {
524529
return new ElicitResult(ElicitResult.Action.ACCEPT, Map.of("message", request.message()));
525530
};
526531

527-
CallToolResult callResponse = new CallToolResult(List.of(new McpSchema.TextContent("CALL RESPONSE")), null);
532+
CallToolResult callResponse = CallToolResult.builder().addContent(new TextContent("CALL RESPONSE")).build();
528533

529534
AtomicReference<ElicitResult> resultRef = new AtomicReference<>();
530535

@@ -765,7 +770,9 @@ void testToolCallSuccess(String clientType) {
765770
var clientBuilder = clientBuilders.get(clientType);
766771

767772
var responseBodyIsNullOrBlank = new AtomicBoolean(false);
768-
var callResponse = new McpSchema.CallToolResult(List.of(new McpSchema.TextContent("CALL RESPONSE")), null);
773+
var callResponse = McpSchema.CallToolResult.builder()
774+
.addContent(new McpSchema.TextContent("CALL RESPONSE; ctx=importantValue"))
775+
.build();
769776
McpServerFeatures.SyncToolSpecification tool1 = McpServerFeatures.SyncToolSpecification.builder()
770777
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
771778
.callHandler((exchange, request) -> {
@@ -836,8 +843,7 @@ void testThrowingToolCallIsCaughtBeforeTimeout(String clientType) {
836843
assertThat(initResult).isNotNull();
837844

838845
// We expect the tool call to fail immediately with the exception raised by
839-
// the offending tool
840-
// instead of getting back a timeout.
846+
// the offending tool instead of getting back a timeout.
841847
assertThatExceptionOfType(McpError.class)
842848
.isThrownBy(() -> mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of())))
843849
.withMessageContaining("Timeout on blocking read");
@@ -857,8 +863,9 @@ void testToolCallSuccessWithTranportContextExtraction(String clientType) {
857863
var transportContextIsEmpty = new AtomicBoolean(false);
858864
var responseBodyIsNullOrBlank = new AtomicBoolean(false);
859865

860-
var expectedCallResponse = new McpSchema.CallToolResult(
861-
List.of(new McpSchema.TextContent("CALL RESPONSE; ctx=value")), null);
866+
var expectedCallResponse = McpSchema.CallToolResult.builder()
867+
.addContent(new McpSchema.TextContent("CALL RESPONSE; ctx=value"))
868+
.build();
862869
McpServerFeatures.SyncToolSpecification tool1 = McpServerFeatures.SyncToolSpecification.builder()
863870
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
864871
.callHandler((exchange, request) -> {
@@ -876,8 +883,9 @@ void testToolCallSuccessWithTranportContextExtraction(String clientType) {
876883
e.printStackTrace();
877884
}
878885

879-
return new McpSchema.CallToolResult(
880-
List.of(new McpSchema.TextContent("CALL RESPONSE; ctx=" + ctxValue)), null);
886+
return McpSchema.CallToolResult.builder()
887+
.addContent(new McpSchema.TextContent("CALL RESPONSE; ctx=" + ctxValue))
888+
.build();
881889
})
882890
.build();
883891

@@ -910,7 +918,10 @@ void testToolListChangeHandlingSuccess(String clientType) {
910918

911919
var clientBuilder = clientBuilders.get(clientType);
912920

913-
var callResponse = new McpSchema.CallToolResult(List.of(new McpSchema.TextContent("CALL RESPONSE")), null);
921+
var callResponse = McpSchema.CallToolResult.builder()
922+
.addContent(new McpSchema.TextContent("CALL RESPONSE"))
923+
.build();
924+
914925
McpServerFeatures.SyncToolSpecification tool1 = McpServerFeatures.SyncToolSpecification.builder()
915926
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
916927
.callHandler((exchange, request) -> {

0 commit comments

Comments
 (0)