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);

}