MCPToolSupport

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

Reference

MCPToolSupport (org.xwiki.contrib.llm.mcp.MCPToolSupport, annotated @Unstable) 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.

An instance is created with MCPToolSupport.builder() and held in a static final field, which puts the failure mode of a broken declaration at class initialization.

private static final MCPToolSupport PARAMS = MCPToolSupport.builder()
    .requiredString("reference", "The document to read, e.g. \"Sandbox.WebHome\".")
    .string("locale", "Read a translation, e.g. " + MCPToolSupport.LOCALE_FORMS + ".")
    .integer("limit", "Revisions per page (default 20, max 100).")
    .bool("showHidden", "Include hidden pages (default false).")
    .build();

Declaring parameters

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.

Method Declares
string(name, description) An optional string
requiredString(name, description) A required string
stringIf(condition, name, description) An optional string, only when the condition holds
integer(name, description) An optional integer
requiredInteger(name, description) A required integer
bool(name, description) An optional boolean
stringArray(name, description) An optional flat array of strings
stringMap(name, description) An optional flat object whose values are strings
requiredStringMap(name, description) The same, required
build() The finished parameter set

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 inputSchema(Map).

Generating the schema

Method Returns
inputSchema() The JSON Schema 2020-12 object map that McpSchema.Tool.builder(String, Map) expects
inputSchema(Map extraProperties) The same, merged with hand-built properties, for the rare non-scalar parameter
@Override
public McpSchema.Tool getToolDefinition()
{
    return McpSchema.Tool.builder(TOOL_ID, PARAMS.inputSchema())
        .description("Read a document's revision history.")
        .build();
}

Reading arguments

Every accessor takes the call's argument map and the parameter name.

Method Returns
string(args, key) The trimmed value, or null when absent or blank
stringOrEmpty(args, key) The value, distinguishing present-but-empty from absent, for a parameter where clearing is meaningful
requireString(args, key) The value, refusing an absent or blank one
integer(args, key) The value, or null when absent
integer(args, key, defaultValue) The value, or the default when absent
requireInteger(args, key) The value, refusing an absent one
bool(args, key) The value, false when absent
boolOrNull(args, key) The value as a tri-state, null when absent, for an omitted-means-unchanged flag
stringList(args, key) The array's elements, refusing a non-string element
stringMap(args, key) The object's entries, refusing a non-string value
requireStringMap(args, key) The same, refusing an absent one

A type mismatch throws IllegalArgumentException 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 IllegalStateException: that is a programmer error, and the tool's own tests are where it is meant to surface.

Static helpers

Member Purpose
result(message) A successful text result
errorResult(message) An error text result (isError=true)
stripLineBreaks(value) 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
parseLocale(raw, key) Parses and validates a locale argument, with the message shared by every locale-aware tool
isoInstant(value) Formats an untyped date value as an ISO-8601 UTC instant, the unambiguous form for agent-facing output
booleanValue(value, key) Coerces an already-extracted value to a boolean, for a flag nested inside a bespoke argument
ERROR_PREFIX The prefix the parameter errors share, for a tool building its own
LOCALE_FORMS The example locale forms, shared by the descriptions and the parse error

Schemas that vary with cross-wiki reach

A tool whose advertised schema depends on cross-wiki reach holds both variants in MCPReachAwareParams rather than re-implementing the split. Parsing always uses the superset; only the advertised schema differs.

private static final MCPReachAwareParams PARAMS = MCPReachAwareParams.of(MyTool::params);

private static MCPToolSupport params(boolean crossWiki)
{
    return MCPToolSupport.builder()
        .requiredString("reference", "The document to read.")
        .stringIf(crossWiki, "wiki", "Optional wiki id to read from instead of the current wiki.")
        .build();
}
Member Purpose
of(paramsBuilder) Builds both variants eagerly, from apply(true) and apply(false)
advertised(reachEnabled) The variant to put in the tool definition
parser() The superset, always used to read arguments
CROSS_WIKI_REFERENCE_SENTENCE The sentence the cross-wiki variants append to a reference description

FAQ

Why not just write the JSON schema by hand?

Because then it exists twice: once as the advertised schema and once as the parsing code, and nothing fails when they disagree. The agent is told about a parameter the tool no longer reads, or sends one the schema never mentioned.

Can a tool declare a nested object parameter?

Not through the builder. Declare the scalars, arrays and string maps here and merge the bespoke part with inputSchema(Map), keeping the hand-written schema down to the part that needs it.

Related

Get Connected