fff9f5211619cbb9d7cd51920cda50c6c1c2244c
Every test drives the real Spring AI advisor classes (ToolCallingAdvisor,
ToolSearchToolCallingAdvisor) against a hand-written ScriptedChatModel that queues
real ChatResponse objects instead of calling a live LLM -- confirmed viable because
ChatModel has exactly one abstract method, call(Prompt) (checked with javap).
Covers:
- The plain call/execute/recall loop (WeatherTools, an ordinary @Tool method)
- @Tool(returnDirect = true) skipping the second model round trip entirely
(ServerStatusTools)
- ToolContext: excluded from the model-facing JSON schema (verified against the
real generated schema), still delivered to the tool from caller-supplied data
(UserContextTools)
- A 230-tool synthetic library across six fake domains, generated via
FunctionToolCallback.builder(...) (LargeToolLibrary)
- ToolSearchToolCallingAdvisor + RegexToolIndex: one tool ("toolSearchTool")
offered on the first call instead of 230, with real tool-count and
character-footprint measurements taken off the actual outgoing prompts
Findings recorded in the module's Javadoc rather than silently worked around:
- ToolCallingAdvisor only engages when the request's Prompt carries
ToolCallingChatOptions, built from ChatModel.getOptions().mutate() (not
getDefaultOptions(), a separate default method the request-building path never
calls) -- confirmed by disassembling ToolCallingAdvisor.adviseCall and
DefaultChatClientUtils
- The Tool Search Advisor's own tool is named "toolSearchTool" (camelCase), not
"tool_search_tool" -- confirmed via @Tool(name=...) in the decompiled class
- Its session ID comes from ChatClientRequest.context() (AdvisorSpec.param), not
from ChatClient.toolContext(Map) -- confirmed by disassembling
ToolSearchToolCallingAdvisor.initializeSession
- RegexToolIndex matches on verb/noun substrings, not semantic relevance -- a
real captured search for "look up an invoice" returned 5 lookup_-named tools
across three unrelated domains alongside the one actually wanted
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01FtpJvZfg4nvLvtzgJTDWpB
spring-ai
Runnable companion code for the Spring AI articles on ankurm.com. One directory per module; each module is one commit and carries its own README, tests and captured output.
| Module | What it is | Article |
|---|---|---|
getting-started/ |
One ChatClient bean, three endpoints (plain call, templated system prompt, streaming), and a test proving spring.ai.model.chat switches providers with zero code change. Spring Boot 4.1.1, Spring AI 2.0.1, Java 25. |
Spring AI 2.0 in 10 Minutes: ChatClient on Spring Boot 4.1 |
rag/ |
Ingest PDFs, chunk, retrieve from pgvector, rerank, answer, check the answer. Spring Boot 4.1.1, Spring AI 2.0.1, Java 25. | Production-grade RAG with Spring AI and the complete example |
mcp-server/ |
An order-lookup service exposed as MCP tools, a resource, and a prompt with @McpTool/@McpResource/@McpPrompt, served over Streamable HTTP (Spring AI 2.0's default MCP server transport). Spring Boot 4.1.1, Spring AI 2.0.1, Java 25. |
Build an MCP Server with Spring AI 2.0 |
mcp-client/ |
ChatClient calling tools from two real external MCP servers (filesystem, git) over stdio via defaultToolCallbacks(ToolCallbackProvider...), contrasted with a local @Tool method, with every call logged through one Micrometer ObservationHandler. Spring Boot 4.1.1, Spring AI 2.0.1, Java 25. |
Spring AI MCP Client: Calling External MCP Servers from ChatClient |
mcp-secure/ |
The mcp-server article's order-lookup tools behind a real OAuth2 resource server: JWT validation, one scope per tool via @PreAuthorize, unauthenticated tool discovery rejected outright, and every call audit-logged through MDC -- denials included. Spring Boot 4.1.1, Spring AI 2.0.1, Spring Security 7.1.1, Java 25. |
Securing an MCP Server with Spring Security 7 |
tool-calling/ |
@Tool methods, ToolCallingAdvisor (the advisor-layer replacement for Spring AI 1.x's per-model tool loop), returnDirect, ToolContext, and ToolSearchToolCallingAdvisor for progressive disclosure across a 230-tool synthetic library -- every test driven by a hand-written ScriptedChatModel, no live model anywhere. Spring Boot 4.1.1, Spring AI 2.0.1, Java 25. |
Tool Calling in Spring AI 2.0 |
Upgrading from Spring AI 1.x: migration guide.
Languages
Java
94.3%
Shell
5.7%