{"record":{"id":"b65683190d3ae06a","repo":"spring-projects/spring-ai","slug":"currently-only-methods-with-a-single-createmessage","errorCode":null,"errorMessage":"Currently only methods with a single CreateMessageRequest parameter are supported: {methodName} in {className} has {paramCount} parameters","messagePattern":"Currently only methods with a single CreateMessageRequest parameter are supported: (.+?) in (.+?) has (.+?) parameters","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/sampling/AbstractMcpSamplingMethodCallback.java","lineNumber":111,"sourceCode":"\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Method must have at least 1 parameter (CreateMessageRequest): \" + method.getName() + \" in \"\n\t\t\t\t\t\t\t+ method.getDeclaringClass().getName() + \" has \" + parameters.length + \" parameters\");\n\t\t}\n\n\t\t// Check parameter types\n\t\tif (parameters.length == 1) {\n\t\t\t// Single parameter must be CreateMessageRequest\n\t\t\tif (!CreateMessageRequest.class.isAssignableFrom(parameters[0].getType())) {\n\t\t\t\tthrow new IllegalArgumentException(\"Single parameter must be of type CreateMessageRequest: \"\n\t\t\t\t\t\t+ method.getName() + \" in \" + method.getDeclaringClass().getName() + \" has parameter of type \"\n\t\t\t\t\t\t+ parameters[0].getType().getName());\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\t// TODO: Support for multiple parameters corresponding to CreateMessageRequest\n\t\t\t// fields\n\t\t\t// For now, we only support the single parameter version\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Currently only methods with a single CreateMessageRequest parameter are supported: \"\n\t\t\t\t\t\t\t+ method.getName() + \" in \" + method.getDeclaringClass().getName() + \" has \"\n\t\t\t\t\t\t\t+ parameters.length + \" parameters\");\n\t\t}\n\t}\n\n\t/**\n\t * Builds the arguments array for invoking the method.\n\t * <p>\n\t * This method constructs an array of arguments based on the method's parameter types\n\t * and the available values (exchange, request).\n\t * @param method The method to build arguments for\n\t * @param exchange The server exchange\n\t * @param request The sampling request\n\t * @return An array of arguments for the method invocation\n\t */\n\tprotected Object[] buildArgs(Method method, Object exchange, CreateMessageRequest request) {\n\t\tParameter[] parameters = method.getParameters();","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/sampling/AbstractMcpSamplingMethodCallback.java#L93-L129","documentation":"The sampling callback currently supports only the single-parameter form: exactly one CreateMessageRequest argument. validateParameters() rejects methods with 2+ parameters, including multi-arg injections of individual request fields, because that support is not yet implemented (marked TODO in source).","triggerScenarios":"Annotating a sampling handler with two or more parameters, e.g. `void handler(CreateMessageRequest req, McpTransportContext ctx)` or splitting request fields into separate parameters.","commonSituations":"Developers mirror the tool/prompt callback style, which supports extra context parameters, and assume sampling does the same; or they add convenience parameters like locale or model hints.","solutions":["Reduce the signature to a single CreateMessageRequest parameter","Move any additional data derivation (context, options) inside the method body, reading from the CreateMessageRequest","Watch the library for the multi-parameter TODO being implemented, or file/request the feature upstream"],"exampleFix":"// before\n@McpSampling\npublic String sample(CreateMessageRequest request, McpTransportContext ctx) { return \"reply\"; }\n// after\n@McpSampling\npublic String sample(CreateMessageRequest request) { /* derive context from request */ return \"reply\"; }","handlingStrategy":"validation","validationCode":"for (Method m : clazz.getDeclaredMethods()) {\n    if (m.isAnnotationPresent(McpSampling.class)\n            && m.getParameterCount() != 1) {\n        throw new IllegalStateException(\"Sampling method \" + m.getName()\n            + \" must have exactly one CreateMessageRequest parameter\");\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    registerSamplingCallback(method);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"only methods with a single CreateMessageRequest\")) {\n        log.error(\"Too many parameters on sampling method {}: {}\", method, e.getMessage());\n    } else { throw e; }\n}","preventionTips":["Treat sampling handlers as single-argument functions; compute extras inside the method","Do not copy multi-parameter patterns from tool or prompt callbacks","Keep a signature checklist per callback type (sampling vs tool vs prompt) in team docs"],"tags":["java","mcp","sampling","unsupported","reflection"],"backgroundTag":"unsupported-operation","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"}