Currently when using Elicitation the schema needs to be manually created:
@McpTool(description = "generate a random number")
fun random(exchange: McpSyncServerExchange): Int {
// sometimes additional data is needed
val maybeNumber = if (Random.nextBoolean()) {
val schema = mapOf("type" to "object", "properties" to mapOf("number" to mapOf("type" to "integer")), "required" to listOf("number"))
val userInput = exchange.createElicitation(McpSchema.ElicitRequest("what is your favorite number?", schema))
(userInput.content["number"] as? String)?.toIntOrNull().takeIf { userInput.action == McpSchema.ElicitResult.Action.ACCEPT }
} else null
return maybeNumber ?: Random.nextInt(100)
}
It'd be nice if McpSchema.ElicitRequest supported passing a class that'd enable generating the schema automatically.
Note that the MCP Spec seems to constrain the schema of Elicitation to an object (no list or primitives).
Comment From: tzolov
@jamesward with the https://github.com/spring-ai-community/mcp-annotations/pull/70 improvements you should be able to do something like:
public record UserInput(Number number) {}
@McpTool(description = "generate a random number")
public Integer random(McpSyncRequestContext ctx) {
ctx.info("random tool Invoked"); // Structured logging
ctx.progress(25); // progress track (assumes the client have set the progress token)
// sometimes additional data is needed
Integer maybeNumber = null;
if (Random.nextBoolean()) {
StructuredElicitResult<userInput> userInput= ctx..elicitation(
new TypeReference<UserInput>() {},
"what is your favorite number?",
null).get();
if (userInput.action() == McpSchema.ElicitResult.Action.ACCEPT) {
maybeNumber = userInput.structuredContent().number().intValue();
}
}
ctx.progress(100);
return maybeNumber != null ? maybeNumber : Random.nextInt(100);
}