Wiki source code of MCPToolSupport

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

Show last authors
1 {{code}}MCPToolSupport{{/code}} ({{code}}org.xwiki.contrib.llm.mcp.MCPToolSupport{{/code}}, annotated {{code}}@Unstable{{/code}}) is the declarative parameter layer of the MCP server. A tool declares its parameters once and gets back both the advertised MCP input schema and the typed accessors that read them, so the schema and the parsing code cannot drift apart.
2
3 An instance is created with {{code}}MCPToolSupport.builder(){{/code}} and held in a {{code}}static final{{/code}} field, which puts the failure mode of a broken declaration at class initialization.
4
5 {{code language="java"}}
6 private static final MCPToolSupport PARAMS = MCPToolSupport.builder()
7 .requiredString("reference", "The document to read, e.g. \"Sandbox.WebHome\".")
8 .string("locale", "Read a translation, e.g. " + MCPToolSupport.LOCALE_FORMS + ".")
9 .integer("limit", "Revisions per page (default 20, max 100).")
10 .bool("showHidden", "Include hidden pages (default false).")
11 .build();
12 {{/code}}
13
14 == Declaring parameters ==
15
16 Each builder method declares one flat parameter and its agent-facing description. Declaration order is preserved, so the advertised schema lists the parameters in the order the tool chose.
17
18 |=Method |=Declares
19 |{{code}}string(name, description){{/code}} |An optional string
20 |{{code}}requiredString(name, description){{/code}} |A required string
21 |{{code}}stringIf(condition, name, description){{/code}} |An optional string, only when the condition holds
22 |{{code}}integer(name, description){{/code}} |An optional integer
23 |{{code}}requiredInteger(name, description){{/code}} |A required integer
24 |{{code}}bool(name, description){{/code}} |An optional boolean
25 |{{code}}stringArray(name, description){{/code}} |An optional flat array of strings
26 |{{code}}stringMap(name, description){{/code}} |An optional flat object whose values are strings
27 |{{code}}requiredStringMap(name, description){{/code}} |The same, required
28 |{{code}}build(){{/code}} |The finished parameter set
29
30 Nested object parameters are out of scope by design: a tool declares its scalars, flat string arrays and flat string maps here, and merges any bespoke schema part through {{code}}inputSchema(Map){{/code}}.
31
32 == Generating the schema ==
33
34 |=Method |=Returns
35 |{{code}}inputSchema(){{/code}} |The JSON Schema 2020-12 object map that {{code}}McpSchema.Tool.builder(String, Map){{/code}} expects
36 |{{code}}inputSchema(Map extraProperties){{/code}} |The same, merged with hand-built properties, for the rare non-scalar parameter
37
38 {{code language="java"}}
39 @Override
40 public McpSchema.Tool getToolDefinition()
41 {
42 return McpSchema.Tool.builder(TOOL_ID, PARAMS.inputSchema())
43 .description("Read a document's revision history.")
44 .build();
45 }
46 {{/code}}
47
48 == Reading arguments ==
49
50 Every accessor takes the call's argument map and the parameter name.
51
52 |=Method |=Returns
53 |{{code}}string(args, key){{/code}} |The trimmed value, or {{code}}null{{/code}} when absent or blank
54 |{{code}}stringOrEmpty(args, key){{/code}} |The value, distinguishing present-but-empty from absent, for a parameter where clearing is meaningful
55 |{{code}}requireString(args, key){{/code}} |The value, refusing an absent or blank one
56 |{{code}}integer(args, key){{/code}} |The value, or {{code}}null{{/code}} when absent
57 |{{code}}integer(args, key, defaultValue){{/code}} |The value, or the default when absent
58 |{{code}}requireInteger(args, key){{/code}} |The value, refusing an absent one
59 |{{code}}bool(args, key){{/code}} |The value, {{code}}false{{/code}} when absent
60 |{{code}}boolOrNull(args, key){{/code}} |The value as a tri-state, {{code}}null{{/code}} when absent, for an omitted-means-unchanged flag
61 |{{code}}stringList(args, key){{/code}} |The array's elements, refusing a non-string element
62 |{{code}}stringMap(args, key){{/code}} |The object's entries, refusing a non-string value
63 |{{code}}requireStringMap(args, key){{/code}} |The same, refusing an absent one
64
65 A type mismatch throws {{code}}IllegalArgumentException{{/code}} carrying an agent-facing message, which the tool turns into an error result. Reading a parameter that was never declared, or declared with another type, throws {{code}}IllegalStateException{{/code}}: that is a programmer error, and the tool's own tests are where it is meant to surface.
66
67 == Static helpers ==
68
69 |=Member |=Purpose
70 |{{code}}result(message){{/code}} |A successful text result
71 |{{code}}errorResult(message){{/code}} |An error text result ({{code}}isError=true{{/code}})
72 |{{code}}stripLineBreaks(value){{/code}} |Removes every control, line-separator and bidirectional formatting character, so untrusted page text can neither break a line of the output grammar nor reorder what it renders as
73 |{{code}}parseLocale(raw, key){{/code}} |Parses and validates a {{code}}locale{{/code}} argument, with the message shared by every locale-aware tool
74 |{{code}}isoInstant(value){{/code}} |Formats an untyped date value as an ISO-8601 UTC instant, the unambiguous form for agent-facing output
75 |{{code}}booleanValue(value, key){{/code}} |Coerces an already-extracted value to a boolean, for a flag nested inside a bespoke argument
76 |{{code}}ERROR_PREFIX{{/code}} |The prefix the parameter errors share, for a tool building its own
77 |{{code}}LOCALE_FORMS{{/code}} |The example locale forms, shared by the descriptions and the parse error
78
79 == Schemas that vary with cross-wiki reach ==
80
81 A tool whose advertised schema depends on cross-wiki reach holds both variants in {{code}}MCPReachAwareParams{{/code}} rather than re-implementing the split. Parsing always uses the superset; only the advertised schema differs.
82
83 {{code language="java"}}
84 private static final MCPReachAwareParams PARAMS = MCPReachAwareParams.of(MyTool::params);
85
86 private static MCPToolSupport params(boolean crossWiki)
87 {
88 return MCPToolSupport.builder()
89 .requiredString("reference", "The document to read.")
90 .stringIf(crossWiki, "wiki", "Optional wiki id to read from instead of the current wiki.")
91 .build();
92 }
93 {{/code}}
94
95 |=Member |=Purpose
96 |{{code}}of(paramsBuilder){{/code}} |Builds both variants eagerly, from {{code}}apply(true){{/code}} and {{code}}apply(false){{/code}}
97 |{{code}}advertised(reachEnabled){{/code}} |The variant to put in the tool definition
98 |{{code}}parser(){{/code}} |The superset, always used to read arguments
99 |{{code}}CROSS_WIKI_REFERENCE_SENTENCE{{/code}} |The sentence the cross-wiki variants append to a reference description

Get Connected