alibaba/arthas · error · IllegalArgumentException

Duplicate tool name: ${toolName}

Error message

Duplicate tool name: ${toolName}

What it means

Thrown by assertNoDuplicateTool() during the .tools(...) / .tool(...) builder calls when a ToolSpecification name collides with an already-registered regular (non-task) tool. The MCP tools capability requires unique tool names; the check scans the existing this.tools list. It is an IllegalArgumentException at configuration time, before any server starts.

Source

Thrown at arthas-mcp-server/src/main/java/com/taobao/arthas/mcp/server/protocol/server/McpServer.java:274

			this.taskMessageQueue = taskMessageQueue;
			return this;
		}

		protected void validateTaskConfiguration() {
			boolean hasTaskTools = !this.taskTools.isEmpty();
			boolean hasTaskStore = this.taskStore != null;
			
			if (hasTaskTools && !hasTaskStore) {
				throw new IllegalStateException("Task-aware tools registered but no TaskStore configured. "
						+ "Add a TaskStore via .taskStore(store) or remove task tools.");
			}
			// Note: Having taskStore without taskTools is allowed (for future dynamic registration)
		}

		private void assertNoDuplicateTool(String toolName) {
			for (McpServerFeatures.ToolSpecification tool : this.tools) {
				if (tool.getTool().getName().equals(toolName)) {
					throw new IllegalArgumentException("Duplicate tool name: " + toolName);
				}
			}
			for (TaskAwareToolSpecification taskTool : this.taskTools) {
				if (taskTool.tool().getName().equals(toolName)) {
					throw new IllegalArgumentException("Duplicate tool name: " + toolName);
				}
			}
		}

		public McpNettyServer build() {
			validateTaskConfiguration();
			
			ObjectMapper mapper = this.objectMapper != null ? this.objectMapper : JsonParser.getObjectMapper();
			Assert.notNull(this.commandExecutor, "CommandExecutor must be set before building");
			return new McpNettyServer(
					this.transportProvider, mapper, this.requestTimeout,
					new McpServerFeatures.McpServerConfig(this.serverInfo, this.serverCapabilities, this.tools,
							this.taskTools,

View on GitHub (pinned to 21cf2e9ba5)

Solutions

  1. Give each ToolSpecification a globally unique name; namespace by module/feature.
  2. Dedupe the tool list before passing it to the builder.
  3. Catch IllegalArgumentException during registration setup to fail fast with a clear message.
  4. Audit all .tool()/.tools() call sites for name collisions.

Example fix

// before
spec.tool(toolNamedExecute).tool(otherToolNamedExecute); // throws

// after
Set<String> names = new HashSet<>();
for (var t : allTools) {
    if (!names.add(t.getTool().getName())) {
        throw new IllegalStateException("Duplicate: " + t.getTool().getName());
    }
}
spec.tools(allTools);
Defensive patterns

Strategy: validation

Validate before calling

Set<String> names = new HashSet<>();
for (McpServerFeatures.ToolSpecification t : allTools) {
    if (!names.add(t.getTool().getName())) {
        throw new IllegalStateException("Duplicate tool name: " + t.getTool().getName());
    }
}
spec.tools(allTools);

Try / catch

try {
    spec.tool(newTool);
} catch (IllegalArgumentException e) {
    if (e.getMessage().startsWith("Duplicate tool name")) {
        // rename or skip
    } else throw e;
}

Prevention

When it happens

Trigger: Adding two ToolSpecifications whose getTool().getName() returns the same string via .tools(...) or .tool(...); merging tool lists from modules that did not coordinate names.

Common situations: Two plugins defining a tool named identically (e.g. 'execute'); copy-pasted tool specs; default tool names colliding with custom ones.

Related errors


AI-assisted analysis of alibaba/arthas@21cf2e9ba5 (2026-08-14). Data as JSON: /api/errors/9448452d5b679852. Report an issue: GitHub.