DEV Community

Piyush Kumar Singh
Piyush Kumar Singh

Posted on • Originally published at Medium

Spring AI Tool Calling Explained | How to Give Your LLM Real Superpowers

Ask ChatGPT what Apple’s stock price is right now. It’ll either tell you it doesn’t have real-time data or confidently give you a number that’s months old. That’s not a bug in the model. It’s a fundamental limitation of how LLMs work. They’re trained on data up to a certain date, sealed, and shipped. They can’t call your database. They can’t check the weather. They can’t look up a customer’s order status. They know nothing about the world after their training cutoff — and nothing about your world at all.

Tool calling is how you fix that. And it’s one of the core building blocks of what people are calling AI agents — systems where an LLM can take action, not just generate text.

What tool calling actually is

Before diving into Spring AI, let’s get the concept straight in plain language.

Imagine you hire an extremely smart consultant. They know a lot — strategy, frameworks, history, writing. But they don’t have access to your company’s internal systems. They can’t log into your CRM and check whether a customer paid last month.

So you make an arrangement: whenever they need internal data to answer a question, they hand you a sticky note saying, “I need order #4521’s status.” You go look it up. You bring them the answer. They use it to finish their response.

That sticky note exchange is tool calling. You may also see it called function calling — same concept, different name depending on which LLM provider’s docs you’re reading.

The LLM doesn’t execute code. It doesn’t call APIs. It doesn’t touch your database directly. It just says, “I need this specific piece of information from this specific function.” Your application handles the actual execution and returns the result. The LLM then uses that result to write its final response.

That distinction — the model asks, your app executes — is the most important thing to understand about tool calling. And most tutorials skip right past it.
The 5-step loop — what happens inside every tool call

Spring AI Tool call

The diagram above shows the full cycle. Here’s each step in plain terms:

Step 1 — User asks a question. “What is Apple’s stock price right now?”

Step 2 — The LLM reads the question and your tool list. When you register tools with Spring AI, each one has a name and a description. The LLM reads those descriptions and makes a decision: can I answer this from my training data, or do I need to call a tool?

Step 3 — The LLM returns a tool request, not an answer. This is the part people don’t expect. Instead of answering your question, the model sends back something like: “Call the getStockPrice function with argument AAPL.” Just a tool name and arguments. No answer yet.

Step 4 — Your Spring app executes the method. Spring AI picks up the tool request, finds the matching @Tool method in your code, calls it, and gets the real data — from your API, your database, whatever you wired in.

Step 5 — The result goes back to the LLM. Spring AI sends the function’s return value back into the conversation context. The LLM reads it and now writes its actual response: “Apple is trading at $213.45 as of this morning.”

The user sees a real, grounded answer. No hallucination. No outdated training data. The model provided the intelligence, your application provided the data.

The @Tool annotation: registering tools in Spring AI

In Spring AI, turning a regular Java method into a tool the LLM can call takes one annotation:

@Component
public class StockPriceTool {

@Tool(description = "Get the current stock price for a given ticker symbol")
    public String getStockPrice(
            @ToolParam(description = "The stock ticker symbol, e.g. AAPL, TSLA")
            String ticker) {
        // call a real stock API here
        return financialApiClient.getCurrentPrice(ticker);
    }
}
Enter fullscreen mode Exit fullscreen mode

Two things here that most tutorials don’t explain:

The description on @Tool is not documentation for you — it’s instructions for the LLM. The model reads this text to decide when to call your function. A vague description like “gets stock data” will confuse the model. A precise one like “Get the current stock price for a given ticker symbol” tells the model exactly when this tool applies. Treat it like a prompt, not a comment.

The same goes for @ToolParam. The LLM reads your parameter descriptions to understand what values it should pass. If your description is unclear, the model will pass the wrong arguments. Bad input = bad output.

Registering your tools with ChatClient

Once your tool class is ready, you wire it into ChatClient through your configuration:

@Configuration
public class AiConfig {

    @Autowired
    private StockPriceTool stockPriceTool;

    @Bean
    public ChatClient chatClient(ChatClient.Builder builder) {
        return builder
            .defaultTools(stockPriceTool)
            .build();
    }
}
Enter fullscreen mode Exit fullscreen mode

Spring AI scans the bean, finds every method annotated with @Tool, and registers them automatically. No verbose schema definitions, no manual type mappings — just your annotated method and a single line in your configuration.

What the LLM reads and why description quality is everything

Think about what happens from the model’s perspective. Before answering any question, it sees something like this:

Available tools:
- getStockPrice: Get the current stock price for a given ticker symbol
- getWeatherForecast: Get 3-day weather forecast for a city
- getUserOrderHistory: Get the last 10 orders for a given customer ID
Enter fullscreen mode Exit fullscreen mode

That list of descriptions is the only information the model has about what your tools can do. It uses those descriptions to reason about which tool — if any — applies to the current question.

If someone asks, “Is it going to rain in Mumbai tomorrow?” — the model reads the descriptions, matches them to getWeatherForecast, and calls it.

If someone asks, “Should I carry an umbrella to the client meeting?” — the model has to infer that “client meeting” implies weather concern, decide Mumbai is a reasonable assumption if that’s the user’s location, and call the right tool accordingly.

All of that reasoning happens against your description strings. Write them well, and the model becomes genuinely useful. Write them carelessly, and the model either calls the wrong tool, passes wrong arguments, or skips tools it should use.

When the LLM chains tools and the security

Once you register multiple tools, something interesting happens: the LLM can call them in sequence. It might call getCustomerProfile first, get the customer’s account tier, then call getOrderHistory with that tier to filter the results, then call calculateDiscount based on the history.

This is the foundation of what people call AI agents — LLMs that don’t just answer questions but complete multi-step tasks by composing tools. Spring AI’s approach to this connects naturally with MCP (Model Context Protocol), a standard for exposing tools to LLMs in a consistent, discoverable way — but that’s a deeper topic for another article.

Here’s the security consideration that almost every tutorial skips: the LLM cannot call your database directly. It cannot make HTTP requests. It cannot run arbitrary code. Every tool execution goes through your Spring code — which means every tool execution can be authenticated, rate-limited, logged, and validated exactly like any other function call in your system.

That’s not a limitation. That’s the design. You can safely expose powerful capabilities to the LLM without handing over the keys to your infrastructure.

When NOT to use the tool calling

Don’t use a tool calling for questions the LLM can answer from its training. Every tool call adds a round trip — your app waits for the LLM to respond, executes the function, and sends a second request back. That’s real latency. If someone asks, “explain what a P/E ratio is”, there’s no need to call a financial API. Reserve tool calls for data the model genuinely can’t have.

Watch out for circular tool chains. If Tool A can trigger Tool B, which can trigger Tool A again under certain conditions, you can end up in a loop. Spring AI doesn’t automatically break cycles. Define clear, narrow tool responsibilities to avoid this.

Description drift is a real maintenance problem. When your underlying function changes — new parameters, changed logic, different return format — you need to update your @Tool description to match. The LLM reasons from the description, not the code. Stale descriptions lead to bad arguments and broken calls.

Latency stacks with every hop. One tool call adds roughly 500ms–1s. Three tool calls in sequence can add 2–3 seconds. For user-facing chatbots, that’s noticeable. Design your tools to be composable but not deeply chained unless the use case genuinely requires it.

Spring AI 2.x — a note if you’re upgrading

If you’ve followed a tool-calling tutorial written before mid-2025 and the code isn’t working, this is likely why.

The old approach used FunctionCallback and verbose bean registration, long configuration chains, manual input/output type definitions, and FunctionCallbackWrapper setup. Spring AI has moved away from all of that.

The old .tools(“toolName”) syntax no longer works reliably in Spring AI 1.0 GA. If your tools aren’t being picked up, check that you’re passing the bean directly to defaultTools(myToolBean), not a string name.

The new annotation-based approach shown in this article is the current recommended path. It’s cleaner, requires no manual schema definitions, and fits naturally into how Spring Boot beans already work. If you’re migrating from an older version, the change is mostly mechanical: replace FunctionCallback registrations with @Tool annotated methods and update your ChatClient configuration.

Top comments (1)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.