spring-projects/spring-ai · error · IllegalStateException
Multiple tools with the same name (%s)
Error message
Multiple tools with the same name (%s)
What it means
AsyncMcpToolCallbackProvider.getToolCallbacks() collects ToolCallbacks from multiple MCP clients and rejects the whole set if two or more callbacks resolve to the same tool name, since the framework cannot disambiguate which callback to invoke. Duplicate names arise when multiple MCP servers expose identically named tools or when connectionNamePrefix is absent or insufficient to namespace them.
Source
Thrown at mcp/common/src/main/java/org/springframework/ai/mcp/AsyncMcpToolCallbackProvider.java:203
}
private static McpConnectionInfo connectionInfo(McpAsyncClient mcpClient) {
return McpConnectionInfo.builder()
.clientCapabilities(mcpClient.getClientCapabilities())
.clientInfo(mcpClient.getClientInfo())
.initializeResult(mcpClient.getCurrentInitializationResult())
.build();
}
/**
* Validates tool name uniqueness.
* @param toolCallbacks callbacks to validate
* @throws IllegalStateException if duplicate names found
*/
private void validateToolCallbacks(List<ToolCallback> toolCallbacks) {
List<String> duplicateToolNames = ToolUtils.getDuplicateToolNames(toolCallbacks);
if (!duplicateToolNames.isEmpty()) {
throw new IllegalStateException(
"Multiple tools with the same name (%s)".formatted(String.join(", ", duplicateToolNames)));
}
}
/**
* Creates a reactive stream of tool callbacks from multiple MCP clients.
* <p>
* Provides fully reactive tool discovery suitable for non-blocking applications.
* Combines tools from all clients into a single stream with name conflict validation.
* @param mcpClients MCP clients for tool discovery
* @return Flux of tool callbacks from all clients
*/
public static Flux<ToolCallback> asyncToolCallbacks(List<McpAsyncClient> mcpClients) {
if (CollectionUtils.isEmpty(mcpClients)) {
return Flux.empty();
}
return Flux.fromArray(new AsyncMcpToolCallbackProvider(mcpClients).getToolCallbacks());View on GitHub (pinned to 98a7beda4f)
Solutions
- Set a unique connection name prefix per MCP client so tool names are namespaced as <prefix>_<toolName>
- Rename the duplicate tool on one of the MCP servers
- Inspect the comma-separated list in the message to identify the colliding tool names and which servers expose them
- If only one server is needed, remove the redundant McpAsyncClient from the provider
Example fix
// before — two clients both expose tool "search", no prefixes
new AsyncMcpToolCallbackProvider(List.of(asyncClientA, asyncClientB));
// after — prefixes namespace names: serverA_search, serverB_search
new AsyncMcpToolCallbackProvider(
Map.of("serverA", asyncClientA, "serverB", asyncClientB)); Defensive patterns
Strategy: validation
Validate before calling
List<String> dups = ToolUtils.getDuplicateToolNames(provider.getToolCallbacks());
if (!dups.isEmpty()) throw new ConfigurationException("Duplicate MCP tool names: " + dups); // run at startup, before publishing callbacks Type guard
Set<String> seen = new HashSet<>();
List<ToolCallback> unique = callbacks.stream()
.filter(c -> seen.add(c.getToolDefinition().name()))
.toList();
if (unique.size() != callbacks.size()) throw new IllegalStateException("Duplicate MCP tool names detected"); Prevention
- Always set a distinct connectionNamePrefix for every MCP client connection
- Startup-check getToolCallbacks() for duplicates before registering them with ChatClient
- Avoid two server connections exposing the same toolset
- Pin server versions so tool names don't drift
When it happens
Trigger: Calling getToolCallbacks() on an AsyncMcpToolCallbackProvider built from two or more McpAsyncClient instances where ToolUtils.prefixedToolName(...) yields identical names for tools from different clients (no connectionNamePrefix set, or the same prefix used twice).
Common situations: Configuring multiple MCP servers that both expose a tool named e.g. 'search' without a per-connection connectionNamePrefix; two server entries in configuration sharing the same name/prefix; upgrading spring-ai where prefixing behavior changed so previously-distinct names now collide.
Related errors
- Multiple tools with the same name (%s)
- Failed to read stdio connection resource
- SSE connection '<connectionName>' requires a 'url' property.
- Failed to create SSE transport for connection '<connectionNa
- At least one client Id must be specified
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/8057badf1ff5b73b.
Report an issue: GitHub.