alibaba/arthas · warning · McpError

Tool with name '${toolName}' not found

Error message

Tool with name '${toolName}' not found

What it means

McpNettyServer.removeTool throws McpError (wrapped in CompletionException) when no registered tool matches the given name. The list is filtered by Tool.name equality; if nothing matched, removeIf returns false and the server treats it as an error rather than a no-op.

Source

Thrown at arthas-mcp-server/src/main/java/com/taobao/arthas/mcp/server/protocol/server/McpNettyServer.java:303

		});
	}

	public CompletableFuture<Void> removeTool(String toolName) {
		if (toolName == null) {
			CompletableFuture<Void> future = new CompletableFuture<>();
			future.completeExceptionally(new McpError("Tool name must not be null"));
			return future;
		}
		if (this.serverCapabilities.getTools() == null) {
			CompletableFuture<Void> future = new CompletableFuture<>();
			future.completeExceptionally(new McpError("Server must be configured with tool capabilities"));
			return future;
		}

		return CompletableFuture.supplyAsync(() -> {
			boolean removed = this.tools.removeIf(spec -> spec.getTool().getName().equals(toolName));
			if (!removed) {
				throw new CompletionException(new McpError("Tool with name '" + toolName + "' not found"));
			}
			this.toolsByName.remove(toolName);
			logger.debug("Removed tool handler: {}", toolName);
			return null;
		}).thenCompose(ignored -> {
			if (this.serverCapabilities.getTools().getListChanged()) {
				return notifyToolsListChanged();
			}
			return CompletableFuture.completedFuture(null);
		}).exceptionally(ex -> {
			Throwable cause = (ex instanceof CompletionException) ? ex.getCause() : ex;
			logger.error("Error while removing tool '{}'", toolName, cause);
			throw new CompletionException(cause);
		});
	}

	public CompletableFuture<Void> notifyToolsListChanged() {
		logger.debug("Notifying clients about tool list changes");

View on GitHub (pinned to 21cf2e9ba5)

Solutions

  1. Check the current tool list before calling removeTool.
  2. Treat not-found as acceptable by catching McpError and ignoring it.
  3. Verify the exact (case-sensitive) tool name before removing.

Example fix

// before
server.removeTool("arthas.dump").join(); // not found

// after
boolean exists = server.getTools().stream()
    .anyMatch(t -> t.getTool().getName().equals("arthas.dump"));
if (exists) server.removeTool("arthas.dump").join();
Defensive patterns

Strategy: try-catch

Validate before calling

boolean exists = server.getTools().stream()
    .anyMatch(t -> t.getTool().getName().equals(name));
if (exists) server.removeTool(name).join(); // else no-op

Try / catch

try { server.removeTool(name).join(); }
catch (CompletionException e) {
  if (e.getCause() instanceof io.modelcontextprotocol.spec.McpError && e.getCause().getMessage().contains("not found")) { /* ignore */ }
  else throw e;
}

Prevention

When it happens

Trigger: Thrown at arthas-mcp-server/src/main/java/com/taobao/arthas/mcp/server/protocol/server/McpNettyServer.java:303 when the library encounters an invalid state.

Common situations: Stale UI/client calling remove for a tool that was never added or already removed; a race where the tool was removed concurrently; a typo in the tool name.

Related errors


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