{"record":{"id":"0c7bf3982e7e7502","repo":"alibaba/spring-ai-alibaba","slug":"llm-may-have-adapted-the-tool-name-especiall-0c7bf3","errorCode":null,"errorMessage":"LLM may have adapted the tool name '{}', especially if the name was truncated due to length limits. If this is the case, you can customize the prefixing and processing logic using McpToolNamePrefixGenerator","messagePattern":"LLM may have adapted the tool name '(.+?)', especially if the name was truncated due to length limits\\. If this is the case, you can customize the prefixing and processing logic using McpToolNamePrefixGenerator","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/node/AgentToolNode.java","lineNumber":546,"sourceCode":"\t * @return the tool call response\n\t */\n\tprivate ToolCallResponse executeToolCallWithInterceptors(AssistantMessage.ToolCall toolCall, OverAllState state,\n\t\t\tRunnableConfig config, Map<String, Object> extraStateFromToolCall, boolean inParallelExecution,\n\t\t\tMap<Integer, DefaultCancellationToken> cancellationTokens, int toolIndex) {\n\n\t\t// Create ToolCallRequest\n\t\tToolCallRequest request = ToolCallRequest.builder()\n\t\t\t\t.toolCall(toolCall)\n\t\t\t\t.context(config.metadata().orElse(new HashMap<>()))\n\t\t\t\t.executionContext(new ToolCallExecutionContext(config, state))\n\t\t\t\t.build();\n\n\t\t// Create base handler that actually executes the tool\n\t\tToolCallHandler baseHandler = req -> {\n\t\t\tToolCallback toolCallback = resolve(req.getToolName(), config);\n\n\t\t\tif (toolCallback == null) {\n\t\t\t\tlogger.warn(POSSIBLE_LLM_TOOL_NAME_CHANGE_WARNING, req.getToolName());\n\t\t\t\treturn ToolCallResponse.builder()\n\t\t\t\t\t.content(\"Tool not available: \" + req.getToolName())\n\t\t\t\t\t.toolName(req.getToolName())\n\t\t\t\t\t.toolCallId(req.getToolCallId())\n\t\t\t\t\t.status(\"error\")\n\t\t\t\t\t.metadata(Map.of(\"error\", true, \"unresolvedToolName\", req.getToolName()))\n\t\t\t\t\t.build();\n\t\t\t}\n\n\t\t\tif (enableActingLog) {\n\t\t\t\tlogger.info(\"[ThreadId {}] Agent {} acting, executing tool {}.\",\n\t\t\t\t\t\tconfig.threadId().orElse(THREAD_ID_DEFAULT), agentName, req.getToolName());\n\t\t\t}\n\n\t\t\tMap<String, Object> toolContextMap = new HashMap<>(toolContext);\n\t\t\ttoolContextMap.putAll(req.getContext());\n\n\t\t\t// Handle tools that need state injection:","sourceCodeStart":528,"sourceCodeEnd":564,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/node/AgentToolNode.java#L528-L564","documentation":"When handling a model tool call, AgentToolNode.executeToolCallWithInterceptors resolves the requested tool name against configured callbacks. If no callback matches, it logs the POSSIBLE_LLM_TOOL_NAME_CHANGE_WARNING — the LLM may have altered, truncated, or hallucinated the tool name (common with long names that get prefixed, e.g. by MCP's McpToolNamePrefixGenerator) — and returns an error ToolCallResponse ('Tool not available: <name>') back to the model instead of throwing.","triggerScenarios":"executeToolCallWithInterceptors' base handler calls resolve(req.getToolName(), config), which returns null because the model emitted a tool name not registered on the node — due to prefix mismatch, name truncation, case differences, or the tool never being registered.","commonSituations":"1) MCP tools whose names exceed length limits and get truncated/prefixed inconsistently between registration and call. 2) Registering tools on one AgentLlmNode but not wiring them into AgentToolNode's config. 3) LLM hallucinating a similar tool name under a large tool set. 4) Renaming a tool without restarting/rebuilding the agent so the model's cached description uses the old name.","solutions":["Verify the tool name exists at call time: print the registered names (config) and compare with req.getToolName() — look for prefix/truncation differences.","Shorten MCP tool names or customize McpToolNamePrefixGenerator so prefixed names stay within the model/provider's length limits.","Register the missing tool callback on the node that executes calls, keeping registration and execution config in sync.","Optionally add fuzzy name resolution (case-insensitive/starts-with match) in your resolve path for robustness."],"exampleFix":"// before\nToolCallback resolve(String name, Config c) { return byExactName(c, name); }\n// after\nToolCallback resolve(String name, Config c) {\n    ToolCallback t = byExactName(c, name);\n    if (t == null) t = byCaseInsensitiveOrPrefix(c, name); // handle LLM name drift\n    return t;\n}","handlingStrategy":"validation","validationCode":"static void verifyRegistered(Collection<String> requested, Collection<String> registered) {\n    List<String> missing = requested.stream().filter(n -> !registered.contains(n)).toList();\n    if (!missing.isEmpty()) throw new IllegalStateException(\"Unregistered tools: \" + missing);\n}","typeGuard":"static ToolCallback resolveTolerant(String name, List<ToolCallback> tools) {\n    return tools.stream().filter(t -> t.getToolDefinition().name().equals(name)).findFirst()\n        .or(() -> tools.stream().filter(t -> t.getToolDefinition().name().equalsIgnoreCase(name)).findFirst())\n        .or(() -> tools.stream().filter(t -> t.getToolDefinition().name().endsWith(name)).findFirst())\n        .orElse(null);\n}","tryCatchPattern":null,"preventionTips":["Keep tool registration and execution config in sync (same node/source)","Keep MCP tool names short; customize McpToolNamePrefixGenerator when prefixes cause truncation","Log registered tool names at startup and diff against model-declared tools","Treat the returned 'error' ToolCallResponse as a signal to re-list tools for the model"],"tags":["tool-call","mcp","tool-name","llm"],"backgroundTag":"resource-not-found","analyzedSha":"f82da0b50f35744c13968191be2b1cd2452ef550","analyzedAt":"2026-09-09T15:32:42.421Z","contentChangedAt":"2026-09-09T15:32:42.421Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}