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
- Check the current tool list before calling removeTool.
- Treat not-found as acceptable by catching McpError and ignoring it.
- 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
- Check existence before removeTool.
- Treat not-found as idempotent success.
- Verify exact case-sensitive tool name.
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
- Tool with name '${toolName}' already exists
- Resource with URI '${resourceUri}' already exists
- Resource with URI '${resourceUri}' not found
- Prompt with name '${promptName}' already exists
- Prompt with name '${promptName}' not found
AI-assisted analysis of alibaba/arthas@21cf2e9ba5 (2026-08-14).
Data as JSON: /api/errors/8ca1b50f608a015d.
Report an issue: GitHub.