{"record":{"id":"577bd3b04e43abca","repo":"alibaba/arthas","slug":"tool-with-name-toolname-already-exists","errorCode":null,"errorMessage":"Tool with name '${toolName}' already exists","messagePattern":"Tool with name '(.+?)' already exists","errorType":"exception","errorClass":"McpError","httpStatus":null,"severity":"error","filePath":"arthas-mcp-server/src/main/java/com/taobao/arthas/mcp/server/protocol/server/McpNettyServer.java","lineNumber":269,"sourceCode":"\t\t\tfuture.completeExceptionally(new McpError(\"Tool must not be null\"));\n\t\t\treturn future;\n\t\t}\n\t\tif (toolSpecification.getCall() == null) {\n\t\t\tCompletableFuture<Void> future = new CompletableFuture<>();\n\t\t\tfuture.completeExceptionally(new McpError(\"Tool call handler must not be null\"));\n\t\t\treturn future;\n\t\t}\n\t\tif (this.serverCapabilities.getTools() == null) {\n\t\t\tCompletableFuture<Void> future = new CompletableFuture<>();\n\t\t\tfuture.completeExceptionally(new McpError(\"Server must be configured with tool capabilities\"));\n\t\t\treturn future;\n\t\t}\n\n\t\treturn CompletableFuture.supplyAsync(() -> {\n\t\t\t// Check for duplicate tool names\n\t\t\tif (this.tools.stream()\n\t\t\t\t\t.anyMatch(th -> th.getTool().getName().equals(toolSpecification.getTool().getName()))) {\n\t\t\t\tthrow new CompletionException(\n\t\t\t\t\t\tnew McpError(\"Tool with name '\" + toolSpecification.getTool().getName() + \"' already exists\"));\n\t\t\t}\n\t\t\tthis.tools.add(toolSpecification);\n\t\t\tthis.toolsByName.put(toolSpecification.getTool().getName(), toolSpecification);\n\t\t\tlogger.debug(\"Added tool handler: {}\", toolSpecification.getTool().getName());\n\t\t\treturn null;\n\t\t}).thenCompose(ignored -> {\n\t\t\tif (this.serverCapabilities.getTools().getListChanged()) {\n\t\t\t\treturn notifyToolsListChanged();\n\t\t\t}\n\t\t\treturn CompletableFuture.completedFuture(null);\n\t\t}).exceptionally(ex -> {\n\t\t\tThrowable cause = ex instanceof CompletionException ? ex.getCause() : ex;\n\t\t\tlogger.error(\"Error while adding tool\", cause);\n\t\t\tthrow new CompletionException(cause);\n\t\t});\n\t}\n","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/alibaba/arthas/blob/21cf2e9ba52b305290be7223b980ff504bb9cb5b/arthas-mcp-server/src/main/java/com/taobao/arthas/mcp/server/protocol/server/McpNettyServer.java#L251-L287","documentation":"McpNettyServer.addTool throws McpError (wrapped in CompletionException) when a tool with the same name is already registered. Tools are indexed both in a list and a toolsByName map keyed by Tool.getName(), so duplicate names would corrupt that index; the check refuses the second registration.","triggerScenarios":"Calling addTool twice with tools whose Tool.name is equal (case-sensitive, exact); dynamically registering a tool whose name collides with one registered at server startup; re-registering after a hot reload without removing first.","commonSituations":"Plugin/tool auto-discovery registers the same tool twice; a reload path forgets to removeTool before re-adding; two modules contribute a tool with the same name.","solutions":["Call removeTool(name) before addTool when refreshing a tool.","Check getTools()/the existing list for the name before adding.","Namespace tool names by module to avoid collisions."],"exampleFix":"// before\nserver.addTool(spec); // name 'arthas.dump' already registered\n\n// after\nserver.removeTool(\"arthas.dump\").thenRun(() -> server.addTool(spec));","handlingStrategy":"validation","validationCode":"boolean exists = server.getTools().stream()\n    .anyMatch(t -> t.getTool().getName().equals(name));\nif (exists) server.removeTool(name).join();\nserver.addTool(spec).join();","typeGuard":null,"tryCatchPattern":"try { server.addTool(spec).join(); }\ncatch (CompletionException e) {\n  if (e.getCause() instanceof io.modelcontextprotocol.spec.McpError && e.getCause().getMessage().contains(\"already exists\")) { server.removeTool(name).thenRun(() -> server.addTool(spec)).join(); }\n  else throw e;\n}","preventionTips":["Check the tool list before addTool.","removeTool before re-adding on reload.","Namespace tool names by module."],"tags":["mcp","arthas-mcp-server","registration","duplicate"],"backgroundTag":null,"analyzedSha":"21cf2e9ba52b305290be7223b980ff504bb9cb5b","analyzedAt":"2026-08-14T00:57:07.243Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}