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

SyncMcpToolCallbackProvider.getToolCallbacks() validates that the aggregated list of ToolCallbacks across all configured MCP clients has no duplicate tool names and throws IllegalStateException otherwise. Because tool invocation is by name, duplicates would be ambiguous, so the provider fails fast during discovery.

Source

Thrown at mcp/common/src/main/java/org/springframework/ai/mcp/SyncMcpToolCallbackProvider.java:185

	}

	private static McpConnectionInfo connectionInfo(McpSyncClient mcpClient) {
		return McpConnectionInfo.builder()
			.clientCapabilities(mcpClient.getClientCapabilities())
			.clientInfo(mcpClient.getClientInfo())
			.initializeResult(mcpClient.getCurrentInitializationResult())
			.build();
	}

	/**
	 * Validates tool callbacks for duplicate names.
	 * @param toolCallbacks callbacks to validate
	 * @throws IllegalStateException if duplicate names exist
	 */
	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 tool callbacks from multiple MCP clients.
	 * <p>
	 * Discovers and consolidates tools from all provided clients into a single list,
	 * ensuring no naming conflicts.
	 * @param mcpClients MCP clients to discover tools from
	 * @return consolidated list of tool callbacks
	 */
	public static List<ToolCallback> syncToolCallbacks(List<McpSyncClient> mcpClients) {

		if (CollectionUtils.isEmpty(mcpClients)) {
			return List.of();
		}
		return List.of((new SyncMcpToolCallbackProvider(mcpClients).getToolCallbacks()));

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Configure a unique connectionNamePrefix per MCP client so tool names become <prefix>_<toolName>
  2. Rename or deduplicate the colliding tool on one of the MCP servers
  3. Use the duplicated names listed in the message to locate the offending clients/servers
  4. Remove the redundant MCP client connection if both expose the same toolset

Example fix

// before (application.yml) — no prefixes, both servers expose "search"
spring:
  ai:
    mcp:
      client:
        connections:
          server1:
            url: http://host1/sse
          server2:
            url: http://host2/sse
// after — prefixes ensure unique tool names (server1_search, server2_search)
spring:
  ai:
    mcp:
      client:
        toolcallback:
          connection-name-prefix: mcp
        connections:
          server1:
            url: http://host1/sse
          server2:
            url: http://host2/sse
Defensive patterns

Strategy: validation

Validate before calling

SyncMcpToolCallbackProvider p = new SyncMcpToolCallbackProvider(clients);
List<ToolCallback> cbs = p.getToolCallbacks(); // wraps validation
List<String> dups = ToolUtils.getDuplicateToolNames(cbs);
if (!dups.isEmpty()) throw new IllegalStateException("Duplicate MCP tool names at startup: " + dups);

Type guard

long distinct = callbacks.stream().map(c -> c.getToolDefinition().name()).distinct().count();
boolean hasDuplicates = distinct != callbacks.size();

Prevention

When it happens

Trigger: Calling getToolCallbacks() on a SyncMcpToolCallbackProvider aggregating multiple McpSyncClients where tools from different clients resolve to identical names — typically because no connectionNamePrefix is set or the same prefix is reused.

Common situations: spring.ai.mcp.client connections for two servers that both expose e.g. 'fetch_docs' without connection-name-prefix configured; duplicating a server entry in the connections map; upgrading to a spring-ai version where default naming no longer includes a unique prefix.

Related errors


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