spring-projects/spring-ai · error · IllegalArgumentException

Found 2 elicitation handlers for client [%s], found in bean

Error message

Found 2 elicitation handlers for client [%s], found in bean with names %s. Only one @McpElicitation handler is allowed per client

What it means

During bean-factory post-processing, AbstractClientMcpHandlerRegistry collects beans annotated with @McpElicitation per target client. MCP allows at most one elicitation handler per client, so if two or more annotated beans map to the same client name it throws IllegalArgumentException listing the conflicting bean names. This fails fast at application startup rather than at first elicitation request.

Source

Thrown at mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/spring/AbstractClientMcpHandlerRegistry.java:104

				this.allAnnotatedBeans.add(beanName);
			}
			for (var foundAnnotation : foundAnnotations) {
				if (foundAnnotation instanceof McpSampling sampling) {
					for (var client : sampling.clients()) {
						samplingClientToAnnotatedBeans.computeIfAbsent(client, c -> new ArrayList<>()).add(beanName);
					}
				}
				else if (foundAnnotation instanceof McpElicitation elicitation) {
					for (var client : elicitation.clients()) {
						elicitationClientToAnnotatedBeans.computeIfAbsent(client, c -> new ArrayList<>()).add(beanName);
					}
				}
			}
		}

		for (var elicitationEntry : elicitationClientToAnnotatedBeans.entrySet()) {
			if (elicitationEntry.getValue().size() > 1) {
				throw new IllegalArgumentException(
						"Found 2 elicitation handlers for client [%s], found in bean with names %s. Only one @McpElicitation handler is allowed per client"
							.formatted(elicitationEntry.getKey(), new LinkedHashSet<>(elicitationEntry.getValue())));
			}
		}
		for (var samplingEntry : samplingClientToAnnotatedBeans.entrySet()) {
			if (samplingEntry.getValue().size() > 1) {
				throw new IllegalArgumentException(
						"Found 2 sampling handlers for client [%s], found in bean with names %s. Only one @McpSampling handler is allowed per client"
							.formatted(samplingEntry.getKey(), new LinkedHashSet<>(samplingEntry.getValue())));
			}
		}

		Map<String, McpSchema.ClientCapabilities.Builder> capsPerClient = new HashMap<>();
		for (var samplingClient : samplingClientToAnnotatedBeans.keySet()) {
			capsPerClient.computeIfAbsent(samplingClient, ignored -> McpSchema.ClientCapabilities.builder()).sampling();
		}
		for (var elicitationClient : elicitationClientToAnnotatedBeans.keySet()) {
			capsPerClient.computeIfAbsent(elicitationClient, ignored -> McpSchema.ClientCapabilities.builder())

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Remove or disable one of the conflicting @McpElicitation beans so only one handler exists per client (the message lists the offending bean names).
  2. If both handlers are needed, consolidate their logic into a single @McpElicitation method that dispatches internally.
  3. Change one bean's client target so each client has at most one elicitation handler.

Example fix

// before: two beans both annotate @McpElicitation for client "myClient"
@Bean MyElicitationHandlerA handlerA() { ... }
@Bean MyElicitationHandlerB handlerB() { ... }

// after: single consolidated handler
@Bean MyElicitationHandler handler() { ... } // merges A and B logic
Defensive patterns

Strategy: validation

Validate before calling

// At startup, verify exactly one @McpElicitation handler per client before context refresh completes
Map<String, Long> counts = beansWithMcpElicitation.stream()
    .collect(Collectors.groupingBy(b -> b.clientName(), Collectors.counting()));
counts.forEach((client, n) -> {
    if (n > 1) throw new IllegalStateException("Multiple @McpElicitation handlers for client " + client);
});

Prevention

When it happens

Trigger: Declaring two (or more) beans whose methods carry @McpElicitation targeting the same MCP client name; application startup with both registered triggers postProcessBeanFactory to throw.

Common situations: Two configuration classes each defining an elicitation handler for the same client; a copied/renamed bean left in place alongside the new handler; component scanning picking up duplicate @McpElicitation beans.

Related errors


AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11). Data as JSON: /api/errors/c39d3644693d3c4c. Report an issue: GitHub.