Understand Spring AI Tool Mechanism in 5 Minutes: From @Tool Annotation to MCP Tool Bridging Full Chain
3/26/2026
ๆฅ็่ฟ็ฏๆ็ซ ็ไธญๆ็ๆฌIntroduction
โWhy isnโt my @Tool method being recognized?โ
โHow do MCP Server tools bridge to my local ChatClient?โ
โWhatโs the relationship between ToolCallback and ToolCallbackProvider?โ
These are common confusions for developers new to Spring AIโs Tool mechanism.
Official documentation teaches you how to use it, but rarely explains how it works under the hood. The result is not knowing where to start troubleshooting when problems arise.
In this article, Iโll use plain language to help you understand Spring AI Toolโs underlying runtime mechanism. After reading, youโll understand:
- How a
@Toolannotation becomes an AI-callable tool - The responsibilities and use cases for each type of Provider
- The complete chain from tool registration to LLM invocation
Version Note (as of 2026-03-22): Spring AIโs latest stable version is 1.1.3, and latest preview version is 2.0.0-M3 (released on 2026-03-17).
1.1.2is also a stable version, just not the latest stable version as of 2026-03-22. This article is primarily based on the current official Reference/API documentation; for 2.0.0-M3 changes, Iโll mark them separately to avoid mixing โstable version statusโ with โpreview version changesโ.If your current project uses
spring-ai-alibaba:1.1.2.0, note that its transitiveorg.springframework.aidependency is still1.1.2, not1.1.3. So APIs likeAugmentedToolCallbackthat are visible in1.1.3may not appear in your IDE yet.
1. Core Interfaces: ToolCallback and ToolCallbackProvider
Bottom line first: ToolCallback is a single tool, ToolCallbackProvider is a provider of tool collections.
1.1 ToolCallback: Abstraction of a Single Tool
ToolCallback is the smallest unit in Spring AIโs tool system, representing a tool that can be called by an AI model.
public interface ToolCallback {
// Tool definition: name, description, JSON Schema for input parameters
ToolDefinition getToolDefinition();
// Tool metadata: e.g., returnDirect controls whether results are returned directly
ToolMetadata getToolMetadata();
// Execute the tool, input is a JSON string
String call(String toolInput);
// Execute with context
String call(String toolInput, ToolContext toolContext);
}
Its responsibilities are simple:
- Tell the AI what this tool is called, what it does, and what parameters it needs
- Receive JSON parameters from the AI, execute logic, return results
Two most common local implementations:
MethodToolCallbackโ Wraps Java methods (used with@Toolannotation)FunctionToolCallbackโ Wraps functional interfaces (Function,Supplier,Consumer,BiFunction, etc.)
1.2 ToolCallbackProvider: Tool Factory
ToolCallbackProvider is responsible for providing tools in batches.
public interface ToolCallbackProvider {
// Return all tool callbacks
ToolCallback[] getToolCallbacks();
}
Its responsibility: Collect tools from different sources and expose them uniformly for ChatClient registration.
2. Five Providers: Four Source Types + One Enhancer
In the current official API, ToolCallbackProvider has 5 known implementations. The first four handle โwhere tools come fromโ, and the last one handles โwrapping existing tools with enhancementsโ.
| Provider | Tool Source | Characteristics |
|---|---|---|
| StaticToolCallbackProvider | Static array | Simplest, immutable after construction, manually create fixed tool sets |
| MethodToolCallbackProvider | @Tool annotated methods | Automatically scans objects for @Tool methods and generates ToolCallbacks |
| SyncMcpToolCallbackProvider | MCP synchronous client | Connects to remote MCP Server, automatically discovers and bridges tools |
| AsyncMcpToolCallbackProvider | MCP asynchronous client | Similar to Sync but uses async client; also implements getToolCallbacks(), plus provides reactive helper capabilities |
| AugmentedToolCallbackProvider | Enhancement wrapper for existing tools | Appends input Schema extension fields to existing tools, e.g., additional structured metadata |
3. MethodToolCallbackProvider: Local Annotation Scanning
This is the most commonly used approach, automatically generating tools through @Tool annotations.
@Service
public class WeatherService {
@Tool(name = "getWeather", description = "Get weather for a specified city")
public String getWeather(@JsonProperty("city") String city) {
return city + ": Sunny, 25\u00B0C";
}
@Tool(name = "query_weather_by_city_date", description = "Query weather by city and date")
public String queryByCityAndDate(
@JsonProperty("city") String city,
@JsonProperty("date") String date) {
return city + " on " + date + ": Cloudy, 22\u00B0C";
}
}
Registration method:
@Configuration
public class ToolConfig {
@Bean
public ToolCallbackProvider weatherTools(WeatherService weatherService) {
return MethodToolCallbackProvider.builder()
.toolObjects(weatherService) // Pass the Service object
.build();
}
}
How it works:
MethodToolCallbackProvider uses reflection to scan all methods in weatherService, finds methods with @Tool annotation, and generates a MethodToolCallback for each method.
This example registers 2 tools:
getWeatherโ Query weather by cityquery_weather_by_city_dateโ Query weather by city and date
4. How to Register Multiple Services?
toolObjects() accepts varargs, you can pass multiple objects:
@Bean
public ToolCallbackProvider allTools(
WeatherService weatherService,
OrderService orderService,
TradeService tradeService,
GoodsService goodsService) {
return MethodToolCallbackProvider.builder()
.toolObjects(weatherService, orderService, tradeService, goodsService)
.build();
}
Method 2: Multiple Beans (Spring AI auto-merges)
@Bean
public ToolCallbackProvider weatherTools(WeatherService weatherService) {
return MethodToolCallbackProvider.builder()
.toolObjects(weatherService)
.build();
}
@Bean
public ToolCallbackProvider orderTools(OrderService orderService) {
return MethodToolCallbackProvider.builder()
.toolObjects(orderService)
.build();
}
If youโre using Spring Bootโs auto-configured MCP Server / MCP Client scenario, the framework collects these ToolCallbackProvider beans and merges them, achieving the same effect.
If youโre manually building a ChatClient, you still need to explicitly pass the ToolCallback[] returned by these Providers to defaultToolCallbacks(...).
5. SyncMcpToolCallbackProvider: Remote Tool Bridging
This is the core of MCP development, wrapping remote MCP Server tools as local ToolCallbacks.
@Component
public class McpClientService {
@Autowired
private SyncMcpToolCallbackProvider toolCallbackProvider;
@PostConstruct
public void init() {
// Get all tools from remote MCP servers
ToolCallback[] toolCallbacks = toolCallbackProvider.getToolCallbacks();
// Register to ChatClient
this.chatClient = ChatClient.builder(chatModel)
.defaultToolCallbacks(toolCallbacks)
.build();
}
}
What it does:
MCP servers configured in application.yml
โ
Spring AI auto-configures SyncMcpToolCallbackProvider
โ
Reads configuration, connects to each MCP Server (starts process or establishes HTTP connection)
โ
Calls MCP's listTools API to get tool list
โ
Wraps each remote tool as a local ToolCallback
โ
Registers to ChatClient, LLM can call them
Result:
chatClient.prompt()
.user("What's the weather in Beijing?")
.call()
.content();
// LLM automatically calls the remote mcp-server's getWeather tool
// Returns: "Beijing: Sunny, 25\u00B0C"
Sync vs Async:
SyncMcpToolCallbackProviderโ UsesMcpSyncClient,getToolCallbacks()returnsToolCallback[]AsyncMcpToolCallbackProviderโ UsesMcpAsyncClient, also implementsgetToolCallbacks()returningToolCallback[]; additionally providesasyncToolCallbacks(List<McpAsyncClient>)returningFlux<ToolCallback>
Note: Both can connect to multiple MCP Servers. Your choice depends on whether your application is synchronous or reactive. See Chapter 8 for details.
6. MCP Server vs Client: Each Has Its Role
Many beginners confuse this: Both MCP Server and Client use Providers, will they conflict?
The answer is no. They have a complementary relationship, each responsible for one side:
6.1 MCP Server Side: Two Main Paths Available Now
Path A: @McpTool annotation + annotation scanner
โ
Directly exposed as MCP Tool (this is the approach currently recommended by the Boot Starter docs)
Path B: Spring AI ToolCallback path
@Service + @Tool method / ToolCallback Bean / ToolCallbackProvider Bean
โ
Auto-converted to MCP Tool spec via tool-callback-converter
โ
Exposed to clients via MCP protocol (stdio / SSE / Streamable-HTTP)
6.2 MCP Client Side: Consuming Tools
Connects to MCP Server
โ
Gets tool list via MCP protocol (listTools)
โ
SyncMcpToolCallbackProvider (remote tools โ local ToolCallback)
โ
Registers to ChatClient, enabling LLM to call them
6.3 One Diagram to Understand Both Sides

6.4 Simple Analogy
| Role | Analogy | Classes Used |
|---|---|---|
| MCP Server | Restaurant (exposes menu items) | @McpTool or ToolCallbackProvider |
| MCP Client | Food delivery platform (aggregates restaurants) | SyncMcpToolCallbackProvider |
- A restaurant can either directly use
@McpToolto produce its menu, or first produceToolCallbacks, then have the Server Starter auto-convert them to MCP Tools - The delivery platform uses
SyncMcpToolCallbackProviderto turn each restaurantโs โmenuโ into โorderable itemsโ (localToolCallbacks) for users
6.5 What If Server Side Doesnโt Use MethodToolCallbackProvider?
A prerequisite here: This question only applies when you choose the @Tool โ MethodToolCallbackProvider โ MCP Tool path.
// Without MethodToolCallbackProvider
public class WeatherService {
@Tool(name = "getWeather", description = "...")
public String getWeather(String city) { ... }
}
Result: The @Tool annotation alone wonโt automatically become an MCP Tool. If you havenโt configured MethodToolCallbackProvider, nor wrapped it as another ToolCallback Bean / ToolCallbackProvider Bean, the clientโs listTools will return an empty list.
But if you take the @McpTool + annotation scanner path, you donโt need MethodToolCallbackProvider.
7. Complete Call Chain: From Registration to Execution
The following flow diagram uses the @Tool / ToolCallback โ ChatClient โ tool calling path as the main thread, connecting all stages:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Registration Phase โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
@Service + @Tool annotated methods application.yml MCP config
โ โ
โผ โผ
MethodToolCallbackProvider SyncMcpToolCallbackProvider
โ โ
โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โผ
ToolCallback[]
โ
โผ
ChatClient.builder()
.defaultToolCallbacks()
โ
โผ
ChatClient initialization complete
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Invocation Phase โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
User: "What's the weather in Beijing?"
โ
โผ
ChatModel sends request (carrying tool definitions: name + description + inputSchema)
โ
โผ
AI model decides: needs to call getWeather tool
โ
โผ
AI returns tool call request (tool name + JSON parameters)
โ
โผ
ToolCallingManager receives
โ
โผ
ToolCallbackResolver finds the corresponding ToolCallback
โ
โผ
ToolCallback.call('{"city":"Beijing"}')
โ
โผ
Returns: "Beijing: Sunny, 25\u00B0C"
โ
โผ
ToolResponseMessage sent back to AI model
โ
โผ
AI generates final response: "The weather in Beijing today is sunny, temperature 25\u00B0C"
8. Five ToolCallback Types: Whatโs Each For?
We covered Providers above; now letโs look at how the ToolCallbacks they generate differ:
8.1 MethodToolCallback
Purpose: Wraps @Tool annotated Java methods
@Tool(name = "getWeather", description = "Get weather")
public String getWeather(String city) {
return city + ": Sunny, 25\u00B0C";
}
// โ Wrapped into
MethodToolCallback
On execution: Directly invokes Java method via reflection
8.2 FunctionToolCallback
Purpose: Encapsulates existing code functionality through functional interfaces
// Call an existing Service method
FunctionToolCallback.builder("calculatePrice", (Function<String, BigDecimal>) input -> {
return orderService.calculatePrice(input);
})
.build();
On execution: Calls Function.apply()
8.3 SyncMcpToolCallback vs AsyncMcpToolCallback
This is the pair most easily confused.
Both are used for bridging remote MCP Server tools, the difference is only in the underlying MCP client type:
| Comparison | SyncMcpToolCallback | AsyncMcpToolCallback |
|---|---|---|
| Underlying client | McpSyncClient | McpAsyncClient |
| Sync entry point for callers | getToolCallbacks() | getToolCallbacks() |
| Generated Provider | SyncMcpToolCallbackProvider | AsyncMcpToolCallbackProvider |
| Multiple MCP Server support | Yes | Yes |
| Additional reactive capability | None | asyncToolCallbacks(List<McpAsyncClient>) -> Flux<ToolCallback> |
Key point: Both can connect to multiple MCP Servers โ you donโt need to choose Async just for multi-Server support!
API Perspective
SyncMcpToolCallbackProvider:
public ToolCallback[] getToolCallbacks()
AsyncMcpToolCallbackProvider:
public ToolCallback[] getToolCallbacks()
public static Flux<ToolCallback> asyncToolCallbacks(List<McpAsyncClient> mcpClients)
In other words: The unified interface exposed externally is still ToolCallback[]; the Async version just additionally provides reactive streaming assembly capability.
8.4 AugmentedToolCallback
This is an implementation that has appeared in the current 1.1.3 API docs but many articles havenโt covered yet.
Note: If your project is still on
1.1.0or1.1.2, you likely wonโt seeAugmentedToolCallback/AugmentedToolCallbackProviderin your IDE. Iโve personally checked the Maven cache: these classes appear inspring-ai-model:1.1.3, but not in1.1.0/1.1.2.
Purpose: Appends input Schema extension fields to existing tools without changing the original tool method signature.
Typical scenarios:
- Adding structured metadata fields to tools
- Uniformly recording extra parameters during tool calls
- Extending the tool protocol without modifying original tool input parameters
The corresponding Provider at the underlying level is AugmentedToolCallbackProvider, with the related low-level tool being ToolInputSchemaAugmenter.
8.5 Which One Should I Use?
| Scenario | Recommendation |
|---|---|
| Spring MVC + standard scenarios | SyncMcpToolCallbackProvider |
| WebFlux reactive applications | AsyncMcpToolCallbackProvider |
| Need to connect to multiple MCP Servers | Either works |
| Need to append structured fields to existing tools | AugmentedToolCallbackProvider |
Conclusion: If youโre using Spring MVC, just go with SyncMcpToolCallbackProvider โ no need to switch architectures.
8.6 FunctionCallback Is Deprecated, How to Migrate?
Spring AI previously used FunctionCallback, later unified to the ToolCallback API. FunctionCallback is deprecated; migration is recommended.
Why was it deprecated?
Early on, different AI vendors used inconsistent naming. Later it was unified to โtool callingโ:
- OpenAI: function calling โ tool calling
- Anthropic: tool use โ tool calling
Spring AI aligned with this and deprecated FunctionCallback, unifying on ToolCallback.
Migration Method 1: @Tool Annotation (Recommended)
Old code (FunctionCallback):
@Component
public class WeatherFunction {
public String getWeather(String city) {
return city + ": Sunny, 25\u00B0C";
}
}
// Registration
FunctionCallback callback = FunctionCallback.builder()
.function("getWeather", weatherFunction)
.description("Get weather for a specified city")
.inputType(WeatherRequest.class)
.build();
New code (@Tool annotation):
@Service
public class WeatherService {
@Tool(name = "getWeather", description = "Get weather for a specified city")
public String getWeather(@JsonProperty("city") String city) {
return city + ": Sunny, 25\u00B0C";
}
}
// Registration - auto scanning
@Bean
public ToolCallbackProvider weatherTools(WeatherService weatherService) {
return MethodToolCallbackProvider.builder()
.toolObjects(weatherService)
.build();
}
Migration Method 2: FunctionToolCallback (Wrapping Existing Interfaces)
If you donโt want to modify existing code, use the wrapping approach:
// Existing Service, no changes needed
@Service
public class OrderService {
public BigDecimal calculatePrice(String orderId) {
// Original logic
return new BigDecimal("99.99");
}
}
// Wrap with FunctionToolCallback
@Configuration
public class ToolConfig {
@Bean
public ToolCallback orderPriceTool(OrderService orderService) {
return FunctionToolCallback.builder("calculatePrice", orderService::calculatePrice)
.description("Calculate price based on order ID")
.inputType(String.class)
.build();
}
}
API Migration Reference
| Old API | New API |
|---|---|
FunctionCallback | ToolCallback |
FunctionCallback.builder().function() | FunctionToolCallback.builder(name, function) |
FunctionCallback.builder().method() | MethodToolCallback.builder() |
FunctionCallingOptions | ToolCallingChatOptions |
ChatClient.builder().defaultFunctions() | ChatClient.builder().defaultTools() |
FunctionCallingOptions.builder().functionCallbacks() | ToolCallingChatOptions.builder().toolCallbacks() |
9. Class Relationship Diagram Cheat Sheet
ToolCallbackProvider (interface)
โ
โโโ StaticToolCallbackProvider โ Static tool array
โ
โโโ MethodToolCallbackProvider โ Local @Tool annotated methods
โ
โโโ SyncMcpToolCallbackProvider โ MCP synchronous client
โ
โโโ AsyncMcpToolCallbackProvider โ MCP asynchronous client
โ
โโโ AugmentedToolCallbackProvider โ Existing tool enhancer
ToolCallback (interface)
โ
โโโ MethodToolCallback โ Wraps Java method
โ
โโโ FunctionToolCallback โ Wraps functional interface
โ
โโโ SyncMcpToolCallback โ Wraps MCP sync tool
โ
โโโ AsyncMcpToolCallback โ Wraps MCP async tool
โ
โโโ AugmentedToolCallback โ Wraps enhanced tool
10. Common Questions
Q1: Does the Bean method name (like weatherTools) matter?
A: No, this is just the Spring Bean name. What actually matters is the name in @Tool(name = "getWeather"), thatโs the tool name exposed to the LLM.
Q2: Will tools with the same name conflict?
A: By default, usually not. The current MCP Client Boot Starter defaults to using DefaultMcpToolNamePrefixGenerator, which automatically adds prefixes when duplicate names appear across connections, ensuring unique tool names.
Only when you explicitly disable prefix generation (e.g., using McpToolNamePrefixGenerator.noPrefix()) AND multiple MCP Servers expose tools with the same name will an IllegalStateException be thrown due to naming conflicts.
Q3: What happens when tool execution throws an exception?
A: Default configuration spring.ai.tools.throw-exception-on-error=false, exceptions are returned to the model as error messages; setting to true will throw exceptions directly.
11. Current Version Status (as of 2026-03-22)
11.1 First Distinguish โStableโ from โPreviewโ
-
Latest Stable Version:
1.1.3 -
Latest Preview Version:
2.0.0-M3Release date is 2026-03-17. If youโre writing about production environments, I recommend primarily following the 1.1.3 official Reference; if youโre evaluating Spring Boot 4 / Jackson 3 / Spring AI 2.x, then supplement with M3 changes separately.
11.2 These Are โCurrently Still Validโ Directions
-
FunctionCallbackโToolCallbackmigration is still the official recommended path But this isnโt a change added in 2.0.0-M3, itโs a unified migration direction that started earlier. Official now maintains a separate migration guide. -
Tool Argument Augmentation has entered current tools documentation The official naming in current docs is Tool Argument Augmentation. If youโre using 1.1.3, the core classes are:
AugmentedToolCallbackProviderAugmentedToolCallbackToolInputSchemaAugmenter
-
Dynamic Tool Discovery has official guide
ToolSearchToolCallAdvisorcan indeed do dynamic tool discovery, and the official guide provides 34% - 64% token savings data. But note: it comes fromorg.springaicommunity:tool-search-tool, not a core module built into Spring AI core itself.
11.3 2.0.0-M3 Breaking Changes Worth Noting
-
MCP Annotations migrated into Spring AI core Package name moved from
org.springaicommunity.mcptoorg.springframework.ai.mcp.annotation. -
Spring-specific MCP transport implementation moved into Spring AI project The Spring transport implementation originally in MCP Java SDK moved to
org.springframework.ai.mcprelated modules. -
Jackson 2 โ Jackson 3 This is a real breaking change, especially affecting dependencies and package names.
-
ToolContextno longer automatically includes conversation history This is a breaking change explicitly called out in the M3 release notes, pay special attention when upgrading.
11.4 This Articleโs Verification Sources
- Spring AI Reference
1.1.3 - Spring AI current API Javadoc
- Spring AI
2.0.0-M3release notes - Spring AI 2.0 Dynamic Tool Search guide
Summary
In one sentence:
ToolCallback is the wrapper for a single tool, ToolCallbackProvider is the factory for tool collections. MethodToolCallbackProvider handles local @Tool methods, Sync/AsyncMcpToolCallbackProvider bridges remote MCP tools, AugmentedToolCallbackProvider handles enhancement of existing tools; and on the MCP Server side, you can currently still use the @McpTool annotation route directly.
Understanding this mechanism enables you to:
- Clearly know why your tools arenโt being recognized
- Understand how MCP tools are integrated
- Know which part of the chain to troubleshoot when problems occur
Welcome to follow the WeChat Official Account FishTech Notes for more discussions!