{"record":{"id":"9a95e284a3a1ea4c","repo":"spring-projects/spring-ai","slug":"no-tool-methods-found-in-the-provided-tool-objects","errorCode":null,"errorMessage":"No tool methods found in the provided tool objects: <toolObjects>","messagePattern":"No tool methods found in the provided tool objects: <toolObjects>","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/provider/tool/AsyncMcpToolProvider.java","lineNumber":161,"sourceCode":"\t\t\t\t\t\t\t\t\t: ReturnMode.TEXT;\n\n\t\t\t\t\tBiFunction<McpAsyncServerExchange, CallToolRequest, Mono<CallToolResult>> methodCallback = new AsyncMcpToolMethodCallback(\n\t\t\t\t\t\t\treturnMode, mcpToolMethod, toolObject);\n\n\t\t\t\t\tAsyncToolSpecification toolSpec = AsyncToolSpecification.builder()\n\t\t\t\t\t\t.tool(tool)\n\t\t\t\t\t\t.callHandler(methodCallback)\n\t\t\t\t\t\t.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":143,"sourceCodeEnd":169,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/provider/tool/AsyncMcpToolProvider.java#L143-L169","documentation":"AsyncMcpToolProvider.getToolSpecifications logs this warning when scanning the configured tool objects yields zero @McpTool-annotated methods, producing an empty tool specification list. The MCP server will expose no tools from this provider; the library flags it because a registered tool provider with no tools is almost always a wiring mistake.","triggerScenarios":"Passing objects without any public @McpTool methods to AsyncMcpToolProvider, annotated methods filtered out (visibility/signature), or an empty toolObjects list — empty toolSpecs triggers the warn at AsyncMcpToolProvider.java:161.","commonSituations":"Tool class not registered with the provider; @McpTool annotation missing or wrong import; tool methods private; upgrade renamed annotation attributes causing scan mismatch.","solutions":["Verify each tool object has at least one public method annotated with @McpTool.","Confirm the tool object list passed to AsyncMcpToolProvider is non-empty and contains the intended beans.","Check method visibility and signatures match the scanner's requirements.","Remove the tool provider if no tools are intended."],"exampleFix":"// before\nclass Tools { public String weather(String city) { ... } } // no @McpTool\n// after\nclass Tools { @McpTool(description = \"Get weather\") public String weather(@McpToolParam String city) { ... } }","handlingStrategy":"validation","validationCode":"// Pre-flight check for tool methods before creating AsyncMcpToolProvider:\nlong tools = Arrays.stream(toolObject.getClass().getDeclaredMethods())\n    .filter(m -> m.isAnnotationPresent(McpTool.class) && Modifier.isPublic(m.getModifiers()))\n    .count();\nif (tools == 0) throw new IllegalStateException(toolObject + \" exposes no @McpTool methods\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Annotate tool methods with @McpTool and parameters with @McpToolParam.","Register the class that actually contains the tool methods.","Keep tool methods public; the scanner skips non-public ones.","Add a test asserting toolSpecifications() is non-empty for each provider."],"tags":["mcp","annotation-scanning","tools","empty-specifications"],"backgroundTag":"empty-result-set","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"}