{"record":{"id":"c871c2985f915f8b","repo":"spring-projects/spring-ai","slug":"parameter-must-be-of-type-list-mcpschema-tool","errorCode":null,"errorMessage":"Parameter must be of type List<McpSchema.Tool>: \" + method.getName() + \" in \" + method.getDeclaringClass().getName() + \" has parameter of type \" + paramType.getName()","messagePattern":"Parameter must be of type List<McpSchema\\.Tool>: \" \\+ method\\.getName\\(\\) \\+ \" in \" \\+ method\\.getDeclaringClass\\(\\)\\.getName\\(\\) \\+ \" has parameter of type \" \\+ paramType\\.getName\\(\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/changed/tool/AbstractMcpToolListChangedMethodCallback.java","lineNumber":102,"sourceCode":"\t/**\n\t * Validates method parameters. This method provides common validation logic.\n\t * @param method The method to validate\n\t * @throws IllegalArgumentException if the parameters are not compatible\n\t */\n\tprotected void validateParameters(Method method) {\n\t\tParameter[] parameters = method.getParameters();\n\n\t\t// Check parameter count - must have exactly 1 parameter\n\t\tif (parameters.length != 1) {\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Method must have exactly 1 parameter (List<McpSchema.Tool>): \" + 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 type - must be List<McpSchema.Tool>\n\t\tClass<?> paramType = parameters[0].getType();\n\t\tif (!List.class.isAssignableFrom(paramType)) {\n\t\t\tthrow new IllegalArgumentException(\"Parameter must be of type List<McpSchema.Tool>: \" + method.getName()\n\t\t\t\t\t+ \" in \" + method.getDeclaringClass().getName() + \" has parameter of type \" + paramType.getName());\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.\n\t * @param method The method to build arguments for\n\t * @param exchange The server exchange\n\t * @param updatedTools The updated list of tools\n\t * @return An array of arguments for the method invocation\n\t */\n\tprotected Object[] buildArgs(Method method, Object exchange, List<McpSchema.Tool> updatedTools) {\n\t\tParameter[] parameters = method.getParameters();\n\t\tObject[] args = new Object[parameters.length];\n","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/changed/tool/AbstractMcpToolListChangedMethodCallback.java#L84-L120","documentation":"After confirming exactly one parameter, validateParameters checks that the parameter type is assignable from java.util.List. The tool-list-changed dispatch passes a List<McpSchema.Tool>, so a non-List parameter (e.g. Tool[] or List<Tool> of the wrong raw type declared differently) cannot receive it; the exception message includes the method name, declaring class, and offending parameter type.","triggerScenarios":"Registering a handler like void onToolListChanged(Tool[] tools) or void onToolListChanged(Set<McpSchema.Tool> tools) — the single parameter's raw type is not a List, so List.class.isAssignableFrom(paramType) fails.","commonSituations":"Porting handlers from callback APIs that deliver arrays instead of lists, or hand-writing a consumer where the developer misremembered the expected signature of AbstractMcpToolListChangedMethodCallback.","solutions":["Change the handler's single parameter type to List<McpSchema.Tool> (or a raw List/assignable subtype)","Convert the data source: if you have an array-based producer, wrap it in Arrays.asList before it reaches the callback registration","Check the raw parameter type via reflection if generics are involved — the library only checks the raw type, so List must appear in the declared signature"],"exampleFix":"// before\npublic void onToolListChanged(McpSchema.Tool[] tools) { ... }\n// after\npublic void onToolListChanged(java.util.List<io.modelcontextprotocol.spec.McpSchema.Tool> tools) { ... }","handlingStrategy":"validation","validationCode":"if (handlerMethod.getParameterCount() == 1 && !java.util.List.class.isAssignableFrom(handlerMethod.getParameterTypes()[0])) throw new IllegalArgumentException(\"Parameter must be List<McpSchema.Tool>\");","typeGuard":"static boolean takesToolList(java.lang.reflect.Method m) { return m.getParameterCount() == 1 && java.util.List.class.isAssignableFrom(m.getParameterTypes()[0]); }","tryCatchPattern":"try { buildCallback(handlerMethod); } catch (IllegalArgumentException e) { log.error(\"Parameter type error: {}\", e.getMessage()); }","preventionTips":["Use List<McpSchema.Tool> (not arrays, sets, or other collections) for the single handler parameter","Copy the canonical signature from the library's javadoc/tests","Check raw parameter types when relying on generics"],"tags":["java","reflection","type-mismatch","method-signature"],"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"}