{"record":{"id":"47668341fb43b9e9","repo":"spring-projects/spring-ai","slug":"tool-name-toolname-already-exists-using-uniqu","errorCode":null,"errorMessage":"Tool name '<toolName>' already exists. Using unique tool name '<uniqueToolName>'","messagePattern":"Tool name '<toolName>' already exists\\. Using unique tool name '<uniqueToolName>'","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"mcp/common/src/main/java/org/springframework/ai/mcp/DefaultMcpToolNamePrefixGenerator.java","lineNumber":73,"sourceCode":"\tprivate final Set<ConnectionId> existingConnections = ConcurrentHashMap.newKeySet();\n\n\tprivate final Set<String> allUsedToolNames = ConcurrentHashMap.newKeySet();\n\n\tprivate final AtomicInteger counter = new AtomicInteger(1);\n\n\t@Override\n\tpublic String prefixedToolName(McpConnectionInfo mcpConnectionInfo, McpSchema.Tool tool) {\n\n\t\tString uniqueToolName = McpToolUtils.format(tool.name());\n\n\t\tif (this.existingConnections\n\t\t\t.add(new ConnectionId(mcpConnectionInfo.clientInfo(), (mcpConnectionInfo.initializeResult() != null)\n\t\t\t\t\t? mcpConnectionInfo.initializeResult().serverInfo() : null, tool))) {\n\t\t\tif (!this.allUsedToolNames.add(uniqueToolName)) {\n\t\t\t\tuniqueToolName = \"alt_\" + this.counter.getAndIncrement() + \"_\" + uniqueToolName;\n\t\t\t\tthis.allUsedToolNames.add(uniqueToolName);\n\t\t\t\tif (logger.isWarnEnabled()) {\n\t\t\t\t\tlogger.warn(\"Tool name '\" + tool.name() + \"' already exists. Using unique tool name '\"\n\t\t\t\t\t\t\t+ uniqueToolName + \"'\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn uniqueToolName;\n\t}\n\n\tprivate record ConnectionId(@Nullable Implementation clientInfo, @Nullable Implementation serverInfo, Tool tool) {\n\t}\n\n}\n","sourceCodeStart":55,"sourceCodeEnd":86,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/mcp/common/src/main/java/org/springframework/ai/mcp/DefaultMcpToolNamePrefixGenerator.java#L55-L86","documentation":"DefaultMcpToolNamePrefixGenerator.prefixedToolName() detected a tool name collision: after prefixing with connection info, the generated name was already used by another registered MCP tool. The generator appends an \"alt_<counter>_\" prefix to create a unique name and warns which original name was remapped, so callers referencing the old name will no longer resolve.","triggerScenarios":"Registering tools from multiple MCP connections/servers that expose identically named tools (e.g. two servers both exposing a 'search' tool) such that the prefix generator produces the same prefixed name twice within the same generator instance.","commonSituations":"Aggregating several MCP servers where tools share generic names; a server reconnecting/re-registering tools without a fresh name-prefix generator; similarly-named tools on the same server after prefix normalization.","solutions":["Look up tools by the renamed unique name ('alt_<n>_<name>') reported in the warning when invoking them.","Provide a custom ToolNamePrefixGenerator (or configure distinct connection ids) that yields stable, collision-free names.","Rename tools on the conflicting MCP servers so their exposed names differ.","Recreate the tool-name generator/registry when the set of MCP connections changes to keep mappings deterministic."],"exampleFix":"// before: assuming prefixed name equals tool name\nString name = prefixGenerator.prefixedToolName(connectionInfo, tool); // 'server1_search'\nclient.call(name, args); // may hit remapped tool\n// after: keep the returned (possibly renamed) name\nString name = prefixGenerator.prefixedToolName(connectionInfo, tool);\ntoolRegistry.put(name, tool); // always use the returned unique name","handlingStrategy":"validation","validationCode":"Set<String> seen = new HashSet<>();\nfor (var conn : connections) {\n    for (var tool : conn.listTools()) {\n        String name = prefixGenerator.prefixedToolName(conn.info(), tool);\n        if (!seen.add(name)) {\n            logger.warn(\"collision on \" + name + \" — configure a custom prefix generator\");\n        }\n    }\n}","typeGuard":"boolean isRemapped(String expectedName, String actualName) {\n    return actualName.startsWith(\"alt_\") && !actualName.equals(expectedName);\n}","tryCatchPattern":"// renamed tools won't throw; detect by absence\nvar tool = toolRegistry.get(name);\nif (tool == null) {\n    // name may have been remapped (alt_N_...); resolve from registry log/lookup\n}","preventionTips":["Use a custom ToolNamePrefixGenerator with stable, distinct prefixes per MCP connection.","Avoid generic tool names across aggregated MCP servers.","Persist the name mapping generated at registration time and invoke tools only via it.","Rebuild the tool registry when connection topology changes."],"tags":["mcp","tool-calling","name-collision","naming"],"backgroundTag":"invalid-identifier","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"}