{"record":{"id":"cd61f04643b6bd97","repo":"spring-projects/spring-ai","slug":"found-2-sampling-handlers-for-client-s-found-i","errorCode":null,"errorMessage":"Found 2 sampling handlers for client [%s], found in bean with names %s. Only one @McpSampling handler is allowed per client","messagePattern":"Found 2 sampling handlers for client \\[(.+?)\\], found in bean with names (.+?)\\. Only one @McpSampling handler is allowed per client","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/spring/AbstractClientMcpHandlerRegistry.java","lineNumber":111,"sourceCode":"\t\t\t\t}\n\t\t\t\telse if (foundAnnotation instanceof McpElicitation elicitation) {\n\t\t\t\t\tfor (var client : elicitation.clients()) {\n\t\t\t\t\t\telicitationClientToAnnotatedBeans.computeIfAbsent(client, c -> new ArrayList<>()).add(beanName);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (var elicitationEntry : elicitationClientToAnnotatedBeans.entrySet()) {\n\t\t\tif (elicitationEntry.getValue().size() > 1) {\n\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\t\"Found 2 elicitation handlers for client [%s], found in bean with names %s. Only one @McpElicitation handler is allowed per client\"\n\t\t\t\t\t\t\t.formatted(elicitationEntry.getKey(), new LinkedHashSet<>(elicitationEntry.getValue())));\n\t\t\t}\n\t\t}\n\t\tfor (var samplingEntry : samplingClientToAnnotatedBeans.entrySet()) {\n\t\t\tif (samplingEntry.getValue().size() > 1) {\n\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\t\"Found 2 sampling handlers for client [%s], found in bean with names %s. Only one @McpSampling handler is allowed per client\"\n\t\t\t\t\t\t\t.formatted(samplingEntry.getKey(), new LinkedHashSet<>(samplingEntry.getValue())));\n\t\t\t}\n\t\t}\n\n\t\tMap<String, McpSchema.ClientCapabilities.Builder> capsPerClient = new HashMap<>();\n\t\tfor (var samplingClient : samplingClientToAnnotatedBeans.keySet()) {\n\t\t\tcapsPerClient.computeIfAbsent(samplingClient, ignored -> McpSchema.ClientCapabilities.builder()).sampling();\n\t\t}\n\t\tfor (var elicitationClient : elicitationClientToAnnotatedBeans.keySet()) {\n\t\t\tcapsPerClient.computeIfAbsent(elicitationClient, ignored -> McpSchema.ClientCapabilities.builder())\n\t\t\t\t.elicitation();\n\t\t}\n\n\t\tthis.capabilitiesPerClient = capsPerClient.entrySet()\n\t\t\t.stream()\n\t\t\t.collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().build()));\n\t}","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/spring/AbstractClientMcpHandlerRegistry.java#L93-L129","documentation":"Same duplicate-detection mechanism as elicitation but for @McpSampling handlers: AbstractClientMcpHandlerRegistry.postProcessBeanFactory requires at most one sampling handler per MCP client. When samplingClientToAnnotatedBeans contains two or more beans for the same client it throws IllegalArgumentException with the client name and conflicting bean names, failing at startup.","triggerScenarios":"Registering two or more beans with @McpSampling methods targeting the same client name; the conflict surfaces during application context startup in postProcessBeanFactory.","commonSituations":"Multiple @Configuration classes each defining sampling handlers for the same client; leftover bean after refactoring; auto-configuration plus a manually defined handler bean colliding.","solutions":["Delete or exclude one of the duplicate @McpSampling beans (bean names are given in the message).","Merge sampling logic into one @McpSampling handler per client.","Assign the handlers to different client names if they were meant for distinct clients."],"exampleFix":"// before\n@Bean SamplingHandlerA samplingA() { ... } // @McpSampling(\"myClient\")\n@Bean SamplingHandlerB samplingB() { ... } // @McpSampling(\"myClient\")\n\n// after\n@Bean SamplingHandler sampling() { ... } // single @McpSampling(\"myClient\") handler","handlingStrategy":"validation","validationCode":"// At startup, verify exactly one @McpSampling handler per client\nMap<String, Long> counts = beansWithMcpSampling.stream()\n    .collect(Collectors.groupingBy(b -> b.clientName(), Collectors.counting()));\ncounts.forEach((client, n) -> {\n    if (n > 1) throw new IllegalStateException(\"Multiple @McpSampling handlers for client \" + client);\n});","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Register exactly one @McpSampling bean per client name.","Grep for @McpSampling when introducing a new handler.","Avoid combining auto-configuration with manual handler beans for the same client."],"tags":["configuration","mcp","sampling","duplicate-handler","startup"],"backgroundTag":"conflicting-config-options","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}