Skip to content

Commit 557849b

Browse files
committed
fix(#6): NullPointerException for tools that return void
1 parent 2994317 commit 557849b

File tree

4 files changed

+83
-27
lines changed

4 files changed

+83
-27
lines changed

src/main/java/com/github/codeboyzhou/mcp/declarative/server/component/McpServerTool.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ public McpSchema.CallToolResult invoke(
9393
result = e + ": " + e.getMessage();
9494
isError = true;
9595
}
96-
McpSchema.Content content = new McpSchema.TextContent(result.toString());
96+
final String text = result == null ? "This tool returned nullable or void" : result.toString();
97+
McpSchema.Content content = new McpSchema.TextContent(text);
9798
return new McpSchema.CallToolResult(List.of(content), isError);
9899
}
99100

src/test/java/com/github/codeboyzhou/mcp/declarative/McpServersTest.java

Lines changed: 60 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
44
import static org.junit.jupiter.api.Assertions.assertEquals;
55
import static org.junit.jupiter.api.Assertions.assertFalse;
6+
import static org.junit.jupiter.api.Assertions.assertNotNull;
67
import static org.junit.jupiter.api.Assertions.assertSame;
78
import static org.junit.jupiter.api.Assertions.assertThrows;
89

@@ -184,19 +185,43 @@ private void verifyPromptsRegistered(McpSyncClient client) {
184185
List<McpSchema.Prompt> prompts = client.listPrompts().prompts();
185186
assertEquals(2, prompts.size());
186187

187-
McpSchema.Prompt prompt = prompts.get(0);
188-
assertEquals("prompt1_name", prompt.name());
189-
assertEquals("prompt1_title", prompt.title());
190-
assertEquals("prompt1_description", prompt.description());
191-
192-
List<McpSchema.PromptArgument> arguments = prompt.arguments();
193-
assertEquals(2, arguments.size());
194-
assertEquals("param1", arguments.get(0).name());
195-
assertEquals("param1_title", arguments.get(0).title());
196-
assertEquals("param1_description", arguments.get(0).description());
197-
assertEquals("param2", arguments.get(1).name());
198-
assertEquals("param2_title", arguments.get(1).title());
199-
assertEquals("param2_description", arguments.get(1).description());
188+
McpSchema.Prompt prompt1 =
189+
prompts.stream()
190+
.filter(prompt -> prompt.name().equals("prompt1_name"))
191+
.findAny()
192+
.orElse(null);
193+
assertNotNull(prompt1);
194+
assertEquals("prompt1_name", prompt1.name());
195+
assertEquals("prompt1_title", prompt1.title());
196+
assertEquals("prompt1_description", prompt1.description());
197+
198+
List<McpSchema.PromptArgument> arguments1 = prompt1.arguments();
199+
assertEquals(2, arguments1.size());
200+
assertEquals("param1", arguments1.get(0).name());
201+
assertEquals("param1_title", arguments1.get(0).title());
202+
assertEquals("param1_description", arguments1.get(0).description());
203+
assertEquals("param2", arguments1.get(1).name());
204+
assertEquals("param2_title", arguments1.get(1).title());
205+
assertEquals("param2_description", arguments1.get(1).description());
206+
207+
McpSchema.Prompt prompt2 =
208+
prompts.stream()
209+
.filter(prompt -> prompt.name().equals("prompt2_name"))
210+
.findAny()
211+
.orElse(null);
212+
assertNotNull(prompt2);
213+
assertEquals("prompt2_name", prompt2.name());
214+
assertEquals("prompt2_title", prompt2.title());
215+
assertEquals("prompt2_description", prompt2.description());
216+
217+
List<McpSchema.PromptArgument> arguments2 = prompt2.arguments();
218+
assertEquals(2, arguments2.size());
219+
assertEquals("param1", arguments2.get(0).name());
220+
assertEquals("param1_title", arguments2.get(0).title());
221+
assertEquals("param1_description", arguments2.get(0).description());
222+
assertEquals("param2", arguments2.get(1).name());
223+
assertEquals("param2_title", arguments2.get(1).title());
224+
assertEquals("param2_description", arguments2.get(1).description());
200225
}
201226

202227
private void verifyPromptsCalled(McpSyncClient client) {
@@ -219,17 +244,28 @@ private void verifyPromptsCalled(McpSyncClient client) {
219244

220245
private void verifyToolsRegistered(McpSyncClient client) {
221246
List<McpSchema.Tool> tools = client.listTools().tools();
222-
assertEquals(2, tools.size());
247+
assertEquals(3, tools.size());
223248

224-
McpSchema.Tool tool1 = tools.get(0);
249+
McpSchema.Tool tool1 =
250+
tools.stream().filter(tool -> tool.name().equals("tool1_name")).findAny().orElse(null);
251+
assertNotNull(tool1);
225252
assertEquals("tool1_name", tool1.name());
226253
assertEquals("tool1_title", tool1.title());
227254
assertEquals("tool1_description", tool1.description());
228255

229-
McpSchema.Tool tool2 = tools.get(1);
256+
McpSchema.Tool tool2 =
257+
tools.stream().filter(tool -> tool.name().equals("tool2_name")).findAny().orElse(null);
258+
assertNotNull(tool2);
230259
assertEquals("tool2_name", tool2.name());
231260
assertEquals("tool2_title", tool2.title());
232261
assertEquals("tool2_description", tool2.description());
262+
263+
McpSchema.Tool tool3 =
264+
tools.stream().filter(tool -> tool.name().equals("tool3_name")).findAny().orElse(null);
265+
assertNotNull(tool3);
266+
assertEquals("tool3_name", tool3.name());
267+
assertEquals("tool3_title", tool3.title());
268+
assertEquals("tool3_description", tool3.description());
233269
}
234270

235271
private void verifyToolsCalled(McpSyncClient client) {
@@ -248,5 +284,13 @@ private void verifyToolsCalled(McpSyncClient client) {
248284
McpSchema.TextContent content2 = (McpSchema.TextContent) result2.content().get(0);
249285
assertFalse(result2.isError());
250286
assertEquals("tool2 is called", content2.text());
287+
288+
String name3 = "tool3_name";
289+
Map<String, Object> args3 = Map.of("param1", "value1", "param2", "value2");
290+
McpSchema.CallToolRequest request3 = new McpSchema.CallToolRequest(name3, args3);
291+
McpSchema.CallToolResult result3 = client.callTool(request3);
292+
McpSchema.TextContent content3 = (McpSchema.TextContent) result3.content().get(0);
293+
assertFalse(result3.isError());
294+
assertEquals("This tool returned nullable or void", content3.text());
251295
}
252296
}

src/test/java/com/github/codeboyzhou/mcp/declarative/test/TestMcpPrompts.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public String prompt1(
1616
@McpPromptParam(name = "param2", title = "param2_title", description = "param2_description")
1717
String param2,
1818
String param3) {
19+
1920
log.debug("prompt1 called with params: {}, {}, {}", param1, param2, param3);
2021
return "prompt1 is called";
2122
}
@@ -27,6 +28,7 @@ public String prompt2(
2728
@McpPromptParam(name = "param2", title = "param2_title", description = "param2_description")
2829
String param2,
2930
String param3) {
31+
3032
log.debug("prompt2 called with params: {}, {}, {}", param1, param2, param3);
3133
return "prompt2 is called";
3234
}

src/test/java/com/github/codeboyzhou/mcp/declarative/test/TestMcpTools.java

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,30 @@ public class TestMcpTools {
1111

1212
@McpTool(name = "tool1_name", title = "tool1_title", description = "tool1_description")
1313
public String tool1(
14-
@McpToolParam(name = "param1", description = "param1_description") String param1,
15-
@McpToolParam(name = "param2", description = "param2_description", required = true)
16-
String param2,
17-
String param3) {
18-
log.debug("tool1 called with params: {}, {}, {}", param1, param2, param3);
14+
@McpToolParam(name = "param1", description = "param1_description") String p1,
15+
@McpToolParam(name = "param2", description = "param2_description", required = true) String p2,
16+
String p3) {
17+
18+
log.debug("tool1 called with params: {}, {}, {}", p1, p2, p3);
1919
return "tool1 is called";
2020
}
2121

2222
@McpTool(name = "tool2_name", title = "tool2_title", description = "tool2_description")
2323
public String tool2(
24-
@McpToolParam(name = "param1", description = "param1_description") String param1,
25-
@McpToolParam(name = "param2", description = "param2_description", required = true)
26-
String param2,
27-
String param3) {
28-
log.debug("tool2 called with params: {}, {}, {}", param1, param2, param3);
24+
@McpToolParam(name = "param1", description = "param1_description") String p1,
25+
@McpToolParam(name = "param2", description = "param2_description", required = true) String p2,
26+
String p3) {
27+
28+
log.debug("tool2 called with params: {}, {}, {}", p1, p2, p3);
2929
return "tool2 is called";
3030
}
31+
32+
@McpTool(name = "tool3_name", title = "tool3_title", description = "tool3_description")
33+
public void tool3(
34+
@McpToolParam(name = "param1", description = "param1_description") String p1,
35+
@McpToolParam(name = "param2", description = "param2_description", required = true) String p2,
36+
String p3) {
37+
38+
log.debug("tool3 called with params: {}, {}, {}, and no return value", p1, p2, p3);
39+
}
3140
}

0 commit comments

Comments
 (0)