{"record":{"id":"451c6b78e3e06e5e","repo":"spring-projects/spring-ai","slug":"no-tool-methods-found-in-the-provided-tool-objects-451c6b","errorCode":null,"errorMessage":"No tool methods found in the provided tool objects: + this.toolObjects","messagePattern":"No tool methods found in the provided tool objects: \\+ this\\.toolObjects","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/provider/tool/SyncStatelessMcpToolProvider.java","lineNumber":158,"sourceCode":"\n\t\t\t\t\tReturnMode returnMode = useStructuredOtput ? ReturnMode.STRUCTURED\n\t\t\t\t\t\t\t: (methodReturnType == Void.TYPE || methodReturnType == void.class ? ReturnMode.VOID\n\t\t\t\t\t\t\t\t\t: ReturnMode.TEXT);\n\n\t\t\t\t\tBiFunction<McpTransportContext, CallToolRequest, CallToolResult> methodCallback = new SyncStatelessMcpToolMethodCallback(\n\t\t\t\t\t\t\treturnMode, mcpToolMethod, toolObject);\n\n\t\t\t\t\tvar toolSpec = SyncToolSpecification.builder().tool(tool).callHandler(methodCallback).build();\n\n\t\t\t\t\treturn toolSpec;\n\t\t\t\t})\n\t\t\t\t.toList())\n\t\t\t.flatMap(List::stream)\n\t\t\t.toList();\n\n\t\tif (toolSpecs.isEmpty()) {\n\t\t\tif (logger.isWarnEnabled()) {\n\t\t\t\tlogger.warn(\"No tool methods found in the provided tool objects: \" + this.toolObjects);\n\t\t\t}\n\t\t}\n\n\t\treturn toolSpecs;\n\t}\n\n}\n","sourceCodeStart":140,"sourceCodeEnd":166,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/provider/tool/SyncStatelessMcpToolProvider.java#L140-L166","documentation":"Same warning as the sync provider, logged by SyncStatelessMcpToolProvider.getToolSpecifications when reflection over the supplied tool objects produces no tool specifications. Because this is the stateless provider, each request will run with an empty tool set rather than failing fast.","triggerScenarios":"Constructing SyncStatelessMcpToolProvider with objects lacking @McpTool-annotated methods; annotation scanning fails due to missing mcp-annotations metadata or wrong annotation import; toolObjects list is empty or contains unrelated beans.","commonSituations":"Stateless HTTP MCP deployments where tool beans are wired by configuration and a profile/config change drops the real tool beans; refactoring removed the @McpTool annotations; version mismatch between the annotation and provider modules.","solutions":["Confirm every tool object exposes at least one @McpTool-annotated method","Inspect the bean wiring (e.g. Spring config) feeding toolObjects to ensure real tool beans are injected","Align mcp-annotations and spring-ai-mcp module versions","Check annotation imports — @McpTool must come from org.springframework.ai.mcp.annotation, not a similarly named class"],"exampleFix":"// before\n@Bean\nSyncStatelessMcpToolProvider provider() {\n    return SyncStatelessMcpToolProvider.builder()\n        .toolObjects(new Object()) // no @McpTool methods\n        .build();\n}\n// after\n@Bean\nSyncStatelessMcpToolProvider provider(WeatherTools weatherTools) {\n    return SyncStatelessMcpToolProvider.builder()\n        .toolObjects(weatherTools) // class with @McpTool methods\n        .build();\n}","handlingStrategy":"validation","validationCode":"// validate before wiring the stateless provider\nif (toolObjects == null || toolObjects.length == 0)\n    throw new IllegalArgumentException(\"toolObjects must not be empty\");\nboolean anyTools = Arrays.stream(toolObjects)\n    .flatMap(o -> Arrays.stream(o.getClass().getDeclaredMethods()))\n    .anyMatch(m -> m.isAnnotationPresent(McpTool.class));\nif (!anyTools) throw new IllegalStateException(\"No @McpTool methods in tool objects\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep tool classes dedicated and clearly annotated with @McpTool","Add a unit test asserting getToolSpecifications().size() > 0","Pin consistent versions of mcp-annotations and provider modules","Review Spring config profiles that might swap in empty tool bean lists"],"tags":["mcp","tools","annotation","stateless"],"backgroundTag":"empty-required-field","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}