{"record":{"id":"c0542dbadcbf60f6","repo":"spring-projects/spring-ai","slug":"method-must-return-a-mono-t-where-t-is-one-of-get","errorCode":null,"errorMessage":"Method must return a Mono<T> where T is one of GetPromptResult, List<PromptMessage>, List<String>, PromptMessage, or String: {method.getName()} in {method.getDeclaringClass().getName()} returns {returnType.getName()}","messagePattern":"Method must return a Mono<T> where T is one of GetPromptResult, List<PromptMessage>, List<String>, PromptMessage, or String: (.+?) in (.+?) returns (.+?)","errorType":"validation","errorClass":"java.lang.IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/prompt/AsyncMcpPromptMethodCallback.java","lineNumber":157,"sourceCode":"\t\t\t\t\t.data(ErrorUtils.findCauseUsingPlainJava(e).getMessage())\n\t\t\t\t\t.build());\n\t\t\t}\n\t\t});\n\t}\n\n\t@Override\n\tprotected boolean isSupportedExchangeOrContextType(Class<?> paramType) {\n\t\treturn (McpAsyncServerExchange.class.isAssignableFrom(paramType)\n\t\t\t\t|| McpTransportContext.class.isAssignableFrom(paramType));\n\t}\n\n\t@Override\n\tprotected void validateReturnType(Method method) {\n\t\tClass<?> returnType = method.getReturnType();\n\n\t\t// For AsyncMcpPromptMethodCallback, the method must return a Mono\n\t\tif (!Mono.class.isAssignableFrom(returnType)) {\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Method must return a Mono<T> where T is one of GetPromptResult, List<PromptMessage>, \"\n\t\t\t\t\t\t\t+ \"List<String>, PromptMessage, or String: \" + method.getName() + \" in \"\n\t\t\t\t\t\t\t+ method.getDeclaringClass().getName() + \" returns \" + returnType.getName());\n\t\t}\n\t}\n\n\t/**\n\t * Create a new builder.\n\t * @return A new builder instance\n\t */\n\tpublic static Builder builder() {\n\t\treturn new Builder();\n\t}\n\n\t/**\n\t * Builder for creating AsyncMcpPromptMethodCallback instances.\n\t * <p>\n\t * This builder provides a fluent API for constructing AsyncMcpPromptMethodCallback","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/prompt/AsyncMcpPromptMethodCallback.java#L139-L175","documentation":"AsyncMcpPromptMethodCallback validates that any @McpPrompt-annotated method handled by the async callback returns a Mono<T>, since async MCP prompt execution is reactive. If the method returns a plain (non-Mono) type, the callback cannot wrap it in the reactive pipeline and throws IllegalArgumentException at registration/validation time.","triggerScenarios":"Registering a class with an @McpPrompt method whose return type is GetPromptResult, List<PromptMessage>, String, etc. directly (not Mono<...>) while using AsyncMcpPromptMethodCallback (async MCP server).","commonSituations":"Reusing a sync-style prompt bean on an async MCP server; copying examples from sync MCP docs into a reactive setup; forgetting to wrap the result with Mono.just().","solutions":["Change the method return type to Mono<T> where T is GetPromptResult, List<PromptMessage>, List<String>, PromptMessage, or String","Wrap the existing return value: return Mono.just(result)","If the method is inherently synchronous, register it with the sync MCP server / SyncMcpPromptMethodCallback instead"],"exampleFix":"// before\n@McpPrompt(description = \"greet\")\npublic String greet(GreetRequest req) { return \"hi\"; }\n// after\n@McpPrompt(description = \"greet\")\npublic Mono<String> greet(GreetRequest req) { return Mono.just(\"hi\"); }","handlingStrategy":"validation","validationCode":"if (!Mono.class.isAssignableFrom(method.getReturnType())) { throw new IllegalStateException(method + \" must return Mono<...>\"); }","typeGuard":"boolean returnsMono(Method m) { return Mono.class.isAssignableFrom(m.getReturnType()); }","tryCatchPattern":"try { server.addPrompt(callback); } catch (IllegalArgumentException e) { /* fix signature, not retry */ }","preventionTips":["Use Mono returns for all @McpPrompt methods in reactive servers","Add a startup-time reflection test asserting Mono return types","Keep sync and async prompt beans in separate packages/configurations"],"tags":["mcp","reactor","prompt-method","return-type"],"backgroundTag":"type-mismatch","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}