Wiki source code of Test an MCP Tool

Last modified by Vincent Massol on 2026/09/04 19:20

Hide last authors
Vincent Massol 2.1 1 Write the unit tests of an MCP tool with the server module's own test base classes, which call the tool the way the server does and pin the text it answers with (see [[MCP Tool API>>doc:documentation.extensions.dev.llm.mcp-server.mcp-tool-api.WebHome]] for the interface under test).
2
3 1. Extend {{code}}AbstractMCPToolTest{{/code}} and carry the component annotations on your own class: the base class is deliberately unannotated, so the concrete test decides between {{code}}@ComponentTest{{/code}} and {{code}}@OldcoreTest{{/code}}.(((
4 {{code language="java"}}
5 @ComponentTest
6 class MyToolTest extends AbstractMCPToolTest
7 {
8 @InjectMockComponents
9 private MyTool tool;
10
11 @Override
12 protected MCPTool getTool()
13 {
14 return this.tool;
15 }
16 }
17 {{/code}}
18 )))
19 1. Call the tool through {{code}}call{{/code}} or {{code}}callText{{/code}}, which build the {{code}}CallToolRequest{{/code}} from the tool's own advertised name, so a renamed tool cannot leave its test calling the old one.(((
20 {{code language="java"}}
21 String text = callText(Map.of("reference", "Sandbox.WebHome"));
22 {{/code}}
23 )))
24 1. Assert the exact text the tool answers with. The message is the contract with the calling agent, so a changed message changes a test, deliberately.(((
25 {{code language="java"}}
26 assertEquals("Access denied to Sandbox.WebHome.", callText(Map.of("reference", "Sandbox.WebHome")));
27 {{/code}}
28 )))
29 1. Extend {{code}}AbstractMCPWriteToolTest{{/code}} instead where the tool writes. It adds the mocked access doors, the stored-document helpers, the version helpers and the failure builders that reproduce a save race, and {{code}}verifyNothingSaved{{/code}} for the refusal paths.(((
30 {{code language="java"}}
31 @OldcoreTest
32 class MyWriteToolTest extends AbstractMCPWriteToolTest
33 {
34 private static final DocumentReference REFERENCE =
35 new DocumentReference("xwiki", "Sandbox", "WebHome");
36
37 @Test
38 void refusesAStaleBaseVersion(MockitoOldcore oldcore) throws Exception
39 {
40 storeDocument(oldcore, REFERENCE, "content", null);
41 assertTrue(callText(Map.of("reference", "Sandbox.WebHome", "base_version", "1.0"))
42 .startsWith("Version conflict: "));
43 verifyNothingSaved(oldcore);
44 }
45 }
46 {{/code}}
47 )))
48 1. Assert the advertised schema, not only the behaviour: {{code}}referenceDescription(){{/code}} reads the description the tool advertises for its {{code}}reference{{/code}} parameter, which is how a schema that stopped matching the parsing is caught.
49 1. Run the module's tests and read the coverage gate, which fails the build below the module's floor.(((
50 {{code}}
51 mvn -B -ntp test -pl application-ai-llm-mcp/application-ai-llm-mcp-server -Dtest=MyToolTest
52 {{/code}}
53 )))

Get Connected